Skip to content

Commit cd2aad3

Browse files
authored
Merge pull request #4084 from NativeScript/kddimitrov/aab-build
Kddimitrov/aab build
2 parents 86f280f + a6dcfb9 commit cd2aad3

25 files changed

+377
-82
lines changed

docs/man_pages/project/testing/build-android.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Builds the project for Android and produces an APK that you can manually deploy
1313

1414
Usage | Synopsis
1515
---|---
16-
General | `$ tns build android [--compileSdk <API Level>] [--key-store-path <File Path> --key-store-password <Password> --key-store-alias <Name> --key-store-alias-password <Password>] [--release] [--static-bindings] [--copy-to <File Path>] [--bundle [<value>] [--env.*]]`
16+
General | `$ tns build android [--compileSdk <API Level>] [--key-store-path <File Path> --key-store-password <Password> --key-store-alias <Name> --key-store-alias-password <Password>] [--release] [--static-bindings] [--copy-to <File Path>] [--bundle [<value>] [--env.*]] [--aab]`
1717

1818
### Options
1919

@@ -27,6 +27,7 @@ General | `$ tns build android [--compileSdk <API Level>] [--key-store-path <Fil
2727
* `--copy-to` - Specifies the file path where the built `.apk` will be copied. If it points to a non-existent directory, it will be created. If the specified value is directory, the original file name will be used.
2828
* `--bundle` - Specifies that the `webpack` bundler will be used to bundle the application.
2929
* `--env.*` - Specifies additional flags that the bundler may process. May be passed multiple times. For example: `--env.uglify --env.snapshot`.
30+
* `--aab` - Specifies that the build will produce an Android App Bundle(`.aab`) file.
3031

3132
<% if(isHtml) { %>
3233

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ $injector.require("devicePathProvider", "./device-path-provider");
125125
$injector.requireCommand("platform|clean", "./commands/platform-clean");
126126

127127
$injector.require("bundleValidatorHelper", "./helpers/bundle-validator-helper");
128+
$injector.require("androidBundleValidatorHelper", "./helpers/android-bundle-validator-helper");
128129
$injector.require("liveSyncCommandHelper", "./helpers/livesync-command-helper");
129130
$injector.require("deployCommandHelper", "./helpers/deploy-command-helper");
130131

lib/commands/build.ts

+31-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE } from "../constants";
1+
import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE, AndroidAppBundleMessages } from "../constants";
22
import { ValidatePlatformCommandBase } from "./command-base";
33

44
export abstract class BuildCommandBase extends ValidatePlatformCommandBase {
@@ -8,12 +8,13 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase {
88
$platformsData: IPlatformsData,
99
protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
1010
$platformService: IPlatformService,
11-
private $bundleValidatorHelper: IBundleValidatorHelper) {
11+
private $bundleValidatorHelper: IBundleValidatorHelper,
12+
protected $logger: ILogger) {
1213
super($options, $platformsData, $platformService, $projectData);
1314
this.$projectData.initializeProjectData();
1415
}
1516

16-
public async executeCore(args: string[]): Promise<void> {
17+
public async executeCore(args: string[]): Promise<string> {
1718
const platform = args[0].toLowerCase();
1819
const appFilesUpdaterOptions: IAppFilesUpdaterOptions = {
1920
bundle: !!this.$options.bundle,
@@ -41,12 +42,19 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase {
4142
keyStoreAlias: this.$options.keyStoreAlias,
4243
keyStorePath: this.$options.keyStorePath,
4344
keyStoreAliasPassword: this.$options.keyStoreAliasPassword,
44-
keyStorePassword: this.$options.keyStorePassword
45+
keyStorePassword: this.$options.keyStorePassword,
46+
androidBundle: this.$options.aab
4547
};
46-
await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData);
48+
49+
const outputPath = await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData);
50+
4751
if (this.$options.copyTo) {
4852
this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData);
53+
} else {
54+
this.$logger.info(`The build result is located at: ${outputPath}`);
4955
}
56+
57+
return outputPath;
5058
}
5159

5260
protected validatePlatform(platform: string): void {
@@ -84,12 +92,13 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand {
8492
$platformsData: IPlatformsData,
8593
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
8694
$platformService: IPlatformService,
87-
$bundleValidatorHelper: IBundleValidatorHelper) {
88-
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
95+
$bundleValidatorHelper: IBundleValidatorHelper,
96+
$logger: ILogger) {
97+
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper, $logger);
8998
}
9099

91100
public async execute(args: string[]): Promise<void> {
92-
return this.executeCore([this.$platformsData.availablePlatforms.iOS]);
101+
await this.executeCore([this.$platformsData.availablePlatforms.iOS]);
93102
}
94103

95104
public async canExecute(args: string[]): Promise<boolean | ICanExecuteCommandOutput> {
@@ -117,18 +126,28 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand {
117126
$platformsData: IPlatformsData,
118127
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
119128
$platformService: IPlatformService,
120-
$bundleValidatorHelper: IBundleValidatorHelper) {
121-
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
129+
$bundleValidatorHelper: IBundleValidatorHelper,
130+
protected $androidBundleValidatorHelper: IAndroidBundleValidatorHelper,
131+
protected $logger: ILogger) {
132+
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper, $logger);
122133
}
123134

124135
public async execute(args: string[]): Promise<void> {
125-
return this.executeCore([this.$platformsData.availablePlatforms.Android]);
136+
await this.executeCore([this.$platformsData.availablePlatforms.Android]);
137+
138+
if (this.$options.aab) {
139+
this.$logger.info(AndroidAppBundleMessages.ANDROID_APP_BUNDLE_DOCS_MESSAGE);
140+
141+
if (this.$options.release) {
142+
this.$logger.info(AndroidAppBundleMessages.ANDROID_APP_BUNDLE_PUBLISH_DOCS_MESSAGE);
143+
}
144+
}
126145
}
127146

128147
public async canExecute(args: string[]): Promise<boolean | ICanExecuteCommandOutput> {
129148
const platform = this.$devicePlatformsConstants.Android;
130149
super.validatePlatform(platform);
131-
150+
this.$androidBundleValidatorHelper.validateRuntimeVersion(this.$projectData);
132151
let result = await super.canExecuteCommandBase(platform, { notConfiguredEnvOptions: { hideSyncToPreviewAppOption: true }});
133152
if (result.canExecute) {
134153
if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) {

lib/commands/debug.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
1919
private $debugDataService: IDebugDataService,
2020
private $liveSyncService: IDebugLiveSyncService,
2121
private $prompter: IPrompter,
22-
private $liveSyncCommandHelper: ILiveSyncCommandHelper) {
22+
private $liveSyncCommandHelper: ILiveSyncCommandHelper,
23+
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) {
2324
super($options, $platformsData, $platformService, $projectData);
2425
}
2526

@@ -108,6 +109,8 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
108109
}
109110

110111
public async canExecute(args: string[]): Promise<ICanExecuteCommandOutput> {
112+
this.$androidBundleValidatorHelper.validateNoAab();
113+
111114
if (!this.$platformService.isPlatformSupportedForOS(this.platform, this.$projectData)) {
112115
this.$errors.fail(`Applications for platform ${this.platform} can not be built on this OS`);
113116
}

lib/commands/deploy.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement
1212
private $errors: IErrors,
1313
private $mobileHelper: Mobile.IMobileHelper,
1414
$platformsData: IPlatformsData,
15-
private $bundleValidatorHelper: IBundleValidatorHelper) {
15+
private $bundleValidatorHelper: IBundleValidatorHelper,
16+
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) {
1617
super($options, $platformsData, $platformService, $projectData);
1718
this.$projectData.initializeProjectData();
1819
}
@@ -24,6 +25,7 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement
2425
}
2526

2627
public async canExecute(args: string[]): Promise<boolean | ICanExecuteCommandOutput> {
28+
this.$androidBundleValidatorHelper.validateNoAab();
2729
this.$bundleValidatorHelper.validate();
2830
if (!args || !args.length || args.length > 1) {
2931
return false;

lib/commands/plugin/build-plugin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class BuildPluginCommand implements ICommand {
3232
temp.track();
3333
const tempAndroidProject = temp.mkdirSync("android-project");
3434

35-
const options: IBuildOptions = {
35+
const options: IPluginBuildOptions = {
3636
aarOutputDir: platformsAndroidPath,
3737
platformsAndroidDirPath: platformsAndroidPath,
3838
pluginName: pluginName,

lib/commands/run.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ export class RunCommandBase implements ICommand {
1010
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
1111
private $errors: IErrors,
1212
private $hostInfo: IHostInfo,
13-
private $liveSyncCommandHelper: ILiveSyncCommandHelper) { }
13+
private $liveSyncCommandHelper: ILiveSyncCommandHelper,
14+
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) { }
1415

1516
public allowedParameters: ICommandParameter[] = [];
1617
public async execute(args: string[]): Promise<void> {
@@ -22,6 +23,8 @@ export class RunCommandBase implements ICommand {
2223
this.$errors.fail(ERROR_NO_VALID_SUBCOMMAND_FORMAT, "run");
2324
}
2425

26+
this.$androidBundleValidatorHelper.validateNoAab();
27+
2528
this.$projectData.initializeProjectData();
2629
this.platform = args[0] || this.platform;
2730

@@ -35,7 +38,6 @@ export class RunCommandBase implements ICommand {
3538
const checkEnvironmentRequirementsOutput = validatePlatformOutput[this.platform.toLowerCase()].checkEnvironmentRequirementsOutput;
3639
this.liveSyncCommandHelperAdditionalOptions.syncToPreviewApp = checkEnvironmentRequirementsOutput && checkEnvironmentRequirementsOutput.selectedOption === "Sync to Playground";
3740
}
38-
3941
return true;
4042
}
4143
}
@@ -66,14 +68,14 @@ export class RunIosCommand implements ICommand {
6668
}
6769

6870
public async execute(args: string[]): Promise<void> {
69-
if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) {
70-
this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`);
71-
}
72-
7371
return this.runCommand.execute(args);
7472
}
7573

7674
public async canExecute(args: string[]): Promise<boolean> {
75+
if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) {
76+
this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`);
77+
}
78+
7779
const result = await this.runCommand.canExecute(args) && await this.$platformService.validateOptions(this.$options.provision, this.$options.teamId, this.$projectData, this.$platformsData.availablePlatforms.iOS);
7880
return result;
7981
}

lib/constants.ts

+12
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ export const BUILD_XCCONFIG_FILE_NAME = "build.xcconfig";
3333
export const BUILD_DIR = "build";
3434
export const OUTPUTS_DIR = "outputs";
3535
export const APK_DIR = "apk";
36+
export const BUNDLE_DIR = "bundle";
3637
export const RESOURCES_DIR = "res";
3738
export const CONFIG_NS_FILE_NAME = "nsconfig.json";
3839
export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath";
3940
export const CONFIG_NS_APP_ENTRY = "appPath";
4041
export const DEPENDENCIES_JSON_NAME = "dependencies.json";
4142
export const APK_EXTENSION_NAME = ".apk";
43+
export const AAB_EXTENSION_NAME = ".aab";
4244
export const HASHES_FILE_NAME = ".nshashes";
4345

4446
export class PackageVersion {
@@ -241,3 +243,13 @@ export class BundleValidatorMessages {
241243
public static MissingBundlePlugin = "Passing --bundle requires a bundling plugin. No bundling plugin found or the specified bundling plugin is invalid.";
242244
public static NotSupportedVersion = `The NativeScript CLI requires nativescript-dev-webpack %s or later to work properly. After updating nativescript-dev-webpack you need to ensure "webpack.config.js" file is up to date with the one in the new version of nativescript-dev-webpack. You can automatically update it using "./node_modules/.bin/update-ns-webpack --configs" command.`;
243245
}
246+
247+
export class AndroidBundleValidatorMessages {
248+
public static AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android App Bundle) parameter.";
249+
public static NOT_SUPPORTED_RUNTIME_VERSION = "Android App Bundle (--aab) option requires NativeScript Android Runtime (tns-android) version %s and above.";
250+
}
251+
252+
export class AndroidAppBundleMessages {
253+
public static ANDROID_APP_BUNDLE_DOCS_MESSAGE = "What is Android App Bundle: https://docs.nativescript.org/tooling/publishing/android-app-bundle";
254+
public static ANDROID_APP_BUNDLE_PUBLISH_DOCS_MESSAGE = "How to use Android App Bundle for publishing: https://docs.nativescript.org/tooling/publishing/publishing-android-apps#android-app-bundle";
255+
}

lib/declarations.d.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,11 @@ interface IPluginSeedOptions {
458458
pluginName: string;
459459
}
460460

461-
interface IOptions extends IRelease, IDeviceIdentifier, IJustLaunch, IAvd, IAvailableDevices, IProfileDir, IHasEmulatorOption, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions, IGenerateOptions {
461+
interface IAndroidBundleOptions {
462+
aab: boolean;
463+
}
464+
465+
interface IOptions extends IRelease, IDeviceIdentifier, IJustLaunch, IAvd, IAvailableDevices, IProfileDir, IHasEmulatorOption, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, IAndroidBundleOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions, IGenerateOptions {
462466
argv: IYargArgv;
463467
validateOptions(commandSpecificDashedOptions?: IDictionary<IDashedOption>): void;
464468
options: IDictionary<IDashedOption>;
@@ -531,7 +535,11 @@ interface IEnvOptions {
531535
env: Object;
532536
}
533537

534-
interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease { }
538+
interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, IHasAndroidBundle { }
539+
540+
interface IHasAndroidBundle {
541+
androidBundle?: boolean;
542+
}
535543

536544
interface IAppFilesUpdaterOptions extends IBundle, IRelease, IOptionalWatchAllFiles, IHasUseHotModuleReloadOption { }
537545

@@ -856,6 +864,23 @@ interface IBundleValidatorHelper {
856864
validate(minSupportedVersion?: string): void;
857865
}
858866

867+
868+
interface IAndroidBundleValidatorHelper {
869+
/**
870+
* Validates android bundling option is not provided.
871+
* Commands that require deploy of the application must not be called with --aab option
872+
* @return {void}
873+
*/
874+
validateNoAab(): void;
875+
876+
/**
877+
* Validates android runtime version is sufficient to support bundling option --aab.
878+
* @param {IProjectData} projectData DTO with information about the project.
879+
* @return {void}
880+
*/
881+
validateRuntimeVersion(projectData: IProjectData): void
882+
}
883+
859884
interface INativeScriptCloudExtensionService {
860885
/**
861886
* Installs nativescript-cloud extension

lib/definitions/android-plugin-migrator.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
interface IBuildOptions extends IAndroidBuildOptions{
2+
interface IPluginBuildOptions extends IAndroidBuildOptions{
33
projectDir?: string
44
}
55

@@ -11,8 +11,8 @@ interface IAndroidBuildOptions {
1111
}
1212

1313
interface IAndroidPluginBuildService {
14-
buildAar(options: IBuildOptions): Promise<boolean>;
15-
migrateIncludeGradle(options: IBuildOptions): boolean;
14+
buildAar(options: IPluginBuildOptions): Promise<boolean>;
15+
migrateIncludeGradle(options: IPluginBuildOptions): boolean;
1616
}
1717

1818
/**

lib/definitions/platform.d.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ interface IPlatformData {
268268
normalizedPlatformName: string;
269269
appDestinationDirectoryPath: string;
270270
deviceBuildOutputPath: string;
271+
bundleBuildOutputPath?: string;
271272
emulatorBuildOutputPath?: string;
272273
getValidBuildOutputData(buildOptions: IBuildOutputOptions): IValidBuildOutputData;
273274
frameworkFilesExtensions: string[];
@@ -285,10 +286,7 @@ interface IValidBuildOutputData {
285286
regexes?: RegExp[];
286287
}
287288

288-
interface IBuildOutputOptions {
289-
isReleaseBuild?: boolean;
290-
isForDevice?: boolean;
291-
}
289+
interface IBuildOutputOptions extends Partial<IBuildForDevice>, IRelease, IHasAndroidBundle {}
292290

293291
interface IPlatformsData {
294292
availablePlatforms: any;

lib/definitions/project.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
437437
/**
438438
* Build native part of a nativescript plugins if necessary
439439
*/
440-
prebuildNativePlugin(buildOptions: IBuildOptions): Promise<void>;
440+
prebuildNativePlugin(buildOptions: IPluginBuildOptions): Promise<void>;
441441

442442
/**
443443
* Traverse through the production dependencies and find plugins that need build/rebuild
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as util from "util";
2+
import { AndroidBundleValidatorMessages, TNS_ANDROID_RUNTIME_NAME } from "../constants";
3+
import { VersionValidatorHelper } from "./version-validator-helper";
4+
5+
export class AndroidBundleValidatorHelper extends VersionValidatorHelper implements IAndroidBundleValidatorHelper {
6+
public static MIN_RUNTIME_VERSION = "5.0.0";
7+
8+
constructor(protected $projectData: IProjectData,
9+
protected $errors: IErrors,
10+
protected $options: IOptions,
11+
protected $projectDataService: IProjectDataService) {
12+
super();
13+
}
14+
15+
public validateNoAab(): void {
16+
if (this.$options.aab) {
17+
this.$errors.fail(AndroidBundleValidatorMessages.AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE);
18+
}
19+
}
20+
21+
public validateRuntimeVersion(projectData: IProjectData): void {
22+
if (this.$options.aab) {
23+
const androidRuntimeInfo = this.$projectDataService.getNSValue(projectData.projectDir, TNS_ANDROID_RUNTIME_NAME);
24+
const androidRuntimeVersion = androidRuntimeInfo ? androidRuntimeInfo.version : "";
25+
const shouldThrowError = androidRuntimeVersion &&
26+
this.isValidVersion(androidRuntimeVersion) &&
27+
this.isVersionLowerThan(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION);
28+
29+
if (shouldThrowError) {
30+
this.$errors.failWithoutHelp(util.format(AndroidBundleValidatorMessages.NOT_SUPPORTED_RUNTIME_VERSION, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION));
31+
}
32+
}
33+
}
34+
}
35+
36+
$injector.register("androidBundleValidatorHelper", AndroidBundleValidatorHelper);

0 commit comments

Comments
 (0)