Skip to content
This repository was archived by the owner on Mar 26, 2024. It is now read-only.

Commit 21f5718

Browse files
authored
Merge pull request #3 from microsoft/apkSizeAnalyzer
Added APK size analyzer
2 parents 728b23b + c4bcd53 commit 21f5718

22 files changed

+342
-113
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ dist
33
*.vsix
44
.taskkey
55
**/.taskkey
6+
.DS_Store
7+
**/.DS_Store
8+
test/assets/extracted/
9+

ado-extension.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifestVersion": 1,
33
"id": "android-app-size-diff",
44
"name": "Android app size changes",
5-
"version": "0.0.19",
5+
"version": "0.0.22",
66
"publisher": "PraveenPendyala",
77
"targets": [
88
{
@@ -18,7 +18,7 @@
1818
},
1919
"files": [
2020
{
21-
"path": "dist/src/"
21+
"path": "dist/src"
2222
}
2323
],
2424
"contributions": [
@@ -29,7 +29,7 @@
2929
"ms.vss-distributed-task.tasks"
3030
],
3131
"properties": {
32-
"name": "dist/src/"
32+
"name": "dist/src"
3333
}
3434
}
3535
]

ado-task-test.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trigger:
2+
- master
3+
4+
pool:
5+
vmImage: 'ubuntu-latest'
6+
7+
steps:
8+
- task: android-app-size-diff-task@0
9+
inputs:
10+
baseAppPath: test/assets/test.apk
11+
targetAppPath: test/assets/test.apk
12+
summaryOutputPath: summary.md
13+
displayName: 'Run APK size comparision'
14+
15+
- script: cat summary.md
16+
displayName: 'Print generated summary'

bumpVersion.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const supportedBumps = ["major", "minor", "patch"]
55

66
const nodePackagJsonPath = 'package.json';
77
const adoExtensionJsonPath = 'ado-extension.json';
8-
const adoTaskJsonPath = 'ado-task.json';
8+
const adoTaskJsonPath = 'src/task.json';
99

1010
if (process.argv.length != 3) {
1111
throw "Need exactly one argument!"

downloadAndroidSdk.ts

Lines changed: 0 additions & 51 deletions
This file was deleted.

package-lock.json

Lines changed: 25 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
{
22
"name": "android-app-size-ado",
3-
"version": "0.0.19",
3+
"version": "0.0.22",
44
"description": "Azure DevOps task to measure the size in Android app size by looking at 2 given APKs and AABs",
55
"main": "index.js",
66
"scripts": {
7-
"postinstall": "npm run download-sdk-all",
87
"build": "npm run build-ts && npm run copy-files",
98
"adoTask": "npm run build && node dist/src/ado-index.js",
109
"build-ts": "tsc",
1110
"copy-files": "ts-node copyFiles.ts",
1211
"test": "npm run build && mocha dist/test/**/*.js",
1312
"bundle": "npm run build && npm run bundle-ado-task",
1413
"bundle-ado-task": "tfx extension create --manifest-globs ado-extension.json",
15-
"download-sdk-windows": "ts-node downloadAndroidSdk.ts Windows_NT",
16-
"download-sdk-linux": "ts-node downloadAndroidSdk.ts Linux",
17-
"download-sdk-mac": "ts-node downloadAndroidSdk.ts Darwin",
18-
"download-sdk-all": "npm run download-sdk-windows && npm run download-sdk-linux && npm run download-sdk-mac",
1914
"bump-major": "ts-node bumpVersion.ts major",
2015
"bump-minor": "ts-node bumpVersion.ts minor",
2116
"bump-patch": "ts-node bumpVersion.ts patch"
@@ -37,7 +32,11 @@
3732
},
3833
"homepage": "https://github.com/microsoft/android-app-size-ci#readme",
3934
"dependencies": {
40-
"azure-pipelines-task-lib": "^2.9.3"
35+
"@types/adm-zip": "^0.4.32",
36+
"@types/line-reader": "0.0.28",
37+
"adm-zip": "^0.4.13",
38+
"azure-pipelines-task-lib": "^2.9.3",
39+
"line-reader": "^0.4.0"
4140
},
4241
"devDependencies": {
4342
"@types/download": "^6.2.4",
@@ -51,4 +50,4 @@
5150
"ts-node": "^8.5.4",
5251
"typescript": "^3.7.4"
5352
}
54-
}
53+
}

src/adoTask/adoTaskRunner.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
1-
import * as tl from 'azure-pipelines-task-lib/task';
2-
import * as shell from "shelljs";
3-
import * as os from 'os';
4-
import * as path from 'path';
1+
import * as adoTask from 'azure-pipelines-task-lib/task';
2+
import * as util from 'util';
3+
import ApkAnalyzer from '../apkAnalyzer/ApkAnalyzer';
4+
import ComparisionReportGenerator from '../apkAnalyzer/ComparisionReportGenerator';
5+
import { MarkdownReporter } from '../apkAnalyzer/reporter/MarkdownReporter';
56

67
export default class AdoTaskRunner {
78

89
public async run() {
9-
const platform = os.type();
10+
try {
11+
const baseAppPath: string | undefined = adoTask.getInput('baseAppPath', true);
12+
const targetAppPath: string | undefined = adoTask.getInput('targetAppPath', true);
13+
const summaryOutputPath: string | undefined = adoTask.getInput('summaryOutputPath', false);
1014

11-
const apkanalyzerPath = path.join('dist', 'src', 'bin', platform, 'tools', 'bin', 'apkanalyzer');
12-
const testApkPath = path.join('test', 'assets', 'test.apk');
15+
if (util.isUndefined(baseAppPath)
16+
|| util.isUndefined(targetAppPath)
17+
|| util.isUndefined(summaryOutputPath)) {
18+
throw 'App paths not supplied!'
19+
}
1320

14-
shell.exec(apkanalyzerPath + ' apk download-size ' + testApkPath);
21+
const apkAnalyzer = new ApkAnalyzer();
22+
const markdownReportor = new MarkdownReporter();
23+
const compareReportGenerator = new ComparisionReportGenerator(
24+
apkAnalyzer, markdownReportor);
1525

16-
try {
17-
const inputString: string | undefined = tl.getInput('baseAppPath', true);
18-
if (inputString == 'bad') {
19-
tl.setResult(tl.TaskResult.Failed, 'Bad input was given');
20-
return;
21-
}
22-
console.log('Hello', inputString);
26+
console.log(await compareReportGenerator.generateComparisionReport(
27+
baseAppPath,
28+
targetAppPath,
29+
summaryOutputPath,
30+
'Base APK',
31+
'Target APK',
32+
['apkSize', 'installSize', 'dexFiles', 'arscFile']
33+
));
2334
}
2435
catch (err) {
25-
tl.setResult(tl.TaskResult.Failed, err.message);
36+
adoTask.setResult(adoTask.TaskResult.Failed, err.message);
2637
}
2738
}
2839

src/apkAnalyzer/ApkAnalyzer.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import AdmZip from 'adm-zip';
2+
import * as path from 'path'
3+
import * as util from 'util';
4+
import MetaMfParser from './MetaMfParser';
5+
import ApkSizeSummary from './model/ApkSizeSummary';
6+
import { FilesSizeCalculator } from './FilesSizeCalculator';
7+
8+
export default class ApkAnalyzer {
9+
10+
public async analyse(apkPath: string, apkLabel: string, workingDir?: string) : Promise<ApkSizeSummary> {
11+
if (util.isUndefined(workingDir)) {
12+
workingDir = path.join(path.dirname(apkPath), 'extracted');
13+
}
14+
15+
// Extract the apk file
16+
const apkFile = new AdmZip(apkPath);
17+
console.log('Extracting APK file to ' + workingDir);
18+
apkFile.extractAllTo(workingDir, true);
19+
20+
// Parse the IMF file
21+
const mfParser = new MetaMfParser(workingDir);
22+
await mfParser.parse();
23+
24+
// Build Apk size summary
25+
const fileSizeCalc = new FilesSizeCalculator();
26+
const sizeSummary = new ApkSizeSummary();
27+
28+
sizeSummary.apkLabel = apkLabel;
29+
sizeSummary.sizeMetrics['apkSize'] = fileSizeCalc.getFileSize(apkPath);
30+
sizeSummary.sizeMetrics['arscFile'] = fileSizeCalc.getFilesSize(mfParser.getFiles('.arsc'));
31+
sizeSummary.sizeMetrics['dexFiles'] = fileSizeCalc.getFilesSize(mfParser.getFiles('.dex'));
32+
sizeSummary.sizeMetrics['nativeLibs'] = fileSizeCalc.getFilesSize(mfParser.getFiles('.so'));
33+
sizeSummary.sizeMetrics['installSize'] = sizeSummary.sizeMetrics['apkSize'] + sizeSummary.sizeMetrics['dexFiles'];
34+
35+
return sizeSummary;
36+
}
37+
38+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import ApkAnalyzer from "./ApkAnalyzer";
2+
import ComparisionReport from "./model/ComparisionReport";
3+
import IReportor from './reporter/IReporter';
4+
5+
/**
6+
* Generates a report comparsing two different APks
7+
*/
8+
9+
export default class ComparisionReportGenerator {
10+
apkAnalyser: ApkAnalyzer;
11+
reporter: IReportor;
12+
13+
public constructor(apkAnalyser : ApkAnalyzer, reporter: IReportor) {
14+
this.apkAnalyser = apkAnalyser;
15+
this.reporter = reporter;
16+
}
17+
18+
public async generateComparisionReport(
19+
baseApkPath: string,
20+
targetApkPath: string,
21+
reportOutputPath: string,
22+
baseApkLabel: string = 'Base APK',
23+
targetApkLabel: string = 'Target APK',
24+
includeMetrics: Array<string> = ['apkSize', 'installSize', 'dexFiles']) : Promise<ComparisionReport> {
25+
const baseSummary = await this.apkAnalyser.analyse(baseApkPath, baseApkLabel);
26+
const targetSummary = await this.apkAnalyser.analyse(targetApkPath, targetApkLabel);
27+
28+
const comparisionReport = new ComparisionReport();
29+
comparisionReport.baseApkLabel = baseSummary.apkLabel;
30+
comparisionReport.targetApkLabel = targetSummary.apkLabel;
31+
32+
includeMetrics.forEach(metric => {
33+
const baseMetric = baseSummary.sizeMetrics[metric];
34+
const targetMetric = baseSummary.sizeMetrics[metric];
35+
const difference = targetMetric - baseMetric;
36+
37+
comparisionReport.comparisionMetrics.push({
38+
metricName: metric,
39+
baseValue: baseMetric,
40+
targetValue: targetMetric,
41+
difference: difference
42+
});
43+
});
44+
45+
this.reporter.writeReport(comparisionReport, reportOutputPath);
46+
47+
return comparisionReport;
48+
}
49+
50+
}

0 commit comments

Comments
 (0)