Skip to content

Commit 99a5ec8

Browse files
committed
refactor: refactor gradle related part of android-project-service
* remove the support for targetSdk * extract the logic to separate services
1 parent 88bbeeb commit 99a5ec8

File tree

8 files changed

+174
-139
lines changed

8 files changed

+174
-139
lines changed

lib/bootstrap.ts

Lines changed: 3 additions & 0 deletions
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

Lines changed: 24 additions & 0 deletions
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+
message?: string;
7+
cwd: string;
8+
stdio?: string;
9+
spawnOptions?: any;
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 14 additions & 132 deletions
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 {
@@ -18,18 +16,18 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
1816
private isAndroidStudioTemplate: boolean;
1917

2018
constructor(private $androidToolsInfo: IAndroidToolsInfo,
21-
private $childProcess: IChildProcess,
2219
private $errors: IErrors,
2320
$fs: IFileSystem,
24-
private $hostInfo: IHostInfo,
2521
private $logger: ILogger,
2622
$projectDataService: IProjectDataService,
2723
private $injector: IInjector,
2824
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
2925
private $androidPluginBuildService: IAndroidPluginBuildService,
3026
private $platformEnvironmentRequirements: IPlatformEnvironmentRequirements,
3127
private $androidResourcesMigrationService: IAndroidResourcesMigrationService,
32-
private $filesHashService: IFilesHashService) {
28+
private $filesHashService: IFilesHashService,
29+
private $gradleCommandService: IGradleCommandService,
30+
private $gradleBuildService: IGradleBuildService) {
3331
super($fs, $projectDataService);
3432
this.isAndroidStudioTemplate = false;
3533
}
@@ -277,76 +275,13 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
277275

278276
@performanceLog()
279277
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";
283278
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-
);
279+
await this.$gradleBuildService.buildProject(platformData.projectRoot, buildConfig);
312280

281+
const outputPath = buildConfig.androidBundle ? platformData.bundleBuildOutputPath : platformData.getBuildOutputPath(buildConfig);
313282
await this.$filesHashService.saveHashesForProject(this._platformData, outputPath);
314283
}
315284

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-
350285
public async buildForDeploy(projectRoot: string, projectData: IProjectData, buildConfig?: IBuildConfig): Promise<void> {
351286
return this.buildProject(projectRoot, projectData, buildConfig);
352287
}
@@ -456,25 +391,19 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
456391
return nativescript && (nativescript.android || (nativescript.platforms && nativescript.platforms.android));
457392
}
458393

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..."
394+
public async stopServices(platformData: IPlatformData): Promise<ISpawnResult> {
395+
const result = await this.$gradleCommandService.executeCommand(["--stop", "--quiet"], {
396+
cwd: platformData.projectRoot,
397+
message: "Gradle stop services...",
398+
stdio: "pipe"
465399
});
400+
401+
return result;
466402
}
467403

468404
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-
}
405+
// TODO: Check why there isn't a clean release build and a clean release aab build
406+
await this.$gradleBuildService.cleanProject(projectRoot, { release: false });
478407
}
479408

480409
public async cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise<void> {
@@ -494,10 +423,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
494423
shell.cp(cpArg, paths, projectRoot);
495424
}
496425

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-
501426
private validatePackageName(packageName: string): void {
502427
//Make the package conform to Java package types
503428
//Enforce underscore limitation
@@ -522,49 +447,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
522447
}
523448
}
524449

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-
568450
private isAndroidStudioCompatibleTemplate(projectData: IProjectData, frameworkVersion?: string): boolean {
569451
const currentPlatformData: IDictionary<any> = this.$projectDataService.getNSValue(projectData.projectDir, constants.TNS_ANDROID_RUNTIME_NAME);
570452
const platformVersion = (currentPlatformData && currentPlatformData[constants.VERSION_STRING]) || frameworkVersion;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as path from "path";
2+
import { Configurations } from "../../common/constants";
3+
4+
export class GradleBuildArgsService implements IGradleBuildArgsService {
5+
constructor(private $androidToolsInfo: IAndroidToolsInfo,
6+
private $logger: ILogger) { }
7+
8+
public getBuildTaskArgs(buildConfig: IAndroidBuildConfig): string[] {
9+
const args = this.getBaseTaskArgs(buildConfig);
10+
args.unshift(this.getBuildTaskName(buildConfig));
11+
12+
return args;
13+
}
14+
15+
public getCleanTaskArgs(buildConfig: IAndroidBuildConfig): string[] {
16+
const args = this.getBaseTaskArgs(buildConfig);
17+
args.unshift("clean");
18+
19+
return args;
20+
}
21+
22+
private getBaseTaskArgs(buildConfig: IAndroidBuildConfig): string[] {
23+
const args = this.getBuildLoggingArgs();
24+
25+
const toolsInfo = this.$androidToolsInfo.getToolsInfo();
26+
args.push(
27+
`-PcompileSdk=android-${toolsInfo.compileSdkVersion}`,
28+
`-PbuildToolsVersion=${toolsInfo.buildToolsVersion}`,
29+
`-PgenerateTypings=${toolsInfo.generateTypings}`
30+
);
31+
32+
if (buildConfig.release) {
33+
args.push(
34+
"-Prelease",
35+
`-PksPath=${path.resolve(buildConfig.keyStorePath)}`,
36+
`-Palias=${buildConfig.keyStoreAlias}`,
37+
`-Ppassword=${buildConfig.keyStoreAliasPassword}`,
38+
`-PksPassword=${buildConfig.keyStorePassword}`
39+
);
40+
}
41+
42+
return args;
43+
}
44+
45+
private getBuildLoggingArgs(): string[] {
46+
const args = [];
47+
48+
const logLevel = this.$logger.getLevel();
49+
if (logLevel === "TRACE") {
50+
args.push("--stacktrace", "--debug");
51+
} else if (logLevel === "INFO") {
52+
args.push("--quiet");
53+
}
54+
55+
return args;
56+
}
57+
58+
private getBuildTaskName(buildConfig: IAndroidBuildConfig): string {
59+
const baseTaskName = buildConfig.androidBundle ? "bundle" : "assemble";
60+
const buildTaskName = buildConfig.release ? `${baseTaskName}${Configurations.Release}` : `${baseTaskName}${Configurations.Debug}`;
61+
62+
return buildTaskName;
63+
}
64+
}
65+
$injector.register("gradleBuildArgsService", GradleBuildArgsService);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { attachAwaitDetach } from "../../common/helpers";
2+
import * as constants from "../../constants";
3+
import { EventEmitter } from "events";
4+
5+
export class GradleBuildService extends EventEmitter implements IGradleBuildService {
6+
constructor(
7+
private $childProcess: IChildProcess,
8+
private $gradleBuildArgsService: IGradleBuildArgsService,
9+
private $gradleCommandService: IGradleCommandService,
10+
) { super(); }
11+
12+
public async buildProject(projectRoot: string, buildConfig: IAndroidBuildConfig): Promise<void> {
13+
const buildTaskArgs = this.$gradleBuildArgsService.getBuildTaskArgs(buildConfig);
14+
const spawnOptions = { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true };
15+
const gradleCommandOptions = { cwd: projectRoot, message: "Gradle build...", stdio: buildConfig.buildOutputStdio, spawnOptions };
16+
17+
await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME,
18+
this.$childProcess,
19+
(data: any) => this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data),
20+
this.$gradleCommandService.executeCommand(buildTaskArgs, gradleCommandOptions)
21+
);
22+
}
23+
24+
public async cleanProject(projectRoot: string, buildConfig: IAndroidBuildConfig): Promise<void> {
25+
const cleanTaskArgs = this.$gradleBuildArgsService.getCleanTaskArgs(buildConfig);
26+
const gradleCommandOptions = { cwd: projectRoot, message: "Gradle clean..." };
27+
await this.$gradleCommandService.executeCommand(cleanTaskArgs, gradleCommandOptions);
28+
}
29+
}
30+
$injector.register("gradleBuildService", GradleBuildService);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export class GradleCommandService implements IGradleCommandService {
2+
constructor(
3+
private $childProcess: IChildProcess,
4+
private $errors: IErrors,
5+
private $hostInfo: IHostInfo,
6+
private $logger: ILogger
7+
) { }
8+
9+
public async executeCommand(gradleArgs: string[], options: IGradleCommandOptions): Promise<ISpawnResult> {
10+
const { message, cwd, stdio = "inherit", spawnOptions } = options;
11+
this.$logger.info(message);
12+
13+
const childProcessOptions = { cwd, stdio };
14+
const gradleExecutable = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew";
15+
16+
const result = await this.executeCommandSafe(gradleExecutable, gradleArgs, childProcessOptions, spawnOptions);
17+
18+
return result;
19+
}
20+
21+
private async executeCommandSafe(gradleExecutable: string, gradleArgs: string[], childProcessOptions: any, spawnOptions: any) {
22+
try {
23+
const result = await this.$childProcess.spawnFromEvent(gradleExecutable, gradleArgs, "close", childProcessOptions, spawnOptions);
24+
25+
return result;
26+
} catch (err) {
27+
this.$errors.failWithoutHelp(err.message);
28+
}
29+
}
30+
}
31+
$injector.register("gradleCommandService", GradleCommandService);

0 commit comments

Comments
 (0)