Skip to content

Commit f67e079

Browse files
author
Fatme
authored
Merge pull request #4537 from NativeScript/fatme/gradle
refactor: refactor the logic for building android projects
2 parents 88bbeeb + ed0343a commit f67e079

11 files changed

+354
-212
lines changed

lib/bootstrap.ts

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ $injector.require("performanceService", "./services/performance-service");
1111
$injector.requirePublic("projectService", "./services/project-service");
1212
$injector.require("androidProjectService", "./services/android-project-service");
1313
$injector.require("androidPluginBuildService", "./services/android-plugin-build-service");
14+
$injector.require("gradleCommandService", "./services/android/gradle-command-service");
15+
$injector.require("gradleBuildService", "./services/android/gradle-build-service");
16+
$injector.require("gradleBuildArgsService", "./services/android/gradle-build-args-service");
1417
$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service");
1518
$injector.require("iOSExtensionsService", "./services/ios-extensions-service");
1619
$injector.require("iOSProjectService", "./services/ios-project-service");

lib/definitions/gradle.d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
interface IGradleCommandService {
2+
executeCommand(gradleArgs: string[], options: IGradleCommandOptions): Promise<ISpawnResult>;
3+
}
4+
5+
interface IGradleCommandOptions {
6+
cwd: string;
7+
message?: string;
8+
stdio?: string;
9+
spawnOptions?: ISpawnFromEventOptions;
10+
}
11+
12+
interface IAndroidBuildConfig extends IRelease, IAndroidReleaseOptions, IHasAndroidBundle {
13+
buildOutputStdio?: string;
14+
}
15+
16+
interface IGradleBuildService {
17+
buildProject(projectRoot: string, buildConfig: IAndroidBuildConfig): Promise<void>;
18+
cleanProject(projectRoot: string, buildConfig: IAndroidBuildConfig): Promise<void>;
19+
}
20+
21+
interface IGradleBuildArgsService {
22+
getBuildTaskArgs(buildConfig: IAndroidBuildConfig): string[];
23+
getCleanTaskArgs(buildConfig: IAndroidBuildConfig): string[];
24+
}

lib/definitions/project.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,10 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
439439
/**
440440
* Stops all running processes that might hold a lock on the filesystem.
441441
* Android: Gradle daemon processes are terminated.
442-
* @param {string} projectRoot The root directory of the native project.
442+
* @param {IPlatformData} platformData The data for the specified platform.
443443
* @returns {void}
444444
*/
445-
stopServices(projectRoot: string): Promise<ISpawnResult>;
445+
stopServices(platformData: IPlatformData): Promise<ISpawnResult>;
446446

447447
/**
448448
* Removes build artifacts specific to the platform

lib/services/android-project-service.ts

+19-205
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import * as constants from "../constants";
44
import * as semver from "semver";
55
import * as projectServiceBaseLib from "./platform-project-service-base";
66
import { DeviceAndroidDebugBridge } from "../common/mobile/android/device-android-debug-bridge";
7-
import { attachAwaitDetach } from "../common/helpers";
87
import { Configurations, LiveSyncPaths } from "../common/constants";
9-
import { SpawnOptions } from "child_process";
108
import { performanceLog } from ".././common/decorators";
119

1210
export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService {
@@ -15,23 +13,20 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
1513
private static ANDROID_PLATFORM_NAME = "android";
1614
private static MIN_RUNTIME_VERSION_WITH_GRADLE = "1.5.0";
1715

18-
private isAndroidStudioTemplate: boolean;
19-
2016
constructor(private $androidToolsInfo: IAndroidToolsInfo,
21-
private $childProcess: IChildProcess,
2217
private $errors: IErrors,
2318
$fs: IFileSystem,
24-
private $hostInfo: IHostInfo,
2519
private $logger: ILogger,
2620
$projectDataService: IProjectDataService,
2721
private $injector: IInjector,
2822
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
2923
private $androidPluginBuildService: IAndroidPluginBuildService,
3024
private $platformEnvironmentRequirements: IPlatformEnvironmentRequirements,
3125
private $androidResourcesMigrationService: IAndroidResourcesMigrationService,
32-
private $filesHashService: IFilesHashService) {
26+
private $filesHashService: IFilesHashService,
27+
private $gradleCommandService: IGradleCommandService,
28+
private $gradleBuildService: IGradleBuildService) {
3329
super($fs, $projectDataService);
34-
this.isAndroidStudioTemplate = false;
3530
}
3631

3732
private _platformData: IPlatformData = null;
@@ -41,27 +36,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
4136
}
4237
if (projectData && projectData.platformsDir) {
4338
const projectRoot = path.join(projectData.platformsDir, AndroidProjectService.ANDROID_PLATFORM_NAME);
44-
if (this.isAndroidStudioCompatibleTemplate(projectData)) {
45-
this.isAndroidStudioTemplate = true;
46-
}
47-
48-
const appDestinationDirectoryArr = [projectRoot];
49-
if (this.isAndroidStudioTemplate) {
50-
appDestinationDirectoryArr.push(constants.APP_FOLDER_NAME);
51-
}
52-
appDestinationDirectoryArr.push(constants.SRC_DIR, constants.MAIN_DIR, constants.ASSETS_DIR);
5339

54-
const configurationsDirectoryArr = [projectRoot];
55-
if (this.isAndroidStudioTemplate) {
56-
configurationsDirectoryArr.push(constants.APP_FOLDER_NAME);
57-
}
58-
configurationsDirectoryArr.push(constants.SRC_DIR, constants.MAIN_DIR, constants.MANIFEST_FILE_NAME);
59-
60-
const deviceBuildOutputArr = [projectRoot];
61-
if (this.isAndroidStudioTemplate) {
62-
deviceBuildOutputArr.push(constants.APP_FOLDER_NAME);
63-
}
64-
deviceBuildOutputArr.push(constants.BUILD_DIR, constants.OUTPUTS_DIR, constants.APK_DIR);
40+
const appDestinationDirectoryArr = [projectRoot, constants.APP_FOLDER_NAME, constants.SRC_DIR, constants.MAIN_DIR, constants.ASSETS_DIR];
41+
const configurationsDirectoryArr = [projectRoot, constants.APP_FOLDER_NAME, constants.SRC_DIR, constants.MAIN_DIR, constants.MANIFEST_FILE_NAME];
42+
const deviceBuildOutputArr = [projectRoot, constants.APP_FOLDER_NAME, constants.BUILD_DIR, constants.OUTPUTS_DIR, constants.APK_DIR];
6543

6644
const packageName = this.getProjectNameFromId(projectData);
6745

@@ -155,30 +133,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
155133
const targetSdkVersion = androidToolsInfo && androidToolsInfo.targetSdkVersion;
156134
this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`);
157135

158-
this.isAndroidStudioTemplate = this.isAndroidStudioCompatibleTemplate(projectData, frameworkVersion);
159-
if (this.isAndroidStudioTemplate) {
160-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "*", "-R");
161-
} else {
162-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "libs", "-R");
163-
164-
if (config.pathToTemplate) {
165-
const mainPath = path.join(this.getPlatformData(projectData).projectRoot, constants.SRC_DIR, constants.MAIN_DIR);
166-
this.$fs.createDirectory(mainPath);
167-
shell.cp("-R", path.join(path.resolve(config.pathToTemplate), "*"), mainPath);
168-
} else {
169-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, constants.SRC_DIR, "-R");
170-
}
171-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "build.gradle settings.gradle build-tools", "-Rf");
172-
173-
try {
174-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "gradle.properties", "-Rf");
175-
} catch (e) {
176-
this.$logger.warn(`\n${e}\nIt's possible, the final .apk file will contain all architectures instead of the ones described in the abiFilters!\nYou can fix this by using the latest android platform.`);
177-
}
178-
179-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "gradle", "-R");
180-
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "gradlew gradlew.bat", "-f");
181-
}
136+
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "*", "-R");
182137

183138
this.cleanResValues(targetSdkVersion, projectData);
184139
}
@@ -277,76 +232,13 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
277232

278233
@performanceLog()
279234
public async buildProject(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise<void> {
280-
let task;
281-
const gradleArgs = this.getGradleBuildOptions(buildConfig, projectData);
282-
const baseTask = buildConfig.androidBundle ? "bundle" : "assemble";
283235
const platformData = this.getPlatformData(projectData);
284-
const outputPath = buildConfig.androidBundle ? platformData.bundleBuildOutputPath : platformData.getBuildOutputPath(buildConfig);
285-
if (this.$logger.getLevel() === "TRACE") {
286-
gradleArgs.unshift("--stacktrace");
287-
gradleArgs.unshift("--debug");
288-
}
289-
if (buildConfig.release) {
290-
task = `${baseTask}Release`;
291-
} else {
292-
task = `${baseTask}Debug`;
293-
}
294-
295-
gradleArgs.unshift(task);
296-
297-
const handler = (data: any) => {
298-
this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data);
299-
};
300-
301-
await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME,
302-
this.$childProcess,
303-
handler,
304-
this.executeCommand({
305-
projectRoot: this.getPlatformData(projectData).projectRoot,
306-
gradleArgs,
307-
childProcessOpts: { stdio: buildConfig.buildOutputStdio || "inherit" },
308-
spawnFromEventOptions: { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true },
309-
message: "Gradle build..."
310-
})
311-
);
236+
await this.$gradleBuildService.buildProject(platformData.projectRoot, buildConfig);
312237

238+
const outputPath = buildConfig.androidBundle ? platformData.bundleBuildOutputPath : platformData.getBuildOutputPath(buildConfig);
313239
await this.$filesHashService.saveHashesForProject(this._platformData, outputPath);
314240
}
315241

316-
private getGradleBuildOptions(settings: IAndroidBuildOptionsSettings, projectData: IProjectData): Array<string> {
317-
const configurationFilePath = this.getPlatformData(projectData).configurationFilePath;
318-
319-
const buildOptions: Array<string> = this.getBuildOptions(configurationFilePath);
320-
321-
if (settings.release) {
322-
buildOptions.push("-Prelease");
323-
buildOptions.push(`-PksPath=${path.resolve(settings.keyStorePath)}`);
324-
buildOptions.push(`-Palias=${settings.keyStoreAlias}`);
325-
buildOptions.push(`-Ppassword=${settings.keyStoreAliasPassword}`);
326-
buildOptions.push(`-PksPassword=${settings.keyStorePassword}`);
327-
}
328-
329-
return buildOptions;
330-
}
331-
332-
private getBuildOptions(configurationFilePath?: string): Array<string> {
333-
this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true });
334-
335-
const androidToolsInfo = this.$androidToolsInfo.getToolsInfo();
336-
const compileSdk = androidToolsInfo.compileSdkVersion;
337-
const targetSdk = this.getTargetFromAndroidManifest(configurationFilePath) || compileSdk;
338-
const buildToolsVersion = androidToolsInfo.buildToolsVersion;
339-
const generateTypings = androidToolsInfo.generateTypings;
340-
const buildOptions = [
341-
`-PcompileSdk=android-${compileSdk}`,
342-
`-PtargetSdk=${targetSdk}`,
343-
`-PbuildToolsVersion=${buildToolsVersion}`,
344-
`-PgenerateTypings=${generateTypings}`
345-
];
346-
347-
return buildOptions;
348-
}
349-
350242
public async buildForDeploy(projectRoot: string, projectData: IProjectData, buildConfig?: IBuildConfig): Promise<void> {
351243
return this.buildProject(projectRoot, projectData, buildConfig);
352244
}
@@ -456,25 +348,18 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
456348
return nativescript && (nativescript.android || (nativescript.platforms && nativescript.platforms.android));
457349
}
458350

459-
public stopServices(projectRoot: string): Promise<ISpawnResult> {
460-
return this.executeCommand({
461-
projectRoot,
462-
gradleArgs: ["--stop", "--quiet"],
463-
childProcessOpts: { stdio: "pipe" },
464-
message: "Gradle stop services..."
351+
public async stopServices(platformData: IPlatformData): Promise<ISpawnResult> {
352+
const result = await this.$gradleCommandService.executeCommand(["--stop", "--quiet"], {
353+
cwd: platformData.projectRoot,
354+
message: "Gradle stop services...",
355+
stdio: "pipe"
465356
});
357+
358+
return result;
466359
}
467360

468361
public async cleanProject(projectRoot: string, projectData: IProjectData): Promise<void> {
469-
if (this.$androidToolsInfo.getToolsInfo().androidHomeEnvVar) {
470-
const gradleArgs = this.getGradleBuildOptions({ release: false }, projectData);
471-
gradleArgs.unshift("clean");
472-
await this.executeCommand({
473-
projectRoot,
474-
gradleArgs,
475-
message: "Gradle clean..."
476-
});
477-
}
362+
await this.$gradleBuildService.cleanProject(projectRoot, { release: false });
478363
}
479364

480365
public async cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise<void> {
@@ -494,10 +379,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
494379
shell.cp(cpArg, paths, projectRoot);
495380
}
496381

497-
private async spawn(command: string, args: string[], opts?: any, spawnOpts?: ISpawnFromEventOptions): Promise<ISpawnResult> {
498-
return this.$childProcess.spawnFromEvent(command, args, "close", opts || { stdio: "inherit" }, spawnOpts);
499-
}
500-
501382
private validatePackageName(packageName: string): void {
502383
//Make the package conform to Java package types
503384
//Enforce underscore limitation
@@ -522,81 +403,14 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
522403
}
523404
}
524405

525-
private getTargetFromAndroidManifest(configurationFilePath: string): string {
526-
let versionInManifest: string;
527-
if (this.$fs.exists(configurationFilePath)) {
528-
const targetFromAndroidManifest: string = this.$fs.readText(configurationFilePath);
529-
if (targetFromAndroidManifest) {
530-
const match = targetFromAndroidManifest.match(/.*?android:targetSdkVersion=\"(.*?)\"/);
531-
if (match && match[1]) {
532-
versionInManifest = match[1];
533-
}
534-
}
535-
}
536-
537-
return versionInManifest;
538-
}
539-
540-
private async executeCommand(opts: { projectRoot: string, gradleArgs: any, childProcessOpts?: SpawnOptions, spawnFromEventOptions?: ISpawnFromEventOptions, message: string }): Promise<ISpawnResult> {
541-
if (this.$androidToolsInfo.getToolsInfo().androidHomeEnvVar) {
542-
const { projectRoot, gradleArgs, message, spawnFromEventOptions } = opts;
543-
const gradlew = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew";
544-
545-
if (this.$logger.getLevel() === "INFO") {
546-
gradleArgs.push("--quiet");
547-
}
548-
549-
this.$logger.info(message);
550-
551-
const childProcessOpts = opts.childProcessOpts || {};
552-
childProcessOpts.cwd = childProcessOpts.cwd || projectRoot;
553-
childProcessOpts.stdio = childProcessOpts.stdio || "inherit";
554-
let commandResult;
555-
try {
556-
commandResult = await this.spawn(gradlew,
557-
gradleArgs,
558-
childProcessOpts,
559-
spawnFromEventOptions);
560-
} catch (err) {
561-
this.$errors.failWithoutHelp(err.message);
562-
}
563-
564-
return commandResult;
565-
}
566-
}
567-
568-
private isAndroidStudioCompatibleTemplate(projectData: IProjectData, frameworkVersion?: string): boolean {
569-
const currentPlatformData: IDictionary<any> = this.$projectDataService.getNSValue(projectData.projectDir, constants.TNS_ANDROID_RUNTIME_NAME);
570-
const platformVersion = (currentPlatformData && currentPlatformData[constants.VERSION_STRING]) || frameworkVersion;
571-
572-
if (!platformVersion) {
573-
return true;
574-
}
575-
576-
if (platformVersion === constants.PackageVersion.NEXT || platformVersion === constants.PackageVersion.LATEST || platformVersion === constants.PackageVersion.RC) {
577-
return true;
578-
}
579-
580-
const androidStudioCompatibleTemplate = "3.4.0";
581-
const normalizedPlatformVersion = `${semver.major(platformVersion)}.${semver.minor(platformVersion)}.0`;
582-
583-
return semver.gte(normalizedPlatformVersion, androidStudioCompatibleTemplate);
584-
}
585-
586406
private getLegacyAppResourcesDestinationDirPath(projectData: IProjectData): string {
587-
const resourcePath: string[] = [constants.SRC_DIR, constants.MAIN_DIR, constants.RESOURCES_DIR];
588-
if (this.isAndroidStudioTemplate) {
589-
resourcePath.unshift(constants.APP_FOLDER_NAME);
590-
}
407+
const resourcePath: string[] = [constants.APP_FOLDER_NAME, constants.SRC_DIR, constants.MAIN_DIR, constants.RESOURCES_DIR];
591408

592409
return path.join(this.getPlatformData(projectData).projectRoot, ...resourcePath);
593410
}
594411

595412
private getUpdatedAppResourcesDestinationDirPath(projectData: IProjectData): string {
596-
const resourcePath: string[] = [constants.SRC_DIR];
597-
if (this.isAndroidStudioTemplate) {
598-
resourcePath.unshift(constants.APP_FOLDER_NAME);
599-
}
413+
const resourcePath: string[] = [constants.APP_FOLDER_NAME, constants.SRC_DIR];
600414

601415
return path.join(this.getPlatformData(projectData).projectRoot, ...resourcePath);
602416
}

0 commit comments

Comments
 (0)