Skip to content

Commit 065325f

Browse files
Merge pull request #54 from NativeScript/kddimitrov/refactor-android-tools-info
Kddimitrov/refactor android tools info
2 parents 6ba14a3 + a3b35d2 commit 065325f

16 files changed

+288
-129
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,5 @@ test-reports.xml
6464
*.js
6565
*.js.map
6666
/lib/.d.ts
67-
67+
.d.ts
6868
!/*.js

Gruntfile.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ module.exports = function (grunt) {
2929
src: ["lib/**/*.ts", "test/**/*.ts", "typings/**/*.ts", "!**/*.d.ts"]
3030
},
3131
options: {
32-
configuration: grunt.file.readJSON("./tslint.json")
32+
configuration: grunt.file.readJSON("./tslint.json"),
33+
project: "tsconfig.json"
3334
}
3435
}
3536
},

lib/android-tools-info.ts

+97-51
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,68 @@ import { Helpers } from './helpers';
66
import { EOL } from "os";
77
import * as semver from "semver";
88
import * as path from "path";
9+
import * as _ from "lodash";
910

1011
export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
11-
private static ANDROID_TARGET_PREFIX = "android";
12-
private static SUPPORTED_TARGETS = [
13-
"android-17",
14-
"android-18",
15-
"android-19",
16-
"android-21",
17-
"android-22",
18-
"android-23",
19-
"android-24",
20-
"android-25",
21-
"android-26",
22-
"android-27",
23-
"android-28",
24-
];
12+
public readonly ANDROID_TARGET_PREFIX = "android";
13+
private getSupportedTargets(projectDir: string) {
14+
const runtimeVersion = this.getRuntimeVersion({projectDir});
15+
let baseTargets = [
16+
"android-17",
17+
"android-18",
18+
"android-19",
19+
"android-21",
20+
"android-22",
21+
"android-23",
22+
"android-24",
23+
"android-25",
24+
"android-26",
25+
"android-27",
26+
"android-28",
27+
"android-29"
28+
];
29+
30+
if (runtimeVersion && semver.lt(semver.coerce(runtimeVersion), "6.1.0")) {
31+
baseTargets.sort();
32+
const indexOfSdk29 = baseTargets.indexOf("android-29");
33+
baseTargets = baseTargets.slice(0, indexOfSdk29);
34+
}
35+
36+
return baseTargets;
37+
}
2538
private static MIN_REQUIRED_COMPILE_TARGET = 28;
2639
private static REQUIRED_BUILD_TOOLS_RANGE_PREFIX = ">=23";
2740
private static VERSION_REGEX = /((\d+\.){2}\d+)/;
2841
private static MIN_JAVA_VERSION = "1.8.0";
2942

3043
private toolsInfo: NativeScriptDoctor.IAndroidToolsInfoData;
31-
private androidHome = process.env["ANDROID_HOME"];
44+
public get androidHome(): string {
45+
return process.env["ANDROID_HOME"];
46+
}
3247
private pathToEmulatorExecutable: string;
3348

3449
constructor(private childProcess: ChildProcess,
3550
private fs: FileSystem,
3651
private hostInfo: HostInfo,
3752
private helpers: Helpers) { }
3853

39-
public getToolsInfo(): NativeScriptDoctor.IAndroidToolsInfoData {
54+
public getToolsInfo(config: Partial<NativeScriptDoctor.IProjectDir> = {}): NativeScriptDoctor.IAndroidToolsInfoData {
4055
if (!this.toolsInfo) {
4156
const infoData: NativeScriptDoctor.IAndroidToolsInfoData = Object.create(null);
4257
infoData.androidHomeEnvVar = this.androidHome;
43-
infoData.compileSdkVersion = this.getCompileSdk();
44-
infoData.buildToolsVersion = this.getBuildToolsVersion();
58+
infoData.installedTargets = this.getInstalledTargets();
59+
infoData.compileSdkVersion = this.getCompileSdk(infoData.installedTargets, config.projectDir);
60+
infoData.buildToolsVersion = this.getBuildToolsVersion(config.projectDir);
4561

4662
this.toolsInfo = infoData;
4763
}
4864

4965
return this.toolsInfo;
5066
}
5167

52-
public validateInfo(): NativeScriptDoctor.IWarning[] {
68+
public validateInfo(config: Partial<NativeScriptDoctor.IProjectDir> = {}): NativeScriptDoctor.IWarning[] {
5369
const errors: NativeScriptDoctor.IWarning[] = [];
54-
const toolsInfoData = this.getToolsInfo();
70+
const toolsInfoData = this.getToolsInfo(config);
5571
const isAndroidHomeValid = this.isAndroidHomeValid();
5672
if (!toolsInfoData.compileSdkVersion) {
5773
errors.push({
@@ -62,7 +78,7 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
6278
}
6379

6480
if (!toolsInfoData.buildToolsVersion) {
65-
const buildToolsRange = this.getBuildToolsRange();
81+
const buildToolsRange = this.getBuildToolsRange(config.projectDir);
6682
const versionRangeMatches = buildToolsRange.match(/^.*?([\d\.]+)\s+.*?([\d\.]+)$/);
6783
let message = `You can install any version in the following range: '${buildToolsRange}'.`;
6884

@@ -105,7 +121,7 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
105121
if (semver.lt(installedJavaCompilerSemverVersion, AndroidToolsInfo.MIN_JAVA_VERSION)) {
106122
warning = `Javac version ${installedJavaCompilerVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`;
107123
} else {
108-
runtimeVersion = this.getRealRuntimeVersion(runtimeVersion || this.getAndroidRuntimeVersionFromProjectDir(projectDir));
124+
runtimeVersion = this.getRuntimeVersion({runtimeVersion, projectDir});
109125
if (runtimeVersion) {
110126
// get the item from the dictionary that corresponds to our current Javac version:
111127
let runtimeMinVersion: string = null;
@@ -145,7 +161,7 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
145161

146162
public async getPathToAdbFromAndroidHome(): Promise<string> {
147163
if (this.androidHome) {
148-
let pathToAdb = path.join(this.androidHome, "platform-tools", "adb");
164+
const pathToAdb = path.join(this.androidHome, "platform-tools", "adb");
149165
try {
150166
await this.childProcess.execFile(pathToAdb, ["help"]);
151167
return pathToAdb;
@@ -179,6 +195,44 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
179195
return errors;
180196
}
181197

198+
public validateMinSupportedTargetSdk({targetSdk, projectDir}: NativeScriptDoctor.ITargetValidationOptions): NativeScriptDoctor.IWarning[] {
199+
const errors: NativeScriptDoctor.IWarning[] = [];
200+
const newTarget = `${this.ANDROID_TARGET_PREFIX}-${targetSdk}`;
201+
const supportedTargets = this.getSupportedTargets(projectDir);
202+
const targetSupported = _.includes(supportedTargets, newTarget);
203+
204+
if (!_.includes(supportedTargets, newTarget)) {
205+
const supportedVersions = supportedTargets.sort();
206+
const minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions));
207+
208+
if (!targetSupported && targetSdk && (targetSdk < minSupportedVersion)) {
209+
errors.push({
210+
warning:`The selected Android target SDK ${newTarget} is not supported. You must target ${minSupportedVersion} or later.`,
211+
additionalInformation: "",
212+
platforms: [Constants.ANDROID_PLATFORM_NAME]
213+
});
214+
}
215+
}
216+
217+
return errors;
218+
}
219+
220+
public validataMaxSupportedTargetSdk({targetSdk, projectDir}: NativeScriptDoctor.ITargetValidationOptions): NativeScriptDoctor.IWarning[] {
221+
const errors: NativeScriptDoctor.IWarning[] = [];
222+
const newTarget = `${this.ANDROID_TARGET_PREFIX}-${targetSdk}`;
223+
const targetSupported = _.includes(this.getSupportedTargets(projectDir), newTarget);
224+
225+
if (!targetSupported && !targetSdk || targetSdk > this.getMaxSupportedVersion(projectDir)) {
226+
errors.push({
227+
warning:`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`,
228+
additionalInformation: "",
229+
platforms: [Constants.ANDROID_PLATFORM_NAME]
230+
});
231+
}
232+
233+
return errors;
234+
}
235+
182236
public getPathToEmulatorExecutable(): string {
183237
if (!this.pathToEmulatorExecutable) {
184238
const emulatorExecutableName = "emulator";
@@ -222,10 +276,10 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
222276
return sdkManagementToolPath;
223277
}
224278

225-
private getCompileSdk(): number {
226-
let latestValidAndroidTarget = this.getLatestValidAndroidTarget();
279+
private getCompileSdk(installedTargets: string[], projectDir: string): number {
280+
const latestValidAndroidTarget = this.getLatestValidAndroidTarget(installedTargets, projectDir);
227281
if (latestValidAndroidTarget) {
228-
let integerVersion = this.parseAndroidSdkString(latestValidAndroidTarget);
282+
const integerVersion = this.parseAndroidSdkString(latestValidAndroidTarget);
229283

230284
if (integerVersion && integerVersion >= AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET) {
231285
return integerVersion;
@@ -236,11 +290,11 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
236290
private getMatchingDir(pathToDir: string, versionRange: string): string {
237291
let selectedVersion: string;
238292
if (this.fs.exists(pathToDir)) {
239-
let subDirs = this.fs.readDirectory(pathToDir);
293+
const subDirs = this.fs.readDirectory(pathToDir);
240294

241-
let subDirsVersions = subDirs
295+
const subDirsVersions = subDirs
242296
.map(dirName => {
243-
let dirNameGroups = dirName.match(AndroidToolsInfo.VERSION_REGEX);
297+
const dirNameGroups = dirName.match(AndroidToolsInfo.VERSION_REGEX);
244298
if (dirNameGroups) {
245299
return dirNameGroups[1];
246300
}
@@ -249,7 +303,7 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
249303
})
250304
.filter(dirName => !!dirName);
251305

252-
let version = semver.maxSatisfying(subDirsVersions, versionRange);
306+
const version = semver.maxSatisfying(subDirsVersions, versionRange);
253307
if (version) {
254308
selectedVersion = subDirs.find(dir => dir.indexOf(version) !== -1);
255309
}
@@ -258,37 +312,27 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
258312
return selectedVersion;
259313
}
260314

261-
private getBuildToolsRange(): string {
262-
return `${AndroidToolsInfo.REQUIRED_BUILD_TOOLS_RANGE_PREFIX} <=${this.getMaxSupportedVersion()}`;
315+
private getBuildToolsRange(projectDir: string): string {
316+
return `${AndroidToolsInfo.REQUIRED_BUILD_TOOLS_RANGE_PREFIX} <=${this.getMaxSupportedVersion(projectDir)}`;
263317
}
264318

265-
private getBuildToolsVersion(): string {
319+
private getBuildToolsVersion(projectDir: string): string {
266320
let buildToolsVersion: string;
267321
if (this.androidHome) {
268-
let pathToBuildTools = path.join(this.androidHome, "build-tools");
269-
let buildToolsRange = this.getBuildToolsRange();
322+
const pathToBuildTools = path.join(this.androidHome, "build-tools");
323+
const buildToolsRange = this.getBuildToolsRange(projectDir);
270324
buildToolsVersion = this.getMatchingDir(pathToBuildTools, buildToolsRange);
271325
}
272326

273327
return buildToolsVersion;
274328
}
275329

276-
private getLatestValidAndroidTarget(): string {
277-
const installedTargets = this.getInstalledTargets();
278-
let latestValidAndroidTarget: string;
279-
const sortedAndroidToolsInfo = AndroidToolsInfo.SUPPORTED_TARGETS.sort();
280-
281-
sortedAndroidToolsInfo.forEach(s => {
282-
if (installedTargets.indexOf(s) >= 0) {
283-
latestValidAndroidTarget = s;
284-
}
285-
});
286-
287-
return latestValidAndroidTarget;
330+
private getLatestValidAndroidTarget(installedTargets: string[], projectDir: string): string {
331+
return _.findLast(this.getSupportedTargets(projectDir).sort(), supportedTarget => _.includes(installedTargets, supportedTarget));
288332
}
289333

290334
private parseAndroidSdkString(androidSdkString: string): number {
291-
return parseInt(androidSdkString.replace(`${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-`, ""));
335+
return parseInt(androidSdkString.replace(`${this.ANDROID_TARGET_PREFIX}-`, ""));
292336
}
293337

294338
private getInstalledTargets(): string[] {
@@ -304,8 +348,9 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
304348
}
305349
}
306350

307-
private getMaxSupportedVersion(): number {
308-
return this.parseAndroidSdkString(AndroidToolsInfo.SUPPORTED_TARGETS.sort()[AndroidToolsInfo.SUPPORTED_TARGETS.length - 1]);
351+
private getMaxSupportedVersion(projectDir: string): number {
352+
const supportedTargets = this.getSupportedTargets(projectDir);
353+
return this.parseAndroidSdkString(supportedTargets.sort()[supportedTargets.length - 1]);
309354
}
310355

311356
private getSystemRequirementsLink(): string {
@@ -331,7 +376,8 @@ export class AndroidToolsInfo implements NativeScriptDoctor.IAndroidToolsInfo {
331376
return runtimeVersion;
332377
}
333378

334-
private getRealRuntimeVersion(runtimeVersion: string): string {
379+
private getRuntimeVersion({ runtimeVersion, projectDir } : { runtimeVersion?: string, projectDir?: string}): string {
380+
runtimeVersion = runtimeVersion || this.getAndroidRuntimeVersionFromProjectDir(projectDir);
335381
if (runtimeVersion) {
336382
// Check if the version is not "next" or "rc", i.e. tag from npm
337383
if (!semver.valid(runtimeVersion)) {

lib/doctor.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export class Doctor implements NativeScriptDoctor.IDoctor {
8787
platforms: [Constants.ANDROID_PLATFORM_NAME]
8888
}),
8989
this.processValidationErrors({
90-
warnings: this.androidToolsInfo.validateInfo(),
90+
warnings: this.androidToolsInfo.validateInfo({projectDir}),
9191
infoMessage: "A compatible Android SDK for compilation is found.",
9292
platforms: [Constants.ANDROID_PLATFORM_NAME]
9393
}),
@@ -149,7 +149,7 @@ export class Doctor implements NativeScriptDoctor.IDoctor {
149149
);
150150

151151
if (sysInfoData.xcodeVer && sysInfoData.cocoaPodsVer) {
152-
let isCocoaPodsWorkingCorrectly = await this.sysInfo.isCocoaPodsWorkingCorrectly();
152+
const isCocoaPodsWorkingCorrectly = await this.sysInfo.isCocoaPodsWorkingCorrectly();
153153
result = result.concat(
154154
this.processSysInfoItem({
155155
item: isCocoaPodsWorkingCorrectly,

lib/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export class Helpers {
55

66
public getPropertyName(method: Function): string {
77
if (method) {
8-
let match = method.toString().match(/(?:return\s+?.*\.(.+);)|(?:=>\s*?.*\.(.+)\b)/);
8+
const match = method.toString().match(/(?:return\s+?.*\.(.+);)|(?:=>\s*?.*\.(.+)\b)/);
99
if (match) {
1010
return (match[1] || match[2]).trim();
1111
}

lib/local-build-requirements/android-local-build-requirements.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export class AndroidLocalBuildRequirements {
33
private sysInfo: NativeScriptDoctor.ISysInfo) { }
44

55
public async checkRequirements(projectDir?: string, runtimeVersion?: string): Promise<boolean> {
6-
const androidToolsInfo = await this.androidToolsInfo.validateInfo();
6+
const androidToolsInfo = await this.androidToolsInfo.validateInfo({projectDir});
77
const javacVersion = await this.sysInfo.getJavaCompilerVersion();
88
const isJavacVersionInvalid = !javacVersion || (await this.androidToolsInfo.validateJavacVersion(javacVersion, projectDir, runtimeVersion)).length;
99
if (androidToolsInfo.length ||

lib/sys-info.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
265265
const xcodeProjectDir = path.join(tempDirectory, "cocoapods");
266266

267267
try {
268-
let spawnResult = await this.childProcess.spawnFromEvent("pod", ["install"], "exit", { spawnOptions: { cwd: xcodeProjectDir } });
268+
const spawnResult = await this.childProcess.spawnFromEvent("pod", ["install"], "exit", { spawnOptions: { cwd: xcodeProjectDir } });
269269
if (spawnResult.exitCode) {
270270
return false;
271271
} else {
@@ -333,7 +333,7 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
333333

334334
public isCocoaPodsUpdateRequired(): Promise<boolean> {
335335
return this.getValueForProperty(() => this.isCocoaPodsUpdateRequiredCache, async (): Promise<boolean> => {
336-
let xcprojInfo = await this.getXcprojInfo();
336+
const xcprojInfo = await this.getXcprojInfo();
337337
if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) {
338338
return true;
339339
} else {

lib/wrappers/file-system.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ const access = util.promisify(fs.access);
77
const mkdir = util.promisify(fs.mkdir);
88

99
export class FileSystem {
10-
public exists(path: string): boolean {
11-
return fs.existsSync(path);
10+
public exists(filePath: string): boolean {
11+
return fs.existsSync(filePath);
1212
}
1313

1414
public extractZip(pathToZip: string, outputDir: string): Promise<void> {
@@ -25,9 +25,9 @@ export class FileSystem {
2525
}
2626

2727
zipFile.openReadStream(entry, (openStreamError, stream) => {
28-
if(openStreamError) {
28+
if (openStreamError) {
2929
return reject(openStreamError);
30-
};
30+
}
3131

3232
const filePath = `${outputDir}/${fn}`;
3333

@@ -50,12 +50,12 @@ export class FileSystem {
5050
});
5151
}
5252

53-
public readDirectory(path: string): string[] {
54-
return fs.readdirSync(path);
53+
public readDirectory(directoryPath: string): string[] {
54+
return fs.readdirSync(directoryPath);
5555
}
5656

57-
public readJson<T>(path: string, options?: { encoding?: null; flag?: string; }): T {
58-
const content = fs.readFileSync(path, options);
57+
public readJson<T>(filePath: string, options?: { encoding?: null; flag?: string; }): T {
58+
const content = fs.readFileSync(filePath, options);
5959
return JSON.parse(content.toString());
6060
}
6161
}

0 commit comments

Comments
 (0)