Skip to content

Commit f7b0c13

Browse files
Merge pull request #856 from NativeScript/vladimirov/fix-android-sdk-param
Pass correct parameters to gradle build
2 parents 7b3d806 + 23bdee0 commit f7b0c13

File tree

5 files changed

+291
-81
lines changed

5 files changed

+291
-81
lines changed

lib/android-tools-info.ts

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
///<reference path=".d.ts"/>
2+
"use strict";
3+
4+
import * as path from "path";
5+
import * as semver from "semver";
6+
7+
export class AndroidToolsInfo implements IAndroidToolsInfo {
8+
private static ANDROID_TARGET_PREFIX = "android";
9+
private static SUPPORTED_TARGETS = ["android-17", "android-18", "android-19", "android-21", "android-22"];
10+
private static MIN_REQUIRED_COMPILE_TARGET = 21;
11+
private static REQUIRED_BUILD_TOOLS_RANGE_PREFIX = ">=22";
12+
private static VERSION_REGEX = /^(\d+\.){2}\d+$/;
13+
private showWarningsAsErrors: boolean;
14+
private toolsInfo: IAndroidToolsInfoData;
15+
private selectedCompileSdk: number;
16+
private installedTargetsCache: string[] = null;
17+
private androidHome = process.env["ANDROID_HOME"];
18+
19+
constructor(private $childProcess: IChildProcess,
20+
private $errors: IErrors,
21+
private $fs: IFileSystem,
22+
private $logger: ILogger,
23+
private $options: IOptions) {}
24+
25+
public getToolsInfo(): IFuture<IAndroidToolsInfoData> {
26+
return ((): IAndroidToolsInfoData => {
27+
if(!this.toolsInfo) {
28+
let infoData: IAndroidToolsInfoData = Object.create(null);
29+
infoData.androidHomeEnvVar = this.androidHome;
30+
infoData.compileSdkVersion = this.getCompileSdk().wait();
31+
infoData.buildToolsVersion = this.getBuildToolsVersion().wait();
32+
infoData.targetSdkVersion = this.getTargetSdk().wait();
33+
infoData.supportLibraryVersion = this.getAndroidSupportLibVersion().wait();
34+
35+
this.toolsInfo = infoData;
36+
}
37+
38+
return this.toolsInfo;
39+
}).future<IAndroidToolsInfoData>()();
40+
}
41+
42+
public validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture<void> {
43+
return (() => {
44+
this.showWarningsAsErrors = options && options.showWarningsAsErrors;
45+
let toolsInfoData = this.getToolsInfo().wait();
46+
if(!toolsInfoData.androidHomeEnvVar || !this.$fs.exists(toolsInfoData.androidHomeEnvVar).wait()) {
47+
this.printMessage("The ANDROID_HOME environment variable is not set or it points to a non-existent directory. You will not be able to perform any build-related operations for Android.",
48+
"To be able to perform Android build-related operations, set the ANDROID_HOME variable to point to the root of your Android SDK installation directory.");
49+
}
50+
51+
if(!toolsInfoData.compileSdkVersion) {
52+
this.printMessage(`Cannot find a compatible Android SDK for compilation. To be able to build for Android, install Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later.`,
53+
"Run `$ android` to manage your Android SDK versions.");
54+
}
55+
56+
if(!toolsInfoData.buildToolsVersion) {
57+
this.printMessage(`You need to have the Android SDK Build-tools installed on your system. You can install any version in the following range: '${this.getBuildToolsRange()}'.`,
58+
'Run "android" from your command-line to install required Android Build Tools.');
59+
}
60+
61+
if(!toolsInfoData.supportLibraryVersion) {
62+
this.printMessage(`You need to have the Android Support Library installed on your system. You can install any version in the following range: ${this.getAppCompatRange().wait() || ">=" + AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET}}.`,
63+
'Run `$ android` to manage the Android Support Library.');
64+
}
65+
66+
if(options && options.validateTargetSdk) {
67+
let targetSdk = toolsInfoData.targetSdkVersion;
68+
let newTarget = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${targetSdk}`;
69+
if(!_.contains(AndroidToolsInfo.SUPPORTED_TARGETS, newTarget)) {
70+
let supportedVersions = AndroidToolsInfo.SUPPORTED_TARGETS.sort();
71+
let minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions));
72+
73+
if(targetSdk && (targetSdk < minSupportedVersion)) {
74+
this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You пкяш target ${minSupportedVersion} or later.`);
75+
} else if(!targetSdk || targetSdk > this.getMaxSupportedVersion()) {
76+
this.$logger.warn(`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`);
77+
}
78+
}
79+
}
80+
}).future<void>()();
81+
}
82+
83+
/**
84+
* Prints messages on the screen. In case the showWarningsAsErrors flag is set to true, warnings are shown, else - errors.
85+
* Uses logger.warn for warnings and errors.failWithoutHelp when erros must be shown.
86+
* In case additional details must be shown as info message, use the second parameter.
87+
* NOTE: The additional information will not be printed when showWarningsAsErrors flag is set.
88+
* @param {string} msg The message that will be shown as warning or error.
89+
* @param {string} additionalMsg The additional message that will be shown as info message.
90+
* @return {void}
91+
*/
92+
private printMessage(msg: string, additionalMsg?: string): void {
93+
if (this.showWarningsAsErrors) {
94+
this.$errors.failWithoutHelp(msg);
95+
} else {
96+
this.$logger.warn(msg);
97+
}
98+
99+
if(additionalMsg) {
100+
this.$logger.info(additionalMsg);
101+
}
102+
}
103+
104+
private getCompileSdk(): IFuture<number> {
105+
return ((): number => {
106+
if(!this.selectedCompileSdk) {
107+
let latestValidAndroidTarget = this.getLatestValidAndroidTarget().wait();
108+
if(latestValidAndroidTarget) {
109+
let integerVersion = this.parseAndroidSdkString(latestValidAndroidTarget);
110+
111+
if(integerVersion && integerVersion >= AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET) {
112+
this.selectedCompileSdk = integerVersion;
113+
}
114+
}
115+
}
116+
117+
return this.selectedCompileSdk;
118+
}).future<number>()();
119+
}
120+
121+
private getTargetSdk(): IFuture<number> {
122+
return ((): number => {
123+
let targetSdk = this.$options.sdk ? parseInt(this.$options.sdk) : this.getCompileSdk().wait();
124+
this.$logger.trace(`Selected targetSdk is: ${targetSdk}`);
125+
return targetSdk;
126+
}).future<number>()();
127+
}
128+
129+
private getMatchingDir(pathToDir: string, versionRange: string): IFuture<string> {
130+
return ((): string => {
131+
let selectedVersion: string;
132+
if(this.$fs.exists(pathToDir).wait()) {
133+
let subDirs = this.$fs.readDirectory(pathToDir).wait()
134+
.filter(buildTools => !!buildTools.match(AndroidToolsInfo.VERSION_REGEX));
135+
this.$logger.trace(`Versions found in ${pathToDir} are ${subDirs.join(", ")}`);
136+
selectedVersion = semver.maxSatisfying(subDirs, versionRange);
137+
}
138+
139+
return selectedVersion;
140+
}).future<string>()();
141+
}
142+
143+
private getBuildToolsRange(): string {
144+
return `${AndroidToolsInfo.REQUIRED_BUILD_TOOLS_RANGE_PREFIX} <=${this.getMaxSupportedVersion()}`;
145+
}
146+
147+
private getBuildToolsVersion(): IFuture<string> {
148+
return ((): string => {
149+
let pathToBuildTools = path.join(this.androidHome, "build-tools");
150+
let buildToolsRange = this.getBuildToolsRange();
151+
152+
return this.getMatchingDir(pathToBuildTools, buildToolsRange).wait();
153+
}).future<string>()();
154+
}
155+
156+
private getAppCompatRange(): IFuture<string> {
157+
return ((): string => {
158+
let compileSdkVersion = this.getCompileSdk().wait();
159+
let requiredAppCompatRange: string;
160+
if(compileSdkVersion) {
161+
requiredAppCompatRange = `>=${compileSdkVersion} <${compileSdkVersion + 1}`;
162+
}
163+
164+
return requiredAppCompatRange;
165+
}).future<string>()();
166+
}
167+
168+
private getAndroidSupportLibVersion(): IFuture<string> {
169+
return ((): string => {
170+
let pathToAppCompat = path.join(this.androidHome, "extras", "android", "m2repository", "com", "android", "support", "appcompat-v7");
171+
let requiredAppCompatRange = this.getAppCompatRange().wait();
172+
let selectedAppCompatVersion = requiredAppCompatRange ? this.getMatchingDir(pathToAppCompat, requiredAppCompatRange).wait() : undefined;
173+
this.$logger.trace(`Selected AppCompat version is: ${selectedAppCompatVersion}`);
174+
return selectedAppCompatVersion;
175+
}).future<string>()();
176+
}
177+
178+
private getLatestValidAndroidTarget(): IFuture<string> {
179+
return (() => {
180+
let installedTargets = this.getInstalledTargets().wait();
181+
return _.findLast(AndroidToolsInfo.SUPPORTED_TARGETS.sort(), supportedTarget => _.contains(installedTargets, supportedTarget));
182+
}).future<string>()();
183+
}
184+
185+
private parseAndroidSdkString(androidSdkString: string): number {
186+
return parseInt(androidSdkString.replace(`${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-`, ""));
187+
}
188+
189+
private getInstalledTargets(): IFuture<string[]> {
190+
return (() => {
191+
if (!this.installedTargetsCache) {
192+
this.installedTargetsCache = [];
193+
let output = this.$childProcess.exec('android list targets').wait();
194+
output.replace(/id: \d+ or "(.+)"/g, (m:string, p1:string) => (this.installedTargetsCache.push(p1), m));
195+
}
196+
return this.installedTargetsCache;
197+
}).future<string[]>()();
198+
}
199+
200+
private getMaxSupportedVersion(): number {
201+
return this.parseAndroidSdkString(_.last(AndroidToolsInfo.SUPPORTED_TARGETS.sort()));
202+
}
203+
}
204+
$injector.register("androidToolsInfo", AndroidToolsInfo);

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,4 @@ $injector.requireCommand("init", "./commands/init");
7272

7373
$injector.require("projectFilesManager", "./services/project-files-manager");
7474
$injector.requireCommand("livesync", "./commands/livesync");
75+
$injector.require("androidToolsInfo", "./android-tools-info");

lib/declarations.ts

+50
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,53 @@ interface IProjectFilesManager {
9090
interface IInitService {
9191
initialize(): IFuture<void>;
9292
}
93+
94+
/**
95+
* Provides access to information about installed Android tools and SDKs versions.
96+
*/
97+
interface IAndroidToolsInfo {
98+
/**
99+
* Provides information about installed Android SDKs, Build Tools, Support Library
100+
* and ANDROID_HOME environement variable.
101+
* @return {IAndroidToolsInfoData} Information about installed Android Tools and SDKs.
102+
*/
103+
getToolsInfo(): IFuture<IAndroidToolsInfoData>;
104+
105+
/**
106+
* Validates the information about required Android tools and SDK versions.
107+
* @param {any} options Defines if the warning messages should treated as error and if the targetSdk value should be validated as well.
108+
* @return {void}
109+
*/
110+
validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture<void>;
111+
}
112+
113+
/**
114+
* Describes information about installed Android tools and SDKs.
115+
*/
116+
interface IAndroidToolsInfoData {
117+
/**
118+
* The value of ANDROID_HOME environment variable.
119+
*/
120+
androidHomeEnvVar: string;
121+
122+
/**
123+
* The latest installed version of Android Build Tools that satisfies CLI's requirements.
124+
*/
125+
buildToolsVersion: string;
126+
127+
/**
128+
* The latest installed version of Android SDK that satisfies CLI's requirements.
129+
*/
130+
compileSdkVersion: number;
131+
132+
/**
133+
* The latest installed version of Android Support Library that satisfies CLI's requirements.
134+
*/
135+
supportLibraryVersion: string;
136+
137+
/**
138+
* The Android targetSdkVersion specified by the user.
139+
* In case it is not specified, compileSdkVersion will be used for targetSdkVersion.
140+
*/
141+
targetSdkVersion: number;
142+
}

0 commit comments

Comments
 (0)