Skip to content

Commit 04f9737

Browse files
committed
refactor: the CLI builds aar files if necessary even with no --syncAllFiles flag
* removed invalid test * move gradlew plugin to vendor folder and add licese file * add unit test for android build plugin service
1 parent 0b49a64 commit 04f9737

17 files changed

+1237
-119
lines changed

lib/services/android-plugin-build-service.ts

+31-37
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as path from "path";
22
import * as shell from "shelljs";
3-
4-
const xml2js = require("xml2js");
3+
import { Builder, parseString } from "xml2js";
54

65
export class AndroidPluginBuildService implements IAndroidPluginBuildService {
76

@@ -17,11 +16,12 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
1716
"xmlns:android": "http://schemas.android.com/apk/res/android"
1817
}
1918
};
20-
private static ANDROID_PLUGIN_GRADLE_TEMPLATE = "gradle-plugin";
19+
private static ANDROID_PLUGIN_GRADLE_TEMPLATE = "../../vendor/gradle-plugin";
2120

2221
private getAndroidSourceDirectories(source: string): Array<string> {
2322
const directories = ["res", "java", "assets", "jniLibs"];
2423
const resultArr: Array<string> = [];
24+
2525
this.$fs.enumerateFilesInDirectorySync(source, (file, stat) => {
2626
if (stat.isDirectory() && _.some(directories, (element) => file.endsWith(element))) {
2727
resultArr.push(file);
@@ -32,11 +32,9 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
3232
return resultArr;
3333
}
3434

35-
private getManifest(platformsDir: string) {
36-
const manifests = this.$fs
37-
.readDirectory(platformsDir)
38-
.filter(fileName => fileName === AndroidPluginBuildService.ANDROID_MANIFEST_XML);
39-
return manifests.length > 0 ? path.join(platformsDir, manifests[0]) : null;
35+
private getManifest(platformsDir: string): string {
36+
const manifest = path.join(platformsDir, AndroidPluginBuildService.ANDROID_MANIFEST_XML);
37+
return this.$fs.exists(manifest) ? manifest : null;
4038
}
4139

4240
private getShortPluginName(pluginName: string): string {
@@ -68,26 +66,24 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
6866

6967
newManifest.manifest["$"]["package"] = packageName;
7068

71-
const xmlBuilder = new xml2js.Builder();
69+
const xmlBuilder = new Builder();
7270
newManifestContent = xmlBuilder.buildObject(newManifest);
7371

7472
return newManifestContent;
7573
}
7674

77-
private createManifestContent(packageName: string) {
75+
private createManifestContent(packageName: string): string {
7876
let newManifestContent;
7977
const newManifest: any = { manifest: AndroidPluginBuildService.MANIFEST_ROOT };
8078
newManifest.manifest["$"]["package"] = packageName;
81-
const xmlBuilder: any = new xml2js.Builder();
79+
const xmlBuilder: any = new Builder();
8280
newManifestContent = xmlBuilder.buildObject(newManifest);
8381

8482
return newManifestContent;
8583
}
8684

87-
private async getXml(stringContent: string) {
88-
const parseString = xml2js.parseString;
89-
90-
const promise = new Promise((resolve, reject) =>
85+
private async getXml(stringContent: string): Promise<any> {
86+
const promise = new Promise<any>((resolve, reject) =>
9187
parseString(stringContent, (err: any, result: any) => {
9288
if (err) {
9389
reject(err);
@@ -100,11 +96,11 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
10096
return promise;
10197
}
10298

103-
private copyRecursive(source: string, destination: string) {
99+
private copyRecursive(source: string, destination: string): void {
104100
shell.cp("-R", source, destination);
105101
}
106102

107-
private getIncludeGradleCompileDependenciesScope(includeGradleFileContent: string) {
103+
private getIncludeGradleCompileDependenciesScope(includeGradleFileContent: string): Array<string> {
108104
const indexOfDependenciesScope = includeGradleFileContent.indexOf("dependencies");
109105
const result: Array<string> = [];
110106

@@ -126,7 +122,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
126122
return result;
127123
}
128124

129-
private getScope(scopeName: string, content: string) {
125+
private getScope(scopeName: string, content: string): string {
130126
const indexOfScopeName = content.indexOf(scopeName);
131127
let result = "";
132128
const OPENING_BRACKET = "{";
@@ -206,9 +202,9 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
206202
if (manifestFilePath) {
207203
let androidManifestContent;
208204
try {
209-
androidManifestContent = this.$fs.readFile(manifestFilePath).toString();
205+
androidManifestContent = this.$fs.readText(manifestFilePath);
210206
} catch (err) {
211-
throw Error(
207+
throw new Error(
212208
`Failed to fs.readFileSync the manifest file located at ${manifestFilePath}`
213209
);
214210
}
@@ -223,7 +219,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
223219
try {
224220
this.$fs.writeFile(pathToNewAndroidManifest, updatedManifestContent);
225221
} catch (e) {
226-
throw Error(`Failed to write the updated AndroidManifest in the new location - ${pathToNewAndroidManifest}`);
222+
throw new Error(`Failed to write the updated AndroidManifest in the new location - ${pathToNewAndroidManifest}`);
227223
}
228224

229225
// copy all android sourceset directories to the new temporary plugin dir
@@ -241,18 +237,16 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
241237
// copy the preconfigured gradle android library project template to the temporary android library
242238
this.copyRecursive(path.join(path.resolve(path.join(__dirname, AndroidPluginBuildService.ANDROID_PLUGIN_GRADLE_TEMPLATE), "*")), newPluginDir);
243239

244-
// sometimes the AndroidManifest.xml or certain resources in /res may have a compile dependency to a lbirary referenced in include.gradle. Make sure to compile the plugin with a compile dependency to those libraries
240+
// sometimes the AndroidManifest.xml or certain resources in /res may have a compile dependency to a library referenced in include.gradle. Make sure to compile the plugin with a compile dependency to those libraries
245241
const includeGradlePath = path.join(options.platformsAndroidDirPath, "include.gradle");
246242
if (this.$fs.exists(includeGradlePath)) {
247-
const includeGradleContent = this.$fs.readFile(includeGradlePath)
248-
.toString();
243+
const includeGradleContent = this.$fs.readText(includeGradlePath);
249244
const repositoriesAndDependenciesScopes = this.getIncludeGradleCompileDependenciesScope(includeGradleContent);
250245

251246
// dependencies { } object was found - append dependencies scope
252247
if (repositoriesAndDependenciesScopes.length > 0) {
253248
const buildGradlePath = path.join(newPluginDir, "build.gradle");
254-
this.$fs.appendFile(buildGradlePath, "\n" + repositoriesAndDependenciesScopes.join("\n")
255-
);
249+
this.$fs.appendFile(buildGradlePath, "\n" + repositoriesAndDependenciesScopes.join("\n"));
256250
}
257251
}
258252

@@ -277,7 +271,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
277271
try {
278272
await this.$childProcess.exec(localArgs.join(" "), { cwd: newPluginDir });
279273
} catch (err) {
280-
throw Error(`Failed to build plugin ${options.pluginName} : \n${err}`);
274+
throw new Error(`Failed to build plugin ${options.pluginName} : \n${err}`);
281275
}
282276

283277
const finalAarName = `${shortPluginName}-release.aar`;
@@ -289,12 +283,12 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
289283
this.copyRecursive(pathToBuiltAar, path.join(options.aarOutputDir, `${shortPluginName}.aar`));
290284
}
291285
} catch (e) {
292-
throw Error(`Failed to copy built aar to destination. ${e.message}`);
286+
throw new Error(`Failed to copy built aar to destination. ${e.message}`);
293287
}
294288

295289
return true;
296290
} else {
297-
throw Error(`No built aar found at ${pathToBuiltAar}`);
291+
throw new Error(`No built aar found at ${pathToBuiltAar}`);
298292
}
299293
}
300294

@@ -315,7 +309,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
315309
try {
316310
includeGradleFileContent = this.$fs.readFile(includeGradleFilePath).toString();
317311
} catch (err) {
318-
throw Error(`Failed to fs.readFileSync the include.gradle file located at ${includeGradleFilePath}`);
312+
throw new Error(`Failed to fs.readFileSync the include.gradle file located at ${includeGradleFilePath}`);
319313
}
320314

321315
const productFlavorsScope = this.getScope("productFlavors", includeGradleFileContent);
@@ -325,14 +319,14 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
325319
this.$fs.writeFile(includeGradleFilePath, newIncludeGradleFileContent);
326320

327321
} catch (e) {
328-
throw Error(`Failed to write the updated include.gradle in - ${includeGradleFilePath}`);
322+
throw new Error(`Failed to write the updated include.gradle in - ${includeGradleFilePath}`);
329323
}
330324
}
331325
}
332326

333-
private validateOptions(options: IBuildOptions) {
327+
private validateOptions(options: IBuildOptions): void {
334328
if (!options) {
335-
throw Error("Android plugin cannot be built without passing an 'options' object.");
329+
throw new Error("Android plugin cannot be built without passing an 'options' object.");
336330
}
337331

338332
if (!options.pluginName) {
@@ -344,19 +338,19 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
344338
}
345339

346340
if (!options.tempPluginDirPath) {
347-
throw Error("Android plugin cannot be built without passing the path to a directory where the temporary project should be built.");
341+
throw new Error("Android plugin cannot be built without passing the path to a directory where the temporary project should be built.");
348342
}
349343

350344
this.validatePlatformsAndroidDirPathOption(options);
351345
}
352346

353-
private validatePlatformsAndroidDirPathOption(options: IBuildOptions) {
347+
private validatePlatformsAndroidDirPathOption(options: IBuildOptions): void {
354348
if (!options) {
355-
throw Error("Android plugin cannot be built without passing an 'options' object.");
349+
throw new Error("Android plugin cannot be built without passing an 'options' object.");
356350
}
357351

358352
if (!options.platformsAndroidDirPath) {
359-
throw Error("Android plugin cannot be built without passing the path to the platforms/android dir.");
353+
throw new Error("Android plugin cannot be built without passing the path to the platforms/android dir.");
360354
}
361355
}
362356

lib/services/android-project-service.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -436,15 +436,15 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
436436
tempPluginDirPath: path.join(projectData.platformsDir, "tempPlugin")
437437
};
438438

439-
this.prebuildNativePlugin(options);
439+
await this.prebuildNativePlugin(options);
440440
}
441441
}
442442

443443
// Do nothing, the Android Gradle script will configure itself based on the input dependencies.json
444444
}
445445

446-
public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise<Array<any>> {
447-
const detectedPlugins: Array<any> = [];
446+
public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise<Array<{ platformsAndroidDirPath: string, pluginName: string }>> {
447+
const detectedPlugins: Array<{ platformsAndroidDirPath: string, pluginName: string }> = [];
448448

449449
const platformsAndroid = path.join(constants.PLATFORMS_DIR_NAME, "android");
450450
const pathToPlatformsAndroid = path.join(projectData.projectDir, platformsAndroid);
@@ -477,15 +477,15 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
477477
const currentItemStat = this.$fs.getFsStats(item);
478478
if (currentItemStat.mtime > aarStat.mtime) {
479479
detectedPlugins.push({
480-
platformsAndroidDirPath: platformsAndroidDirPath,
481-
pluginName: pluginName
480+
platformsAndroidDirPath,
481+
pluginName
482482
});
483483
}
484484
});
485485
} else if (nativeFiles.length > 0) {
486486
detectedPlugins.push({
487-
platformsAndroidDirPath: platformsAndroidDirPath,
488-
pluginName: pluginName
487+
platformsAndroidDirPath,
488+
pluginName
489489
});
490490
}
491491
}
@@ -494,7 +494,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
494494
return detectedPlugins;
495495
}
496496

497-
private isAllowedFile(item: string) {
497+
private isAllowedFile(item: string): boolean {
498498
return item.endsWith(constants.MANIFEST_FILE_NAME) || item.endsWith(constants.RESOURCES_DIR);
499499
}
500500

lib/services/livesync/livesync-service.ts

+3-54
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import * as path from "path";
22
import * as choki from "chokidar";
33
import { EOL } from "os";
44
import { EventEmitter } from "events";
5-
import { hook, executeActionByChunks, isBuildFromCLI } from "../../common/helpers";
5+
import { hook, isBuildFromCLI } from "../../common/helpers";
66
import { PACKAGE_JSON_FILE_NAME, LiveSyncTrackActionNames, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME, DEBUGGER_DETACHED_EVENT_NAME, TrackActionNames } from "../../constants";
7-
import { DEFAULT_CHUNK_SIZE, DeviceTypes, DeviceDiscoveryEventNames } from "../../common/constants";
7+
import { DeviceTypes, DeviceDiscoveryEventNames } from "../../common/constants";
88
import { cache } from "../../common/decorators";
99

1010
const deviceDescriptorPrimaryKey = "identifier";
@@ -37,8 +37,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
3737
private $debugDataService: IDebugDataService,
3838
private $analyticsService: IAnalyticsService,
3939
private $usbLiveSyncService: DeprecatedUsbLiveSyncService,
40-
private $injector: IInjector,
41-
private $platformsData: IPlatformsData) {
40+
private $injector: IInjector) {
4241
super();
4342
}
4443

@@ -463,26 +462,6 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
463462
const platform = device.deviceInfo.platform;
464463
const deviceBuildInfoDescriptor = _.find(deviceDescriptors, dd => dd.identifier === device.deviceInfo.identifier);
465464

466-
if (liveSyncData.watchAllFiles) {
467-
const platformData: IPlatformData = this.$platformsData.getPlatformData("android", projectData);
468-
const pluginsNeedingRebuild: Array<any> = await platformData.platformProjectService.checkIfPluginsNeedBuild(projectData);
469-
const action = async (buildAarOptions: IBuildOptions) => {
470-
this.$logger.warn(`Building ${buildAarOptions.pluginName}...`);
471-
await platformData.platformProjectService.prebuildNativePlugin(buildAarOptions);
472-
};
473-
const pluginInfo: any = [];
474-
pluginsNeedingRebuild.forEach((item) => {
475-
const options: IBuildOptions = {
476-
pluginName: item.pluginName,
477-
platformsAndroidDirPath: item.platformsAndroidDirPath,
478-
aarOutputDir: item.platformsAndroidDirPath,
479-
tempPluginDirPath: path.join(projectData.platformsDir, 'tempPlugin')
480-
};
481-
pluginInfo.push(options);
482-
});
483-
await executeActionByChunks<IBuildOptions>(pluginInfo, DEFAULT_CHUNK_SIZE, action);
484-
}
485-
486465
await this.ensureLatestAppPackageIsInstalledOnDevice({
487466
device,
488467
preparedPlatforms,
@@ -582,37 +561,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
582561
filesToRemove = [];
583562

584563
const allModifiedFiles = [].concat(currentFilesToSync).concat(currentFilesToRemove);
585-
if (liveSyncData.watchAllFiles) {
586-
587-
const platformData: IPlatformData = this.$platformsData.getPlatformData("android", projectData);
588-
const pluginInfo: any = [];
589-
allModifiedFiles.forEach(async (item) => {
590-
const matchedItem = item.match(/(.*\/node_modules\/[\w-]+)\/platforms\/android\//);
591-
if (matchedItem) {
592-
const matchLength = matchedItem[0].length;
593-
const matchIndex = item.indexOf(matchedItem[0]);
594-
const pluginInputOutputPath = item.substr(0, matchIndex + matchLength);
595-
const pluginPackageJason = require(path.resolve(matchedItem[1], PACKAGE_JSON_FILE_NAME));
596-
597-
if (pluginPackageJason && pluginPackageJason.name) {
598-
const options: IBuildOptions = {
599-
pluginName: pluginPackageJason.name,
600-
platformsAndroidDirPath: pluginInputOutputPath,
601-
aarOutputDir: pluginInputOutputPath,
602-
tempPluginDirPath: path.join(projectData.platformsDir, "tempPlugin")
603-
};
604-
pluginInfo.push(options);
605-
}
606-
}
607-
});
608-
609-
const action = async (buildAarOptions: IBuildOptions) => {
610-
this.$logger.warn(`Building ${buildAarOptions.pluginName}...`);
611-
await platformData.platformProjectService.prebuildNativePlugin(buildAarOptions);
612-
};
613564

614-
await executeActionByChunks<IBuildOptions>(pluginInfo, DEFAULT_CHUNK_SIZE, action);
615-
}
616565
const preparedPlatforms: string[] = [];
617566
const rebuiltInformation: ILiveSyncBuildInfo[] = [];
618567

lib/services/platform-service.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export class PlatformService extends EventEmitter implements IPlatformService {
203203

204204
public async preparePlatform(platformInfo: IPreparePlatformInfo): Promise<boolean> {
205205
const changesInfo = await this.getChangesInfo(platformInfo);
206-
const shouldPrepare = await this.shouldPrepare({ platformInfo, changesInfo });
206+
const shouldPrepare = changesInfo.nativeChanged || await this.shouldPrepare({ platformInfo, changesInfo });
207207

208208
if (shouldPrepare) {
209209
// Always clear up the app directory in platforms if `--bundle` value has changed in between builds or is passed in general
@@ -309,6 +309,7 @@ export class PlatformService extends EventEmitter implements IPlatformService {
309309
platformSpecificData,
310310
changesInfo,
311311
filesToSync,
312+
filesToRemove,
312313
projectFilesConfig,
313314
env
314315
});

lib/services/project-changes-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class ProjectChangesService implements IProjectChangesService {
6464
this._changesInfo.packageChanged = this.isProjectFileChanged(projectData, platform);
6565
this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.appResourcesDirectoryPath, null, projectData);
6666
/*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/
67-
this._changesInfo.nativeChanged = projectChangesOptions.skipModulesNativeCheck ? false : this.containsNewerFiles(
67+
this._changesInfo.nativeChanged = this.containsNewerFiles(
6868
path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME),
6969
path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"),
7070
projectData,

lib/tools/node-modules/node-modules-dest-copy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export class NpmPluginPrepare {
140140
}
141141

142142
public async preparePlugins(dependencies: IDependencyData[], platform: string, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): Promise<void> {
143-
if (_.isEmpty(dependencies) || this.allPrepared(dependencies, platform, projectData)) {
143+
if (_.isEmpty(dependencies)) {
144144
return;
145145
}
146146

0 commit comments

Comments
 (0)