Skip to content

feat: Improve rebuild of native plugins and node_modules checks #3750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/commands/appstore-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export class PublishIOS implements ICommand {
const platformInfo: IPreparePlatformInfo = {
platform,
appFilesUpdaterOptions,
skipModulesNativeCheck: !this.$options.syncAllFiles,
platformTemplate: this.$options.platformTemplate,
projectData: this.$projectData,
config: this.$options,
Expand Down
1 change: 0 additions & 1 deletion lib/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export class BuildCommandBase {
const platformInfo: IPreparePlatformInfo = {
platform,
appFilesUpdaterOptions,
skipModulesNativeCheck: !this.$options.syncAllFiles,
platformTemplate: this.$options.platformTemplate,
projectData: this.$projectData,
config: this.$options,
Expand Down
1 change: 0 additions & 1 deletion lib/commands/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export class PrepareCommand implements ICommand {
const platformInfo: IPreparePlatformInfo = {
platform: args[0],
appFilesUpdaterOptions,
skipModulesNativeCheck: !this.$options.syncAllFiles,
platformTemplate: this.$options.platformTemplate,
projectData: this.$projectData,
config: this.$options,
Expand Down
2 changes: 2 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,5 @@ export const PACKAGE_PLACEHOLDER_NAME = "__PACKAGE__";
export class AddPlaformErrors {
public static InvalidFrameworkPathStringFormat = "Invalid frameworkPath: %s. Please ensure the specified frameworkPath exists.";
}

export const PLUGIN_BUILD_DATA_FILENAME = "plugin-data.json";
3 changes: 2 additions & 1 deletion lib/definitions/files-hash-service.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
interface IFilesHashService {
generateHashes(files: string[]): Promise<IStringDictionary>;
getChanges(files: string[], oldHashes: IStringDictionary): Promise<IStringDictionary>;
}
hasChangesInShasums(oldHashes: IStringDictionary, newHashes: IStringDictionary): boolean;
}
6 changes: 5 additions & 1 deletion lib/definitions/project-changes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ interface IProjectChangesOptions extends IAppFilesUpdaterOptions, IProvision, IT
nativePlatformStatus?: "1" | "2" | "3";
}

interface ICheckForChangesOptions extends IPlatform, IProjectDataComposition {
projectChangesOptions: IProjectChangesOptions;
}

interface IProjectChangesService {
checkForChanges(platform: string, projectData: IProjectData, buildOptions: IProjectChangesOptions): Promise<IProjectChangesInfo>;
checkForChanges(checkForChangesOpts: ICheckForChangesOptions): Promise<IProjectChangesInfo>;
getPrepareInfo(platform: string, projectData: IProjectData): IPrepareInfo;
savePrepareInfo(platform: string, projectData: IProjectData): void;
getPrepareInfoFilePath(platform: string, projectData: IProjectData): string;
Expand Down
72 changes: 65 additions & 7 deletions lib/services/android-plugin-build-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from "path";
import { MANIFEST_FILE_NAME, INCLUDE_GRADLE_NAME, ASSETS_DIR, RESOURCES_DIR, TNS_ANDROID_RUNTIME_NAME, AndroidBuildDefaults } from "../constants";
import { MANIFEST_FILE_NAME, INCLUDE_GRADLE_NAME, ASSETS_DIR, RESOURCES_DIR, TNS_ANDROID_RUNTIME_NAME, AndroidBuildDefaults, PLUGIN_BUILD_DATA_FILENAME } from "../constants";
import { getShortPluginName, hook } from "../common/helpers";
import { Builder, parseString } from "xml2js";
import { ILogger } from "log4js";
Expand All @@ -25,7 +25,8 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
private $npm: INodePackageManager,
private $projectDataService: IProjectDataService,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $errors: IErrors) { }
private $errors: IErrors,
private $filesHashService: IFilesHashService) { }

private static MANIFEST_ROOT = {
$: {
Expand Down Expand Up @@ -172,23 +173,80 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
this.validateOptions(options);
const manifestFilePath = this.getManifest(options.platformsAndroidDirPath);
const androidSourceDirectories = this.getAndroidSourceDirectories(options.platformsAndroidDirPath);
const shouldBuildAar = !!manifestFilePath || androidSourceDirectories.length > 0;
const shortPluginName = getShortPluginName(options.pluginName);
const pluginTempDir = path.join(options.tempPluginDirPath, shortPluginName);
const pluginSourceFileHashesInfo = await this.getSourceFilesHashes(options.platformsAndroidDirPath, shortPluginName);

const shouldBuildAar = await this.shouldBuildAar({
manifestFilePath,
androidSourceDirectories,
pluginTempDir,
pluginSourceDir: options.platformsAndroidDirPath,
shortPluginName,
fileHashesInfo: pluginSourceFileHashesInfo
});

if (shouldBuildAar) {
const shortPluginName = getShortPluginName(options.pluginName);
const pluginTempDir = path.join(options.tempPluginDirPath, shortPluginName);
const pluginTempMainSrcDir = path.join(pluginTempDir, "src", "main");
this.cleanPluginDir(pluginTempDir);

const pluginTempMainSrcDir = path.join(pluginTempDir, "src", "main");
await this.updateManifest(manifestFilePath, pluginTempMainSrcDir, shortPluginName);
this.copySourceSetDirectories(androidSourceDirectories, pluginTempMainSrcDir);
await this.setupGradle(pluginTempDir, options.platformsAndroidDirPath, options.projectDir);
await this.buildPlugin({ pluginDir: pluginTempDir, pluginName: options.pluginName });
this.copyAar(shortPluginName, pluginTempDir, options.aarOutputDir);
this.writePluginHashInfo(pluginSourceFileHashesInfo, pluginTempDir);
}

return shouldBuildAar;
}

private cleanPluginDir(pluginTempDir: string): void {
// In case plugin was already built in the current process, we need to clean the old sources as they may break the new build.
this.$fs.deleteDirectory(pluginTempDir);
this.$fs.ensureDirectoryExists(pluginTempDir);
}

private getSourceFilesHashes(pluginTempPlatformsAndroidDir: string, shortPluginName: string): Promise<IStringDictionary> {
const pathToAar = path.join(pluginTempPlatformsAndroidDir, `${shortPluginName}.aar`);
const pluginNativeDataFiles = this.$fs.enumerateFilesInDirectorySync(pluginTempPlatformsAndroidDir, (file: string, stat: IFsStats) => file !== pathToAar);
return this.$filesHashService.generateHashes(pluginNativeDataFiles);
}

private writePluginHashInfo(fileHashesInfo: IStringDictionary, pluginTempDir: string): void {
const buildDataFile = this.getPathToPluginBuildDataFile(pluginTempDir);
this.$fs.writeJson(buildDataFile, fileHashesInfo);
}

private async shouldBuildAar(opts: {
manifestFilePath: string,
androidSourceDirectories: string[],
pluginTempDir: string,
pluginSourceDir: string,
shortPluginName: string,
fileHashesInfo: IStringDictionary
}): Promise<boolean> {

let shouldBuildAar = !!opts.manifestFilePath || !!opts.androidSourceDirectories.length;

if (shouldBuildAar &&
this.$fs.exists(opts.pluginTempDir) &&
this.$fs.exists(path.join(opts.pluginSourceDir, `${opts.shortPluginName}.aar`))) {

const buildDataFile = this.getPathToPluginBuildDataFile(opts.pluginTempDir);
if (this.$fs.exists(buildDataFile)) {
const oldHashes = this.$fs.readJson(buildDataFile);
shouldBuildAar = this.$filesHashService.hasChangesInShasums(oldHashes, opts.fileHashesInfo);
}
}

return shouldBuildAar;
}

private getPathToPluginBuildDataFile(pluginDir: string): string {
return path.join(pluginDir, PLUGIN_BUILD_DATA_FILENAME);
}

private async updateManifest(manifestFilePath: string, pluginTempMainSrcDir: string, shortPluginName: string): Promise<void> {
let updatedManifestContent;
this.$fs.ensureDirectoryExists(pluginTempMainSrcDir);
Expand Down Expand Up @@ -256,7 +314,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
return runtimeGradleVersions || {};
}

private getGradleVersions(packageData: { gradle: { version: string, android: string }}): IRuntimeGradleVersions {
private getGradleVersions(packageData: { gradle: { version: string, android: string } }): IRuntimeGradleVersions {
const packageJsonGradle = packageData && packageData.gradle;
let runtimeVersions: IRuntimeGradleVersions = null;
if (packageJsonGradle && (packageJsonGradle.version || packageJsonGradle.android)) {
Expand Down
8 changes: 8 additions & 0 deletions lib/services/files-hash-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ export class FilesHashService implements IFilesHashService {

public async getChanges(files: string[], oldHashes: IStringDictionary): Promise<IStringDictionary> {
const newHashes = await this.generateHashes(files);
return this.getChangesInShasums(oldHashes, newHashes);
}

public hasChangesInShasums(oldHashes: IStringDictionary, newHashes: IStringDictionary): boolean {
return !!_.keys(this.getChangesInShasums(oldHashes, newHashes)).length;
}

private getChangesInShasums(oldHashes: IStringDictionary, newHashes: IStringDictionary): IStringDictionary {
return _.omitBy(newHashes, (hash: string, pathToFile: string) => !!_.find(oldHashes, (oldHash: string, oldPath: string) => pathToFile === oldPath && hash === oldHash));
}
}
Expand Down
1 change: 0 additions & 1 deletion lib/services/livesync/livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
deviceBuildInfoDescriptor,
liveSyncData,
settings,
skipModulesNativeCheck: !liveSyncData.watchAllFiles,
bundle: liveSyncData.bundle,
release: liveSyncData.release,
env: liveSyncData.env
Expand Down
18 changes: 11 additions & 7 deletions lib/services/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,17 @@ export class PlatformService extends EventEmitter implements IPlatformService {

const bundle = appFilesUpdaterOptions.bundle;
const nativePlatformStatus = (nativePrepare && nativePrepare.skipNativePrepare) ? constants.NativePlatformStatus.requiresPlatformAdd : constants.NativePlatformStatus.requiresPrepare;
const changesInfo = await this.$projectChangesService.checkForChanges(platform, projectData, {
bundle,
release: appFilesUpdaterOptions.release,
provision: config.provision,
teamId: config.teamId,
nativePlatformStatus,
skipModulesNativeCheck: skipNativeCheckOptions.skipModulesNativeCheck
const changesInfo = await this.$projectChangesService.checkForChanges({
platform,
projectData,
projectChangesOptions: {
bundle,
release: appFilesUpdaterOptions.release,
provision: config.provision,
teamId: config.teamId,
nativePlatformStatus,
skipModulesNativeCheck: skipNativeCheckOptions.skipModulesNativeCheck
}
});

this.$logger.trace("Changes info in prepare platform:", changesInfo);
Expand Down
13 changes: 10 additions & 3 deletions lib/services/project-changes-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from "path";
import { NODE_MODULES_FOLDER_NAME, NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME } from "../constants";
import { getHash } from "../common/helpers";
import { getHash, hook } from "../common/helpers";

const prepareInfoFileName = ".nsprepareinfo";

Expand Down Expand Up @@ -45,18 +45,25 @@ export class ProjectChangesService implements IProjectChangesService {
private _outputProjectMtime: number;
private _outputProjectCTime: number;

private get $hooksService(): IHooksService {
return this.$injector.resolve<IHooksService>("hooksService");
}

constructor(
private $platformsData: IPlatformsData,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $fs: IFileSystem,
private $filesHashService: IFilesHashService) {
private $filesHashService: IFilesHashService,
private $injector: IInjector) {
}

public get currentChanges(): IProjectChangesInfo {
return this._changesInfo;
}

public async checkForChanges(platform: string, projectData: IProjectData, projectChangesOptions: IProjectChangesOptions): Promise<IProjectChangesInfo> {
@hook("checkForChanges")
public async checkForChanges(checkForChangesOpts: ICheckForChangesOptions): Promise<IProjectChangesInfo> {
const { platform, projectData, projectChangesOptions } = checkForChangesOpts;
const platformData = this.$platformsData.getPlatformData(platform, projectData);
this._changesInfo = new ProjectChangesInfo();
const isNewPrepareInfo = await this.ensurePrepareInfo(platform, projectData, projectChangesOptions);
Expand Down
2 changes: 0 additions & 2 deletions lib/services/test-execution-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class TestExecutionService implements ITestExecutionService {
const preparePlatformInfo: IPreparePlatformInfo = {
platform,
appFilesUpdaterOptions,
skipModulesNativeCheck: !this.$options.syncAllFiles,
platformTemplate: this.$options.platformTemplate,
projectData,
config: this.$options,
Expand Down Expand Up @@ -187,7 +186,6 @@ class TestExecutionService implements ITestExecutionService {
const preparePlatformInfo: IPreparePlatformInfo = {
platform,
appFilesUpdaterOptions,
skipModulesNativeCheck: !this.$options.syncAllFiles,
platformTemplate: this.$options.platformTemplate,
projectData,
config: this.$options,
Expand Down
25 changes: 23 additions & 2 deletions test/project-changes-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PlatformsData } from "../lib/platforms-data";
import { ProjectChangesService } from "../lib/services/project-changes-service";
import * as Constants from "../lib/constants";
import { FileSystem } from "../lib/common/file-system";
import { HooksServiceStub } from "./stubs";

// start tracking temporary folders/files
temp.track();
Expand Down Expand Up @@ -36,6 +37,7 @@ class ProjectChangesServiceTest extends BaseServiceTest {
this.injector.register("logger", {
warn: () => ({})
});
this.injector.register("hooksService", HooksServiceStub);

const fs = this.injector.resolve<IFileSystem>("fs");
fs.writeJson(path.join(this.projectDir, Constants.PACKAGE_JSON_FILE_NAME), {
Expand Down Expand Up @@ -149,9 +151,28 @@ describe("Project Changes Service Tests", () => {

describe("Accumulates Changes From Project Services", () => {
it("accumulates changes from the project service", async () => {
const iOSChanges = await serviceTest.projectChangesService.checkForChanges("ios", serviceTest.projectData, { bundle: false, release: false, provision: undefined, teamId: undefined });
const iOSChanges = await serviceTest.projectChangesService.checkForChanges({
platform: "ios",
projectData: serviceTest.projectData,
projectChangesOptions: {
bundle: false,
release: false,
provision: undefined,
teamId: undefined
}
});
assert.isTrue(!!iOSChanges.signingChanged, "iOS signingChanged expected to be true");
const androidChanges = await serviceTest.projectChangesService.checkForChanges("android", serviceTest.projectData, { bundle: false, release: false, provision: undefined, teamId: undefined });

const androidChanges = await serviceTest.projectChangesService.checkForChanges({
platform: "android",
projectData: serviceTest.projectData,
projectChangesOptions: {
bundle: false,
release: false,
provision: undefined,
teamId: undefined
}
});
assert.isFalse(!!androidChanges.signingChanged, "Android signingChanged expected to be false");
});
});
Expand Down
Loading