diff --git a/CHANGELOG.md b/CHANGELOG.md
index be26c03..8aebe72 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@ Change Log
Version 1.2.0 *(In Development)*
--------------------------------
+ * New: SDK Tools will be updated if version is older than defined with sdkManager.minSdkToolsVersion
+ in build.gradle:
+ sdkManager {
+ minSdkToolsVersion '24.3.4'
+ }
* New: Support for the 1.2.x Android plugin.
* New: Download r24.2 Android SDK.
diff --git a/src/main/groovy/com/jakewharton/sdkmanager/SdkManagerExtension.groovy b/src/main/groovy/com/jakewharton/sdkmanager/SdkManagerExtension.groovy
index 7ca8f3b..6f95404 100644
--- a/src/main/groovy/com/jakewharton/sdkmanager/SdkManagerExtension.groovy
+++ b/src/main/groovy/com/jakewharton/sdkmanager/SdkManagerExtension.groovy
@@ -3,4 +3,5 @@ package com.jakewharton.sdkmanager;
class SdkManagerExtension {
String emulatorVersion
String emulatorArchitecture
+ String minSdkToolsVersion
}
diff --git a/src/main/groovy/com/jakewharton/sdkmanager/internal/AndroidCommand.groovy b/src/main/groovy/com/jakewharton/sdkmanager/internal/AndroidCommand.groovy
index db5b263..dda0553 100644
--- a/src/main/groovy/com/jakewharton/sdkmanager/internal/AndroidCommand.groovy
+++ b/src/main/groovy/com/jakewharton/sdkmanager/internal/AndroidCommand.groovy
@@ -66,7 +66,7 @@ interface AndroidCommand {
def result = ''
output.split('----------').each {
- if (it.contains(filter)) {
+ if (it.contains("\"$filter\"")) {
result += it
}
}
diff --git a/src/main/groovy/com/jakewharton/sdkmanager/internal/PackageResolver.groovy b/src/main/groovy/com/jakewharton/sdkmanager/internal/PackageResolver.groovy
index 9d60eb3..2a1d592 100644
--- a/src/main/groovy/com/jakewharton/sdkmanager/internal/PackageResolver.groovy
+++ b/src/main/groovy/com/jakewharton/sdkmanager/internal/PackageResolver.groovy
@@ -17,6 +17,7 @@ import static com.android.SdkConstants.FD_PLATFORMS
import static com.android.SdkConstants.FD_ADDONS
import static com.android.SdkConstants.FD_PLATFORM_TOOLS
import static com.android.SdkConstants.FD_SYSTEM_IMAGES
+import static com.android.SdkConstants.FD_TOOLS
class PackageResolver {
static void resolve(Project project, File sdk) {
@@ -59,6 +60,7 @@ class PackageResolver {
}
def resolve() {
+ resolveSdkTools()
resolveBuildTools()
resolvePlatformTools()
resolveCompileVersion()
@@ -67,6 +69,56 @@ class PackageResolver {
resolveEmulator()
}
+ def resolveSdkTools() {
+ def minSdkToolsVersion = project.sdkManager.minSdkToolsVersion
+ if (minSdkToolsVersion == null) {
+ log.debug 'No minSdkToolsVersion defined'
+ return
+ }
+ log.debug "Found minSdkToolsVersion: $minSdkToolsVersion"
+
+ def sdkToolsDir = new File(sdk, FD_TOOLS)
+ def sdkToolsVersion = getPackageRevision(sdkToolsDir)
+ log.debug "Found sdkToolsVersion: $sdkToolsVersion"
+
+ def minSdkToolsRevision = FullRevision.parseRevision(minSdkToolsVersion)
+ def sdkToolsRevision = FullRevision.parseRevision(sdkToolsVersion)
+ def needsDownload = sdkToolsRevision < minSdkToolsRevision
+
+ if (!needsDownload) {
+ log.debug "SDK tools are up to date."
+ return
+ }
+
+ def sdkToolsPackage = "tools"
+ def currentSdkToolsInfo = androidCommand.list sdkToolsPackage
+ if (currentSdkToolsInfo == null || currentSdkToolsInfo.isEmpty()) {
+ throw new StopExecutionException('Could not get the current SDK tools revision.')
+ }
+
+ def matcher = Pattern.compile("revision\\ (.+)").matcher(currentSdkToolsInfo)
+ if (!matcher.find()) {
+ throw new StopExecutionException("Could not find the current SDK tools revision." +
+ " currentSdkToolsInfo: $currentSdkToolsInfo")
+ }
+
+ def currentSdkToolsVersion = matcher.group(1)
+ log.debug "currentSdkToolsVersion: $currentSdkToolsVersion"
+
+ def currentSdkToolsRevision = FullRevision.parseRevision(currentSdkToolsVersion)
+ if (currentSdkToolsRevision < minSdkToolsRevision) {
+ throw new StopExecutionException("Currently available SDK tools version($currentSdkToolsVersion)" +
+ " is smaller than defined in minSdkToolsVersion: $minSdkToolsVersion")
+ }
+
+ log.lifecycle "SDK tools $sdkToolsVersion outdated. Downloading update..."
+
+ def code = androidCommand.update sdkToolsPackage
+ if (code != 0) {
+ throw new StopExecutionException("SDK tools download failed with code $code.")
+ }
+ }
+
def resolveBuildTools() {
def buildToolsRevision = project.android.buildToolsRevision
log.debug "Build tools version: $buildToolsRevision"
@@ -223,21 +275,7 @@ class PackageResolver {
needsDownload = true
log.lifecycle "Emulator $emulatorVersion $emulatorArchitecture missing. Downloading..."
} else {
- def emulatorPropertiesFile = new File(emulatorDir, 'source.properties')
- if (!emulatorPropertiesFile.canRead()) {
- emulatorPropertiesFile = new File(alternativeEmulatorDir, 'source.properties')
- if (!emulatorPropertiesFile.canRead()) {
- throw new StopExecutionException('Could not read ' + emulatorPropertiesFile.absolutePath)
- }
- }
-
- def emulatorProperties = new Properties()
- emulatorProperties.load(new FileInputStream(emulatorPropertiesFile))
- def emulatorRevision = emulatorProperties.getProperty('Pkg.Revision')
- if (emulatorRevision == null) {
- throw new StopExecutionException('Could not get the installed emulator revision for ' +
- emulatorPackage)
- }
+ def emulatorRevision = getPackageRevision(emulatorDir, alternativeEmulatorDir)
def currentEmulatorInfo = androidCommand.list emulatorPackage
if (currentEmulatorInfo == null || currentEmulatorInfo.isEmpty()) {
@@ -266,6 +304,24 @@ class PackageResolver {
}
}
+ def getPackageRevision(File[] packageDirs) {
+ def propertiesFileName = 'source.properties'
+ def propertiesFile = packageDirs.collect { new File(it, propertiesFileName) }.find { it.canRead() }
+ if (propertiesFile == null) {
+ throw new StopExecutionException("Could not read '$propertiesFileName' from: $packageDirs")
+ }
+
+ def properties = new Properties()
+ properties.load(new FileInputStream(propertiesFile))
+ def revision = properties.getProperty('Pkg.Revision')
+ if (revision == null) {
+ throw new StopExecutionException("Could not read the revision from: ${propertiesFile.absolutePath}")
+ }
+
+ log.debug "Found revision for package ${propertiesFile.absolutePath}: $revision"
+ return revision
+ }
+
def findDependenciesWithGroup(String group) {
def deps = []
for (Configuration configuration : project.configurations) {
diff --git a/src/test/fixtures/outdated-sdk-tools/.android-sdk/tools/source.properties b/src/test/fixtures/outdated-sdk-tools/.android-sdk/tools/source.properties
new file mode 100644
index 0000000..2b262bc
--- /dev/null
+++ b/src/test/fixtures/outdated-sdk-tools/.android-sdk/tools/source.properties
@@ -0,0 +1,8 @@
+### Android Tool: Source of this archive.
+#Thu Sep 24 18:29:52 EEST 2015
+Archive.HostOs=macosx
+Pkg.License=To get started with the Android SDK, you must agree to the following terms and conditions.\n
+Pkg.LicenseRef=android-sdk-license
+Pkg.Revision=22.6
+Pkg.SourceUrl=https\://dl-ssl.google.com/android/repository/repository-10.xml
+Platform.MinPlatformToolsRev=20
diff --git a/src/test/fixtures/outdated-sdk-tools/project/src/main/AndroidManifest.xml b/src/test/fixtures/outdated-sdk-tools/project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ef0e03b
--- /dev/null
+++ b/src/test/fixtures/outdated-sdk-tools/project/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/test/fixtures/up-to-date-sdk-tools/.android-sdk/tools/source.properties b/src/test/fixtures/up-to-date-sdk-tools/.android-sdk/tools/source.properties
new file mode 100644
index 0000000..d7bf894
--- /dev/null
+++ b/src/test/fixtures/up-to-date-sdk-tools/.android-sdk/tools/source.properties
@@ -0,0 +1,8 @@
+### Android Tool: Source of this archive.
+#Thu Sep 24 18:29:52 EEST 2015
+Archive.HostOs=macosx
+Pkg.License=To get started with the Android SDK, you must agree to the following terms and conditions.\n
+Pkg.LicenseRef=android-sdk-license
+Pkg.Revision=24.3.4
+Pkg.SourceUrl=https\://dl-ssl.google.com/android/repository/repository-10.xml
+Platform.MinPlatformToolsRev=20
diff --git a/src/test/fixtures/up-to-date-sdk-tools/project/src/main/AndroidManifest.xml b/src/test/fixtures/up-to-date-sdk-tools/project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ef0e03b
--- /dev/null
+++ b/src/test/fixtures/up-to-date-sdk-tools/project/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/test/groovy/com/jakewharton/sdkmanager/internal/PackageResolverTest.groovy b/src/test/groovy/com/jakewharton/sdkmanager/internal/PackageResolverTest.groovy
index fc0c6e5..6557472 100644
--- a/src/test/groovy/com/jakewharton/sdkmanager/internal/PackageResolverTest.groovy
+++ b/src/test/groovy/com/jakewharton/sdkmanager/internal/PackageResolverTest.groovy
@@ -5,6 +5,7 @@ import com.jakewharton.sdkmanager.SdkManagerExtension
import com.jakewharton.sdkmanager.TemporaryFixture
import com.jakewharton.sdkmanager.util.RecordingAndroidCommand
import org.gradle.api.Project
+import org.gradle.api.tasks.StopExecutionException
import org.gradle.testfixtures.ProjectBuilder
import org.junit.Before
import org.junit.Rule
@@ -13,6 +14,7 @@ import org.junit.Test
import static com.android.SdkConstants.FN_LOCAL_PROPERTIES
import static com.android.SdkConstants.SDK_DIR_PROPERTY
import static org.fest.assertions.api.Assertions.assertThat
+import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown
class PackageResolverTest {
@Rule public TemporaryFixture fixture = new TemporaryFixture();
@@ -339,4 +341,48 @@ class PackageResolverTest {
packageResolver.resolveEmulator()
assertThat(androidCommand).contains('update sys-img-armeabi-v7a-android-19')
}
+
+ @FixtureName("up-to-date-sdk-tools")
+ @Test public void upToDateSdkToolsRecognized() {
+ project.apply plugin: 'com.android.application'
+ project.extensions.create("sdkManager", SdkManagerExtension)
+ project.sdkManager {
+ minSdkToolsVersion '24.3.4'
+ }
+
+ packageResolver.resolveSdkTools()
+ assertThat(androidCommand).doesNotContain('update tools')
+ }
+
+ @FixtureName("outdated-sdk-tools")
+ @Test public void outdatedSdkToolsDownloaded() {
+ project.apply plugin: 'com.android.application'
+ project.extensions.create("sdkManager", SdkManagerExtension)
+ project.sdkManager {
+ minSdkToolsVersion '24.3.4'
+ }
+
+ packageResolver.resolveSdkTools()
+ assertThat(androidCommand).contains('update tools')
+ }
+
+ @FixtureName("up-to-date-sdk-tools")
+ @Test public void sdkToolsUnavailable() {
+ def tooBigMinSdkToolsVersion = '100500'
+
+ project.apply plugin: 'com.android.application'
+ project.extensions.create("sdkManager", SdkManagerExtension)
+ project.sdkManager {
+ minSdkToolsVersion tooBigMinSdkToolsVersion
+ }
+
+ try {
+ packageResolver.resolveSdkTools()
+ failBecauseExceptionWasNotThrown(StopExecutionException.class);
+ } catch (Exception e) {
+ assertThat(e)
+ .isInstanceOf(StopExecutionException.class)
+ .hasMessageEndingWith(tooBigMinSdkToolsVersion)
+ }
+ }
}
diff --git a/src/test/groovy/com/jakewharton/sdkmanager/util/RecordingAndroidCommand.groovy b/src/test/groovy/com/jakewharton/sdkmanager/util/RecordingAndroidCommand.groovy
index ffdb4d5..d0f47df 100644
--- a/src/test/groovy/com/jakewharton/sdkmanager/util/RecordingAndroidCommand.groovy
+++ b/src/test/groovy/com/jakewharton/sdkmanager/util/RecordingAndroidCommand.groovy
@@ -16,10 +16,13 @@ final class RecordingAndroidCommand extends ArrayList implements Android
@Override String list(String filter) {
add("list -a -e" as String)
- return "id: 55 or \"sys-img-armeabi-v7a-android-19\"\n" +
- " Type: SystemImage\n" +
- " Desc: Android SDK Platform 4.4.2\n" +
- " Revision 2\n" +
- " Requires SDK Platform Android API 19\n"
+ return "id: 1 or \"tools\"\n" +
+ " Type: Tool\n" +
+ " Desc: Android SDK Tools, revision 24.3.4\n" +
+ "id: 55 or \"sys-img-armeabi-v7a-android-19\"\n" +
+ " Type: SystemImage\n" +
+ " Desc: Android SDK Platform 4.4.2\n" +
+ " Revision 2\n" +
+ " Requires SDK Platform Android API 19\n"
}
}