diff --git a/lib/common/helpers.ts b/lib/common/helpers.ts index f3c22b4430..fd5b6044e4 100644 --- a/lib/common/helpers.ts +++ b/lib/common/helpers.ts @@ -97,12 +97,6 @@ export function regExpEscape(input: string): string { return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -export function isRecommendedAarFile(foundAarFile: string, packageJsonPluginName: string): boolean { - const filename = foundAarFile.replace(/^.*[\\\/]/, ''); - packageJsonPluginName = getShortPluginName(packageJsonPluginName); - return `${packageJsonPluginName}.aar` === filename; -} - export function getShortPluginName(pluginName: string): string { return sanitizePluginName(pluginName).replace(/[\-]/g, "_"); } diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 05d67d21ab..4463c00e38 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -461,16 +461,6 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS */ checkForChanges(changeset: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): Promise; - /** - * Build native part of a nativescript plugins if necessary - */ - prebuildNativePlugin(buildOptions: IPluginBuildOptions): Promise; - - /** - * Traverse through the production dependencies and find plugins that need build/rebuild - */ - checkIfPluginsNeedBuild(projectData: IProjectData): Promise>; - /** * Get the deployment target's version * Currently implemented only for iOS -> returns the value of IPHONEOS_DEPLOYMENT_TARGET property from xcconfig file diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 380ce5a24f..47e3a10a4b 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -4,7 +4,7 @@ import * as constants from "../constants"; import * as semver from "semver"; import * as projectServiceBaseLib from "./platform-project-service-base"; import { DeviceAndroidDebugBridge } from "../common/mobile/android/device-android-debug-bridge"; -import { attachAwaitDetach, isRecommendedAarFile } from "../common/helpers"; +import { attachAwaitDetach } from "../common/helpers"; import { Configurations, LiveSyncPaths } from "../common/constants"; import { SpawnOptions } from "child_process"; import { performanceLog } from ".././common/decorators"; @@ -14,7 +14,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject private static VALUES_VERSION_DIRNAME_PREFIX = AndroidProjectService.VALUES_DIRNAME + "-v"; private static ANDROID_PLATFORM_NAME = "android"; private static MIN_RUNTIME_VERSION_WITH_GRADLE = "1.5.0"; - private static MIN_RUNTIME_VERSION_WITHOUT_DEPS = "4.2.0-2018-06-29-02"; private isAndroidStudioTemplate: boolean; @@ -26,9 +25,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject private $logger: ILogger, $projectDataService: IProjectDataService, private $injector: IInjector, - private $pluginVariablesService: IPluginVariablesService, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $packageManager: INodePackageManager, private $androidPluginBuildService: IAndroidPluginBuildService, private $platformEnvironmentRequirements: IPlatformEnvironmentRequirements, private $androidResourcesMigrationService: IAndroidResourcesMigrationService, @@ -186,52 +183,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } this.cleanResValues(targetSdkVersion, projectData); - - if (semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITHOUT_DEPS)) { - await this.installRuntimeDeps(projectData, config); - } - } - - private async installRuntimeDeps(projectData: IProjectData, config: ICreateProjectOptions) { - const requiredDevDependencies = [ - { name: "babel-traverse", version: "^6.4.5" }, - { name: "babel-types", version: "^6.4.5" }, - { name: "babylon", version: "^6.4.5" }, - { name: "lazy", version: "^1.0.11" } - ]; - - const npmConfig: INodePackageManagerInstallOptions = { - save: true, - "save-dev": true, - "save-exact": true, - silent: true, - disableNpmInstall: false, - frameworkPath: config.frameworkPath, - ignoreScripts: config.ignoreScripts - }; - - const projectPackageJson: any = this.$fs.readJson(projectData.projectFilePath); - - for (const dependency of requiredDevDependencies) { - let dependencyVersionInProject = (projectPackageJson.dependencies && projectPackageJson.dependencies[dependency.name]) || - (projectPackageJson.devDependencies && projectPackageJson.devDependencies[dependency.name]); - - if (!dependencyVersionInProject) { - await this.$packageManager.install(`${dependency.name}@${dependency.version}`, projectData.projectDir, npmConfig); - } else { - const cleanedVersion = semver.clean(dependencyVersionInProject); - - // The plugin version is not valid. Check node_modules for the valid version. - if (!cleanedVersion) { - const pathToPluginPackageJson = path.join(projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME, dependency.name, constants.PACKAGE_JSON_FILE_NAME); - dependencyVersionInProject = this.$fs.exists(pathToPluginPackageJson) && this.$fs.readJson(pathToPluginPackageJson).version; - } - - if (!semver.satisfies(dependencyVersionInProject || cleanedVersion, dependency.version)) { - this.$errors.failWithoutHelp(`Your project have installed ${dependency.name} version ${cleanedVersion} but Android platform requires version ${dependency.version}.`); - } - } - } } private cleanResValues(targetSdkVersion: number, projectData: IProjectData): void { @@ -442,166 +393,37 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public async preparePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise { - if (!this.runtimeVersionIsGreaterThanOrEquals(projectData, "3.3.0")) { - const pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); - await this.processResourcesFromPlugin(pluginData, pluginPlatformsFolderPath, projectData); - } else if (this.runtimeVersionIsGreaterThanOrEquals(projectData, "4.0.0")) { - // build Android plugins which contain AndroidManifest.xml and/or resources - const pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); - if (this.$fs.exists(pluginPlatformsFolderPath)) { - const options: IPluginBuildOptions = { - projectDir: projectData.projectDir, - pluginName: pluginData.name, - platformsAndroidDirPath: pluginPlatformsFolderPath, - aarOutputDir: pluginPlatformsFolderPath, - tempPluginDirPath: path.join(projectData.platformsDir, "tempPlugin") - }; + // build Android plugins which contain AndroidManifest.xml and/or resources + const pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); + if (this.$fs.exists(pluginPlatformsFolderPath)) { + const options: IPluginBuildOptions = { + projectDir: projectData.projectDir, + pluginName: pluginData.name, + platformsAndroidDirPath: pluginPlatformsFolderPath, + aarOutputDir: pluginPlatformsFolderPath, + tempPluginDirPath: path.join(projectData.platformsDir, "tempPlugin") + }; - await this.prebuildNativePlugin(options); + if (await this.$androidPluginBuildService.buildAar(options)) { + this.$logger.info(`Built aar for ${options.pluginName}`); } - } - - // Do nothing, the Android Gradle script will configure itself based on the input dependencies.json - } - - public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise> { - const detectedPlugins: Array<{ platformsAndroidDirPath: string, pluginName: string }> = []; - - const platformsAndroid = path.join(constants.PLATFORMS_DIR_NAME, "android"); - const pathToPlatformsAndroid = path.join(projectData.projectDir, platformsAndroid); - const dependenciesJson = await this.$fs.readJson(path.join(pathToPlatformsAndroid, constants.DEPENDENCIES_JSON_NAME)); - const productionDependencies = dependenciesJson.map((item: any) => { - return path.resolve(pathToPlatformsAndroid, item.directory); - }); - for (const dependency of productionDependencies) { - const jsonContent = this.$fs.readJson(path.join(dependency, constants.PACKAGE_JSON_FILE_NAME)); - const isPlugin = !!jsonContent.nativescript; - const pluginName = jsonContent.name; - if (isPlugin) { - const platformsAndroidDirPath = path.join(dependency, platformsAndroid); - if (this.$fs.exists(platformsAndroidDirPath)) { - let hasGeneratedAar = false; - let generatedAarPath = ""; - const nativeFiles = this.$fs.enumerateFilesInDirectorySync(platformsAndroidDirPath).filter((item) => { - if (isRecommendedAarFile(item, pluginName)) { - generatedAarPath = item; - hasGeneratedAar = true; - } - return this.isAllowedFile(item); - }); - - if (hasGeneratedAar) { - const aarStat = this.$fs.getFsStats(generatedAarPath); - nativeFiles.forEach((item) => { - const currentItemStat = this.$fs.getFsStats(item); - if (currentItemStat.mtime > aarStat.mtime) { - detectedPlugins.push({ - platformsAndroidDirPath, - pluginName - }); - } - }); - } else if (nativeFiles.length > 0) { - detectedPlugins.push({ - platformsAndroidDirPath, - pluginName - }); - } - } - } + this.$androidPluginBuildService.migrateIncludeGradle(options); } - return detectedPlugins; - } - - private isAllowedFile(item: string): boolean { - return item.endsWith(constants.MANIFEST_FILE_NAME) || item.endsWith(constants.RESOURCES_DIR); - } - - public async prebuildNativePlugin(options: IPluginBuildOptions): Promise { - if (await this.$androidPluginBuildService.buildAar(options)) { - this.$logger.info(`Built aar for ${options.pluginName}`); - } - - this.$androidPluginBuildService.migrateIncludeGradle(options); } public async processConfigurationFilesFromAppResources(): Promise { return; } - private async processResourcesFromPlugin(pluginData: IPluginData, pluginPlatformsFolderPath: string, projectData: IProjectData): Promise { - const configurationsDirectoryPath = path.join(this.getPlatformData(projectData).projectRoot, "configurations"); - this.$fs.ensureDirectoryExists(configurationsDirectoryPath); - - const pluginConfigurationDirectoryPath = path.join(configurationsDirectoryPath, pluginData.name); - if (this.$fs.exists(pluginPlatformsFolderPath)) { - this.$fs.ensureDirectoryExists(pluginConfigurationDirectoryPath); - - const isScoped = pluginData.name.indexOf("@") === 0; - const flattenedDependencyName = isScoped ? pluginData.name.replace("/", "_") : pluginData.name; - - // Copy all resources from plugin - const resourcesDestinationDirectoryPath = path.join(this.getPlatformData(projectData).projectRoot, constants.SRC_DIR, flattenedDependencyName); - this.$fs.ensureDirectoryExists(resourcesDestinationDirectoryPath); - shell.cp("-Rf", path.join(pluginPlatformsFolderPath, "*"), resourcesDestinationDirectoryPath); - - const filesForInterpolation = this.$fs.enumerateFilesInDirectorySync(resourcesDestinationDirectoryPath, file => this.$fs.getFsStats(file).isDirectory() || path.extname(file) === constants.XML_FILE_EXTENSION) || []; - for (const file of filesForInterpolation) { - this.$logger.trace(`Interpolate data for plugin file: ${file}`); - await this.$pluginVariablesService.interpolate(pluginData, file, projectData.projectDir, projectData.projectIdentifiers.android); - } - } - - // Copy include.gradle file - const includeGradleFilePath = path.join(pluginPlatformsFolderPath, constants.INCLUDE_GRADLE_NAME); - if (this.$fs.exists(includeGradleFilePath)) { - shell.cp("-f", includeGradleFilePath, pluginConfigurationDirectoryPath); - } - } - public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise { - try { - if (!this.runtimeVersionIsGreaterThanOrEquals(projectData, "3.3.0")) { - const pluginConfigDir = path.join(this.getPlatformData(projectData).projectRoot, "configurations", pluginData.name); - if (this.$fs.exists(pluginConfigDir)) { - await this.cleanProject(this.getPlatformData(projectData).projectRoot, projectData); - } - } - } catch (e) { - if (e.code === "ENOENT") { - this.$logger.debug("No native code jars found: " + e.message); - } else { - throw e; - } - } + // not implemented } public async beforePrepareAllPlugins(projectData: IProjectData, dependencies?: IDependencyData[]): Promise { - const shouldUseNewRoutine = this.runtimeVersionIsGreaterThanOrEquals(projectData, "3.3.0"); - if (dependencies) { dependencies = this.filterUniqueDependencies(dependencies); - if (shouldUseNewRoutine) { - this.provideDependenciesJson(projectData, dependencies); - } else { - const platformDir = path.join(projectData.platformsDir, AndroidProjectService.ANDROID_PLATFORM_NAME); - const buildDir = path.join(platformDir, "build-tools"); - const checkV8dependants = path.join(buildDir, "check-v8-dependants.js"); - if (this.$fs.exists(checkV8dependants)) { - const stringifiedDependencies = JSON.stringify(dependencies); - try { - await this.spawn('node', [checkV8dependants, stringifiedDependencies, projectData.platformsDir], { stdio: "inherit" }); - } catch (e) { - this.$logger.info("Checking for dependants on v8 public API failed. This is likely caused because of cyclic production dependencies. Error code: " + e.code + "\nMore information: https://github.com/NativeScript/nativescript-cli/issues/2561"); - } - } - } - } - - if (!shouldUseNewRoutine) { - const projectRoot = this.getPlatformData(projectData).projectRoot; - await this.cleanProject(projectRoot, projectData); + this.provideDependenciesJson(projectData, dependencies); } } @@ -763,17 +585,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return semver.gte(normalizedPlatformVersion, androidStudioCompatibleTemplate); } - private runtimeVersionIsGreaterThanOrEquals(projectData: IProjectData, versionString: string): boolean { - const platformVersion = this.getCurrentPlatformVersion(this.getPlatformData(projectData), projectData); - - if (platformVersion === constants.PackageVersion.NEXT) { - return true; - } - - const normalizedPlatformVersion = `${semver.major(platformVersion)}.${semver.minor(platformVersion)}.0`; - return semver.gte(normalizedPlatformVersion, versionString); - } - private getLegacyAppResourcesDestinationDirPath(projectData: IProjectData): string { const resourcePath: string[] = [constants.SRC_DIR, constants.MAIN_DIR, constants.RESOURCES_DIR]; if (this.isAndroidStudioTemplate) { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 85960e4ec2..65156eb9cc 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1038,12 +1038,6 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - public async prebuildNativePlugin(options: IPluginBuildOptions): Promise { /** */ } - - public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise> { - return []; - } - public getDeploymentTarget(projectData: IProjectData): semver.SemVer { const target = this.$xcconfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "IPHONEOS_DEPLOYMENT_TARGET"); if (!target) { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index e481600407..591c03214e 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -757,7 +757,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { platform = platform.split("@")[0].toLowerCase(); - if (!this.isValidPlatform(platform, projectData)) { + if (!this.$platformsData.getPlatformData(platform, projectData)) { this.$errors.fail("Invalid platform %s. Valid platforms are %s.", platform, helpers.formatListOfNames(this.$platformsData.platformsNames)); } } @@ -822,10 +822,6 @@ export class PlatformService extends EventEmitter implements IPlatformService { return this.$fs.exists(path.join(projectData.platformsDir, platform.toLowerCase())); } - private isValidPlatform(platform: string, projectData: IProjectData) { - return this.$platformsData.getPlatformData(platform, projectData); - } - public isPlatformSupportedForOS(platform: string, projectData: IProjectData): boolean { const targetedOS = this.$platformsData.getPlatformData(platform, projectData).targetedOS; const res = !targetedOS || targetedOS.indexOf("*") >= 0 || targetedOS.indexOf(process.platform) >= 0; diff --git a/test/debug.ts b/test/debug.ts deleted file mode 100644 index a3de9409d4..0000000000 --- a/test/debug.ts +++ /dev/null @@ -1,115 +0,0 @@ -import * as stubs from "./stubs"; -import * as yok from "../lib/common/yok"; -import { DebugAndroidCommand } from "../lib/commands/debug"; -import { assert } from "chai"; -import { BundleValidatorHelper } from "../lib/helpers/bundle-validator-helper"; -import { Configuration, StaticConfig } from "../lib/config"; -import { Options } from "../lib/options"; -import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants"; -import { FileSystem } from "../lib/common/file-system"; -import { AndroidProjectService } from "../lib/services/android-project-service"; -import { AndroidDebugBridge } from "../lib/common/mobile/android/android-debug-bridge"; -import { AndroidDebugBridgeResultHandler } from "../lib/common/mobile/android/android-debug-bridge-result-handler"; -import { SettingsService } from "../lib/common/test/unit-tests/stubs"; - -function createTestInjector(): IInjector { - const testInjector: IInjector = new yok.Yok(); - - testInjector.register("debug|android", DebugAndroidCommand); - testInjector.register("config", Configuration); - testInjector.register("staticConfig", StaticConfig); - testInjector.register("logger", stubs.LoggerStub); - testInjector.register("options", Options); - testInjector.register("devicePlatformsConstants", DevicePlatformsConstants); - testInjector.register('childProcess', stubs.ChildProcessStub); - testInjector.register('fs', FileSystem); - testInjector.register('errors', stubs.ErrorsStub); - testInjector.register('hostInfo', {}); - testInjector.register("androidBundleValidatorHelper", stubs.AndroidBundleValidatorHelper); - testInjector.register("bundleValidatorHelper", BundleValidatorHelper); - testInjector.register("analyticsService", { - trackException: async (): Promise => undefined, - checkConsent: async (): Promise => undefined, - trackFeature: async (): Promise => undefined - }); - testInjector.register('devicesService', { - initialize: async () => { /* Intentionally left blank */ }, - getDeviceInstances: (): any[] => { return []; }, - execute: async (): Promise => ({}) - }); - testInjector.register("liveSyncService", stubs.LiveSyncServiceStub); - testInjector.register("androidProjectService", AndroidProjectService); - testInjector.register("androidToolsInfo", stubs.AndroidToolsInfoStub); - testInjector.register("hostInfo", {}); - testInjector.register("projectData", { platformsDir: "test", initializeProjectData: () => { /* empty */ } }); - testInjector.register("projectDataService", {}); - testInjector.register("sysInfo", {}); - testInjector.register("mobileHelper", {}); - testInjector.register("pluginVariablesService", {}); - testInjector.register("projectTemplatesService", {}); - testInjector.register("debugService", {}); - testInjector.register("xmlValidator", {}); - testInjector.register("packageManager", {}); - testInjector.register("debugDataService", { - createDebugData: () => ({}) - }); - testInjector.register("androidEmulatorServices", {}); - testInjector.register("adb", AndroidDebugBridge); - testInjector.register("androidDebugBridgeResultHandler", AndroidDebugBridgeResultHandler); - testInjector.register("platformService", stubs.PlatformServiceStub); - testInjector.register("platformsData", { - availablePlatforms: { - Android: "Android", - iOS: "iOS" - } - }); - - testInjector.register("prompter", {}); - testInjector.registerCommand("debug|android", DebugAndroidCommand); - testInjector.register("liveSyncCommandHelper", { - executeLiveSyncOperation: async (): Promise => { - return null; - } - }); - testInjector.register("settingsService", SettingsService); - testInjector.register("androidPluginBuildService", stubs.AndroidPluginBuildServiceStub); - testInjector.register("platformEnvironmentRequirements", {}); - testInjector.register("androidResourcesMigrationService", stubs.AndroidResourcesMigrationServiceStub); - testInjector.register("filesHashService", {}); - - return testInjector; -} - -describe("debug command tests", () => { - describe("Debugger tests", () => { - let testInjector: IInjector; - - beforeEach(() => { - testInjector = createTestInjector(); - }); - - it("Ensures that beforePrepareAllPlugins will call gradle with clean option when *NOT* livesyncing", async () => { - const platformData = testInjector.resolve("platformsData"); - platformData.frameworkPackageName = "tns-android"; - - // only test that 'clean' is performed on android <=3.2. See https://github.com/NativeScript/nativescript-cli/pull/3032 - const projectDataService: IProjectDataService = testInjector.resolve("projectDataService"); - projectDataService.getNSValue = (projectDir: string, propertyName: string) => { - return { version: "3.2.0" }; - }; - - const childProcess: stubs.ChildProcessStub = testInjector.resolve("childProcess"); - const androidProjectService: IPlatformProjectService = testInjector.resolve("androidProjectService"); - androidProjectService.getPlatformData = (_projectData: IProjectData): IPlatformData => { - return platformData; - }; - const projectData: IProjectData = testInjector.resolve("projectData"); - const spawnFromEventCount = childProcess.spawnFromEventCount; - await androidProjectService.beforePrepareAllPlugins(projectData); - assert.isTrue(childProcess.lastCommand.indexOf("gradle") !== -1); - assert.isTrue(childProcess.lastCommandArgs[0] === "clean"); - assert.isTrue(spawnFromEventCount === 0); - assert.isTrue(spawnFromEventCount + 1 === childProcess.spawnFromEventCount); - }); - }); -}); diff --git a/test/stubs.ts b/test/stubs.ts index 7746d4b4b8..cd20ee835c 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -388,13 +388,7 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor fastLivesyncFileExtensions: [] }; } - prebuildNativePlugin(options: IPluginBuildOptions): Promise { - return Promise.resolve(); - } - checkIfPluginsNeedBuild(projectData: IProjectData): Promise> { - return Promise.resolve([]); - } getAppResourcesDestinationDirectoryPath(): string { return ""; }