Skip to content
6 changes: 5 additions & 1 deletion lib/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase {
const buildData = this.$buildDataService.getBuildData(
this.$projectData.projectDir,
platform,
this.$options
{
...this.$options.argv,
// we disable buildFilterDevicesArch for build only to ensure we dont use it in production builds
buildFilterDevicesArch: false
}
);
const outputPath = await this.$buildController.prepareAndBuild(buildData);

Expand Down
6 changes: 6 additions & 0 deletions lib/common/definitions/mobile.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ declare global {
* For iOS simulators - same as the identifier.
*/
imageIdentifier?: string;

/**
* Optional property describing the architecture of the device
* Available for Android only
*/
abis?: string[];
}

interface IDeviceError extends Error, IDeviceIdentifier {}
Expand Down
4 changes: 4 additions & 0 deletions lib/common/mobile/android/android-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ interface IAndroidDeviceDetails {
name: string;
release: string;
brand: string;
'cpu.abi': string;
'cpu.abilist64': string;
'cpu.abilist32': string;
}

interface IAdbDeviceStatusInfo {
Expand Down Expand Up @@ -96,6 +99,7 @@ export class AndroidDevice implements Mobile.IAndroidDevice {
identifier: this.identifier,
displayName: details.name,
model: details.model,
abis: details['cpu.abilist64'].split(',').concat(details['cpu.abilist32'].split(',')),
version,
vendor: details.brand,
platform: this.$devicePlatformsConstants.Android,
Expand Down
2 changes: 1 addition & 1 deletion lib/controllers/build-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class BuildController extends EventEmitter implements IBuildController {
);

if (buildData.copyTo) {
this.$buildArtifactsService.copyLatestAppPackage(
this.$buildArtifactsService.copyAppPackages(
buildData.copyTo,
platformData,
buildData
Expand Down
5 changes: 2 additions & 3 deletions lib/controllers/deploy-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ export class DeployController {
},
};
await this.$prepareController.prepare(prepareData);
const packageFilePath = await deviceDescriptor.buildAction();
await deviceDescriptor.buildAction();
await this.$deviceInstallAppService.installOnDevice(
device,
{ ...deviceDescriptor.buildData, buildForDevice: !device.isEmulator },
packageFilePath
{ ...deviceDescriptor.buildData, buildForDevice: !device.isEmulator }
);
};

Expand Down
21 changes: 5 additions & 16 deletions lib/controllers/run-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,6 @@ export class RunController extends EventEmitter implements IRunController {
deviceDescriptors: ILiveSyncDeviceDescriptor[]
): Promise<void> {
const rebuiltInformation: IDictionary<{
packageFilePath: string;
platform: string;
isEmulator: boolean;
}> = {};
Expand Down Expand Up @@ -508,8 +507,6 @@ export class RunController extends EventEmitter implements IRunController {
);

try {
let packageFilePath: string = null;

// Case where we have three devices attached, a change that requires build is found,
// we'll rebuild the app only for the first device, but we should install new package on all three devices.
if (
Expand All @@ -520,25 +517,20 @@ export class RunController extends EventEmitter implements IRunController {
rebuiltInformation[platformData.platformNameLowerCase]
.isEmulator === device.isEmulator)
) {
packageFilePath =
rebuiltInformation[platformData.platformNameLowerCase]
.packageFilePath;
await this.$deviceInstallAppService.installOnDevice(
device,
buildData,
packageFilePath
buildData
);
} else {
const shouldBuild =
prepareResultData.hasNativeChanges ||
buildData.nativePrepare.forceRebuildNativeApp ||
(await this.$buildController.shouldBuild(buildData));
if (shouldBuild) {
packageFilePath = await deviceDescriptor.buildAction();
await deviceDescriptor.buildAction();
rebuiltInformation[platformData.platformNameLowerCase] = {
isEmulator: device.isEmulator,
platform: platformData.platformNameLowerCase,
packageFilePath,
platform: platformData.platformNameLowerCase
};
} else {
await this.$analyticsService.trackEventActionInGoogleAnalytics({
Expand All @@ -550,8 +542,7 @@ export class RunController extends EventEmitter implements IRunController {

await this.$deviceInstallAppService.installOnDeviceIfNeeded(
device,
buildData,
packageFilePath
buildData
);
}

Expand Down Expand Up @@ -713,9 +704,7 @@ export class RunController extends EventEmitter implements IRunController {

await this.$deviceInstallAppService.installOnDevice(
device,
deviceDescriptor.buildData,
rebuiltInformation[platformData.platformNameLowerCase]
.packageFilePath
deviceDescriptor.buildData
);
await platformLiveSyncService.syncAfterInstall(device, watchInfo);
await this.refreshApplication(
Expand Down
2 changes: 2 additions & 0 deletions lib/data/build-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export class BuildData extends PrepareData implements IBuildData {
public emulator?: boolean;
public clean: boolean;
public buildForDevice?: boolean;
public buildFilterDevicesArch?: boolean;
public buildOutputStdio?: string;
public outputPath?: string;
public copyTo?: string;
Expand Down Expand Up @@ -58,6 +59,7 @@ export class AndroidBuildData extends BuildData {
this.keyStoreAliasPassword = data.keyStoreAliasPassword;
this.keyStorePassword = data.keyStorePassword;
this.androidBundle = data.androidBundle || data.aab;
this.buildFilterDevicesArch = !this.androidBundle && data.filterDevicesArch !== false ;
this.gradlePath = data.gradlePath;
this.gradleArgs = data.gradleArgs;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/definitions/android-plugin-migrator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ interface IAndroidBuildOptions {
pluginName: string;
aarOutputDir: string;
tempPluginDirPath: string;
gradlePath?: string;
gradleArgs?: string;
gradlePath?: string;
}

interface IAndroidPluginBuildService {
Expand Down
3 changes: 2 additions & 1 deletion lib/definitions/build.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface IAndroidBuildData
extends IBuildData,
IAndroidSigningData,
IHasAndroidBundle {
buildFilterDevicesArch?: boolean;
gradlePath?: string;
gradleArgs?: string;
}
Expand Down Expand Up @@ -61,7 +62,7 @@ interface IBuildArtifactsService {
platformData: IPlatformData,
buildOutputOptions: IBuildOutputOptions
): Promise<string>;
copyLatestAppPackage(
copyAppPackages(
targetPath: string,
platformData: IPlatformData,
buildOutputOptions: IBuildOutputOptions
Expand Down
6 changes: 2 additions & 4 deletions lib/definitions/run.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ declare global {
interface IDeviceInstallAppService {
installOnDevice(
device: Mobile.IDevice,
buildData: IBuildData,
packageFile?: string
buildData: IBuildData
): Promise<void>;
installOnDeviceIfNeeded(
device: Mobile.IDevice,
buildData: IBuildData,
packageFile?: string
buildData: IBuildData
): Promise<void>;
shouldInstall(
device: Mobile.IDevice,
Expand Down
1 change: 1 addition & 0 deletions lib/helpers/deploy-command-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class DeployCommandHelper {
{
...this.$options.argv,
outputPath,
buildFilterDevicesArch: false,
buildForDevice: !d.isEmulator,
skipWatcher: !this.$options.watch,
nativePrepare: {
Expand Down
1 change: 1 addition & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export class Options {
gradlePath: { type: OptionType.String, hasSensitiveValue: false },
gradleArgs: { type: OptionType.String, hasSensitiveValue: false },
aab: { type: OptionType.Boolean, hasSensitiveValue: false },
filterDevicesArch: { type: OptionType.Boolean, hasSensitiveValue: false },
performance: { type: OptionType.Object, hasSensitiveValue: true },
appleApplicationSpecificPassword: {
type: OptionType.String,
Expand Down
40 changes: 38 additions & 2 deletions lib/services/android-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ import {
import { IInjector } from "../common/definitions/yok";
import { injector } from "../common/yok";
import { INotConfiguredEnvOptions } from "../common/definitions/commands";
import { IProjectChangesInfo } from "../definitions/project-changes";
import { AndroidPrepareData } from "../data/prepare-data";
import { AndroidBuildData } from "../data/build-data";

interface NativeDependency {
name: string;
Expand Down Expand Up @@ -148,6 +151,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
private $androidPluginBuildService: IAndroidPluginBuildService,
private $platformEnvironmentRequirements: IPlatformEnvironmentRequirements,
private $androidResourcesMigrationService: IAndroidResourcesMigrationService,
private $liveSyncProcessDataService: ILiveSyncProcessDataService,
private $devicesService: Mobile.IDevicesService,
private $filesHashService: IFilesHashService,
private $gradleCommandService: IGradleCommandService,
private $gradleBuildService: IGradleBuildService,
Expand Down Expand Up @@ -826,8 +831,39 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
await adb.executeShellCommand(["rm", "-rf", deviceRootPath]);
}

public async checkForChanges(): Promise<void> {
// Nothing android specific to check yet.
public async checkForChanges(
changesInfo: IProjectChangesInfo,
prepareData: AndroidPrepareData,
projectData: IProjectData
): Promise<void> {
//we need to check for abi change in connected device vs last built
const deviceDescriptors = this.$liveSyncProcessDataService.getDeviceDescriptors(
projectData.projectDir
);
const platformData = this.getPlatformData(projectData);
deviceDescriptors.forEach(deviceDescriptor=>{
const buildData = deviceDescriptor.buildData as AndroidBuildData;
if (buildData.buildFilterDevicesArch) {
const outputPath = platformData.getBuildOutputPath(deviceDescriptor.buildData);
const apkOutputPath = path.join(outputPath, prepareData.release ? "release" : "debug");
if (!this.$fs.exists(outputPath)) {
return;
}
// check if we already build this arch
// if not we need to say native has changed
const device = this.$devicesService.getDevicesForPlatform(deviceDescriptor.buildData.platform).filter(d=>d.deviceInfo.identifier === deviceDescriptor.identifier)[0];
const abis = device.deviceInfo.abis.filter(a=>!!a && a.length)[0];

const directoryContent = this.$fs.readDirectory(apkOutputPath);
const regexp = new RegExp(`${abis}.*\.apk`);
const files = _.filter(directoryContent, (entry: string) => {
return regexp.test(entry);
});
if (files.length === 0) {
changesInfo.nativeChanged = true;
}
}
})
}

public getDeploymentTarget(projectData: IProjectData): semver.SemVer {
Expand Down
1 change: 0 additions & 1 deletion lib/services/android/gradle-build-args-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export class GradleBuildArgsService implements IGradleBuildArgsService {
) {
args.push("-PgatherAnalyticsData=true");
}

// allow modifying gradle args from a `before-build-task-args` hook
await this.$hooksService.executeBeforeHooks("build-task-args", {
hookArgs: { args },
Expand Down
14 changes: 13 additions & 1 deletion lib/services/android/gradle-build-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export class GradleBuildService
constructor(
private $childProcess: IChildProcess,
private $gradleBuildArgsService: IGradleBuildArgsService,
private $gradleCommandService: IGradleCommandService
private $gradleCommandService: IGradleCommandService,
private $devicesService: Mobile.IDevicesService
) {
super();
}
Expand All @@ -28,6 +29,17 @@ export class GradleBuildService
const buildTaskArgs = await this.$gradleBuildArgsService.getBuildTaskArgs(
buildData
);
if (buildData.buildFilterDevicesArch) {
let devices = this.$devicesService.getDevicesForPlatform(buildData.platform);
if(buildData.emulator) {
devices = devices.filter(d=>d.isEmulator);
}
const abis = devices.map(d=>d.deviceInfo.abis[0]);
if (abis.length > 0) {
buildTaskArgs.push(`-PabiFilters=${abis.join(',')}`);
}
}
console.log('buildTaskArgs', buildTaskArgs);
const spawnOptions = {
emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME },
throwError: true,
Expand Down
34 changes: 23 additions & 11 deletions lib/services/build-artifacts-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class BuildArtifactsService implements IBuildArtifactsService {
return [];
}

public copyLatestAppPackage(
public copyAppPackages(
targetPath: string,
platformData: IPlatformData,
buildOutputOptions: IBuildOutputOptions
Expand All @@ -85,31 +85,43 @@ export class BuildArtifactsService implements IBuildArtifactsService {
const outputPath =
buildOutputOptions.outputPath ||
platformData.getBuildOutputPath(buildOutputOptions);
const applicationPackage = this.getLatestApplicationPackage(
const applicationPackages = this.getAllAppPackages(
outputPath,
platformData.getValidBuildOutputData(buildOutputOptions)
);
const packageFile = applicationPackage.packageName;

this.$fs.ensureDirectoryExists(path.dirname(targetPath));

let filterRegex: RegExp;
let targetIsDirectory = false;
if (
this.$fs.exists(targetPath) &&
this.$fs.getFsStats(targetPath).isDirectory()
) {
const sourceFileName = path.basename(packageFile);
this.$logger.trace(
`Specified target path: '${targetPath}' is directory. Same filename will be used: '${sourceFileName}'.`
);
targetPath = path.join(targetPath, sourceFileName);
targetIsDirectory = true;
} else if (targetPath.match(/\.(ipa|aab|apk)/)){
if (applicationPackages.length > 1){
filterRegex = new RegExp('universal');
this.$logger.trace(
`Multiple packages were built but only the universal one will be copied if existing'.`
);
}
} else {
targetIsDirectory = true;
}
this.$fs.copyFile(packageFile, targetPath);
this.$logger.info(`Copied file '${packageFile}' to '${targetPath}'.`);
applicationPackages.forEach(pack => {
const targetFilePath = targetIsDirectory ? path.join(targetPath, path.basename(pack.packageName)) : targetPath;
if (!filterRegex || filterRegex.test(pack.packageName)) {
this.$fs.copyFile(pack.packageName, targetFilePath);
this.$logger.info(`Copied file '${pack.packageName}' to '${targetFilePath}'.`);
}
});
}

private getLatestApplicationPackage(
buildOutputPath: string,
validBuildOutputData: IValidBuildOutputData
validBuildOutputData: IValidBuildOutputData,
abis?: string[]
): IApplicationPackage {
let packages = this.getAllAppPackages(
buildOutputPath,
Expand Down
Loading