diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java index ed72fc36ffb..a24671bf619 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java @@ -37,9 +37,12 @@ import com.android.ide.common.repository.ResourceVisibilityLookup; import com.android.ide.common.res2.AbstractResourceRepository; import com.android.ide.common.res2.ResourceItem; import com.android.prefs.AndroidLocation; +import com.android.repository.api.ProgressIndicator; +import com.android.repository.api.ProgressIndicatorAdapter; import com.android.sdklib.BuildToolInfo; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkVersionInfo; +import com.android.sdklib.repositoryv2.AndroidSdkHandler; import com.android.tools.klint.detector.api.Context; import com.android.tools.klint.detector.api.Detector; import com.android.tools.klint.detector.api.Issue; @@ -741,9 +744,12 @@ public abstract class LintClient { @NonNull public IAndroidTarget[] getTargets() { if (mTargets == null) { - SdkWrapper localSdk = getSdk(); - if (localSdk != null) { - mTargets = localSdk.getTargets(); + AndroidSdkHandler sdkHandler = getSdk(); + if (sdkHandler != null) { + ProgressIndicator logger = getRepositoryLogger(); + Collection targets = sdkHandler.getAndroidTargetManager(logger) + .getTargets(logger); + mTargets = targets.toArray(new IAndroidTarget[targets.size()]); } else { mTargets = new IAndroidTarget[0]; } @@ -752,7 +758,7 @@ public abstract class LintClient { return mTargets; } - protected SdkWrapper mSdk; + protected AndroidSdkHandler mSdk; /** * Returns the SDK installation (used to look up platforms etc) @@ -760,13 +766,13 @@ public abstract class LintClient { * @return the SDK if known */ @Nullable - public SdkWrapper getSdk() { - if (mSdk == null) { - File sdkHome = getSdkHome(); - if (sdkHome != null) { - mSdk = SdkWrapper.createLocalSdk(sdkHome); - } - } + public AndroidSdkHandler getSdk() { + if (mSdk == null) { + File sdkHome = getSdkHome(); + if (sdkHome != null) { + mSdk = AndroidSdkHandler.getInstance(sdkHome); + } + } return mSdk; } @@ -1217,4 +1223,33 @@ public abstract class LintClient { public static boolean isGradle() { return CLIENT_GRADLE.equals(sClientName); } + + @NonNull + public ProgressIndicator getRepositoryLogger() { + return new LintClient.RepoLogger(); + } + + private static final class RepoLogger extends ProgressIndicatorAdapter { + // Intentionally not logging these: the SDK manager is + // logging events such as package.xml parsing + // Parsing /path/to/sdk//build-tools/19.1.0/package.xml + // Parsing /path/to/sdk//build-tools/20.0.0/package.xml + // Parsing /path/to/sdk//build-tools/21.0.0/package.xml + // which we don't want to spam on the console. + // It's also warning about packages that it's encountering + // multiple times etc; that's not something we should include + // in lint command line output. + + @Override + public void logError(@NonNull String s, @Nullable Throwable e) { + } + + @Override + public void logInfo(@NonNull String s) { + } + + @Override + public void logWarning(@NonNull String s, @Nullable Throwable e) { + } + } } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java index 2ce77af76f7..db18a637e41 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java @@ -42,6 +42,7 @@ import com.android.ide.common.res2.ResourceItem; import com.android.resources.ResourceFolderType; import com.android.sdklib.BuildToolInfo; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.repositoryv2.AndroidSdkHandler; import com.android.tools.klint.client.api.LintListener.EventType; import com.android.tools.klint.detector.api.ClassContext; import com.android.tools.klint.detector.api.Context; @@ -2129,7 +2130,7 @@ public class LintDriver { @Nullable @Override - public SdkWrapper getSdk() { + public AndroidSdkHandler getSdk() { return mDelegate.getSdk(); } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkWrapper.kt b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkWrapper.kt deleted file mode 100644 index b21d46fbe11..00000000000 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkWrapper.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.klint.client.api - -import com.android.sdklib.IAndroidTarget -import java.io.File - -class SdkWrapper(val sdk: Any) { - - companion object { - val PROGRESS_INDICATOR_CLASS_NAME = "com.android.repository.api.ConsoleProgressIndicator" - - @JvmStatic - fun createLocalSdk(file: File): SdkWrapper? { - try { - val clazz = Class.forName("com.android.sdklib.repository.local.LocalSdk") - return SdkWrapper(clazz.getConstructor(File::class.java).newInstance(file)) - } - catch (t: Throwable) { - try { - val clazz = Class.forName("com.android.sdklib.repositoryv2.AndroidSdkHandler") - return SdkWrapper(clazz.getMethod("getInstance", File::class.java).invoke(null, file)) - } - catch (t2: Throwable) { - return null - } - } - } - } - - fun getTargets(): Array? { - try { - @Suppress("UNCHECKED_CAST") - return sdk.javaClass.getMethod("getTargets").invoke(sdk) as? Array - } catch (t: Throwable) { - @Suppress("UNCHECKED_CAST") - try { - val targets = sdk.javaClass - .getMethod("getAndroidTargetManager", Class.forName(PROGRESS_INDICATOR_CLASS_NAME)) - .invoke(sdk, createProgressIndicator()) as? Collection ?: return null - return targets.toTypedArray() - } - catch (t2: Throwable) { - return null - } - } - } - - fun getPlatformVersion(): String? { - try { - val pkgPlatformTools = Class.forName("com.android.sdklib.repository.descriptors.PkgType") - .getField("PKG_PLATFORM_TOOLS").get(null) - val pkgInfo = sdk.javaClass.declaredMethods - .first { it.name == "getPkgInfo" && it.parameterTypes.size == 1 } - .invoke(sdk, pkgPlatformTools) - if (pkgInfo != null) { - val desc = pkgInfo.javaClass.getMethod("getDesc").invoke(pkgInfo) - val version = desc.javaClass.getMethod("getFullRevision").invoke(desc) - if (version != null) { - return version.javaClass.getMethod("toShortString").invoke(version) as? String - } - } - else { - return null - } - } catch (t: Throwable) { - try { - val pkgPlatformTools = Class.forName("com.android.SdkConstants") - .getField("FD_PLATFORM_TOOLS").get(null) - val pkgInfo = sdk.javaClass.declaredMethods - .first { it.name == "getLocalPackage" && it.parameterTypes.size == 2 } - .invoke(sdk, pkgPlatformTools, createProgressIndicator()) - if (pkgInfo != null) { - val version = pkgInfo.javaClass.getMethod("getVersion").invoke(pkgInfo) - return version.javaClass.getMethod("toShortString").invoke(version) as? String - } - else { - return null - } - } - catch (ignore: Throwable) {} - } - - return null - } - - private fun createProgressIndicator(): Any { - return Class.forName(PROGRESS_INDICATOR_CLASS_NAME).newInstance() - } -} \ No newline at end of file diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java index 933c54cff6a..ba3221cc5b0 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java @@ -48,6 +48,7 @@ import com.android.builder.model.AndroidProject; import com.android.builder.model.ProductFlavor; import com.android.builder.model.ProductFlavorContainer; import com.android.builder.model.Variant; +import com.android.ide.common.repository.GradleVersion; import com.android.ide.common.repository.ResourceVisibilityLookup; import com.android.resources.Density; import com.android.resources.ResourceFolderType; @@ -136,6 +137,7 @@ public class Project { protected Boolean mGradleProject; protected Boolean mSupportLib; protected Boolean mAppCompat; + protected GradleVersion mGradleVersion; private Map mSuperClassMap; private ResourceVisibilityLookup mResourceVisibility; private BuildToolInfo mBuildTools; @@ -190,6 +192,24 @@ public class Project { public AndroidProject getGradleProjectModel() { return null; } + + /** + * If this is a Gradle project with a valid Gradle model, return the version + * of the model/plugin. + * + * @return the Gradle plugin version, or null if invalid or not a Gradle project + */ + @Nullable + public GradleVersion getGradleModelVersion() { + if (mGradleVersion == null && isGradleProject()) { + AndroidProject gradleProjectModel = getGradleProjectModel(); + if (gradleProjectModel != null) { + mGradleVersion = GradleVersion.tryParse(gradleProjectModel.getModelVersion()); + } + } + + return mGradleVersion; + } /** * Returns the project model for this project if it corresponds to diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java index 298c8d307e6..b6c0e6b0354 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java @@ -63,10 +63,8 @@ import com.android.resources.ResourceType; import com.android.sdklib.AndroidVersion; import com.android.sdklib.BuildToolInfo; import com.android.sdklib.SdkVersionInfo; -import com.android.tools.klint.client.api.IssueRegistry; -import com.android.tools.klint.client.api.JavaEvaluator; -import com.android.tools.klint.client.api.LintDriver; -import com.android.tools.klint.client.api.UastLintUtils; +import com.android.sdklib.repositoryv2.AndroidSdkHandler; +import com.android.tools.klint.client.api.*; import com.android.tools.klint.detector.api.Category; import com.android.tools.klint.detector.api.ClassContext; import com.android.tools.klint.detector.api.Context; @@ -339,68 +337,67 @@ public class ApiDetector extends ResourceXmlDetector @Override public void beforeCheckProject(@NonNull Context context) { if (mApiDatabase == null) { - //TODO - //mApiDatabase = ApiLookup.get(context.getClient()); - //// We can't look up the minimum API required by the project here: - //// The manifest file hasn't been processed yet in the -before- project hook. - //// For now it's initialized lazily in getMinSdk(Context), but the - //// lint infrastructure should be fixed to parse manifest file up front. - // - //if (mApiDatabase == null && !mWarnedMissingDb) { - // mWarnedMissingDb = true; - // context.report(IssueRegistry.LINT_ERROR, Location.create(context.file), - // "Can't find API database; API check not performed"); - //} else { - // // See if you don't have at least version 23.0.1 of platform tools installed - // AndroidSdkHandler sdk = context.getClient().getSdk(); - // if (sdk == null) { - // return; - // } - // LocalPackage pkgInfo = sdk.getLocalPackage(SdkConstants.FD_PLATFORM_TOOLS, - // context.getClient().getRepositoryLogger()); - // if (pkgInfo == null) { - // return; - // } - // Revision revision = pkgInfo.getVersion(); - // - // // The platform tools must be at at least the same revision - // // as the compileSdkVersion! - // // And as a special case, for 23, they must be at 23.0.1 - // // because 23.0.0 accidentally shipped without Android M APIs. - // int compileSdkVersion = context.getProject().getBuildSdk(); - // if (compileSdkVersion == 23) { - // if (revision.getMajor() > 23 || revision.getMajor() == 23 - // && (revision.getMinor() > 0 || revision.getMicro() > 0)) { - // return; - // } - // } else if (compileSdkVersion <= revision.getMajor()) { - // return; - // } - // - // // Pick a location: when incrementally linting in the IDE, tie - // // it to the current file - // List currentFiles = context.getProject().getSubset(); - // Location location; - // if (currentFiles != null && currentFiles.size() == 1) { - // File file = currentFiles.get(0); - // String contents = context.getClient().readFile(file); - // int firstLineEnd = contents.indexOf('\n'); - // if (firstLineEnd == -1) { - // firstLineEnd = contents.length(); - // } - // location = Location.create(file, - // new DefaultPosition(0, 0, 0), new - // DefaultPosition(0, firstLineEnd, firstLineEnd)); - // } else { - // location = Location.create(context.file); - // } - // context.report(UNSUPPORTED, - // location, - // String.format("The SDK platform-tools version (%1$s) is too old " - // + " to check APIs compiled with API %2$d; please update", - // revision.toShortString(), - // compileSdkVersion)); - //} + mApiDatabase = ApiLookup.get(context.getClient()); + // We can't look up the minimum API required by the project here: + // The manifest file hasn't been processed yet in the -before- project hook. + // For now it's initialized lazily in getMinSdk(Context), but the + // lint infrastructure should be fixed to parse manifest file up front. + + if (mApiDatabase == null && !mWarnedMissingDb) { + mWarnedMissingDb = true; + context.report(IssueRegistry.LINT_ERROR, Location.create(context.file), + "Can't find API database; API check not performed"); + } else { + // See if you don't have at least version 23.0.1 of platform tools installed + AndroidSdkHandler sdk = context.getClient().getSdk(); + if (sdk == null) { + return; + } + LocalPackage pkgInfo = sdk.getLocalPackage(SdkConstants.FD_PLATFORM_TOOLS, + context.getClient().getRepositoryLogger()); + if (pkgInfo == null) { + return; + } + Revision revision = pkgInfo.getVersion(); + + // The platform tools must be at at least the same revision + // as the compileSdkVersion! + // And as a special case, for 23, they must be at 23.0.1 + // because 23.0.0 accidentally shipped without Android M APIs. + int compileSdkVersion = context.getProject().getBuildSdk(); + if (compileSdkVersion == 23) { + if (revision.getMajor() > 23 || revision.getMajor() == 23 + && (revision.getMinor() > 0 || revision.getMicro() > 0)) { + return; + } + } else if (compileSdkVersion <= revision.getMajor()) { + return; + } + + // Pick a location: when incrementally linting in the IDE, tie + // it to the current file + List currentFiles = context.getProject().getSubset(); + Location location; + if (currentFiles != null && currentFiles.size() == 1) { + File file = currentFiles.get(0); + String contents = context.getClient().readFile(file); + int firstLineEnd = contents.indexOf('\n'); + if (firstLineEnd == -1) { + firstLineEnd = contents.length(); + } + location = Location.create(file, + new DefaultPosition(0, 0, 0), new + DefaultPosition(0, firstLineEnd, firstLineEnd)); + } else { + location = Location.create(context.file); + } + context.report(UNSUPPORTED, + location, + String.format("The SDK platform-tools version (%1$s) is too old " + + " to check APIs compiled with API %2$d; please update", + revision.toShortString(), + compileSdkVersion)); + } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java index 46c18bdc691..2c670bd3a90 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java @@ -24,8 +24,10 @@ import com.android.SdkConstants; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; +import com.android.repository.api.LocalPackage; +import com.android.sdklib.repositoryv2.AndroidSdkHandler; import com.android.tools.klint.client.api.LintClient; -import com.android.tools.klint.client.api.SdkWrapper; +//import com.android.tools.klint.client.api.SdkWrapper; import com.android.tools.klint.detector.api.LintUtils; import com.android.utils.Pair; import com.google.common.base.Charsets; @@ -140,9 +142,13 @@ public class ApiLookup { @VisibleForTesting @Nullable static String getPlatformVersion(@NonNull LintClient client) { - SdkWrapper sdk = client.getSdk(); + AndroidSdkHandler sdk = client.getSdk(); if (sdk != null) { - return sdk.getPlatformVersion(); + LocalPackage pkgInfo = sdk + .getLocalPackage(SdkConstants.FD_PLATFORM_TOOLS, client.getRepositoryLogger()); + if (pkgInfo != null) { + return pkgInfo.getVersion().toShortString(); + } } return null; } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt deleted file mode 100644 index a60f967d545..00000000000 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt +++ /dev/null @@ -1,99 +0,0 @@ -package org.jetbrains.android.inspections.klint - -import com.android.builder.model.AndroidProject -import com.android.builder.model.SourceProvider -import com.android.builder.model.Variant -import com.android.tools.idea.gradle.util.GradleUtil -import com.android.tools.klint.client.api.SdkWrapper -import org.jetbrains.android.facet.AndroidFacet -import kotlin.reflect.memberFunctions -import kotlin.reflect.staticFunctions - -class AndroidModelFacade(val facet: AndroidFacet) { - private val model by lazy { loadModel() } - private val modelClass by lazy { - try { - Class.forName("com.android.tools.idea.gradle.AndroidGradleModel") - } - catch(e: ClassNotFoundException) { - null - } - } - - fun getLocalSdk(): SdkWrapper? { - val sdkData = facet.sdkData ?: return null - val localSdk = sdkData.javaClass.kotlin.memberFunctions - .firstOrNull { it.name == "getLocalSdk" || it.name == "getSdkHandler" } - ?.call(sdkData) ?: return null - return SdkWrapper(localSdk) - } - - fun isModelReady() = model != null - - @Suppress("UNCHECKED_CAST") - fun getFlavorSourceProviders(): List? - = getFacetOrModel("getFlavorSourceProviders") as? List - - fun getMultiFlavorSourceProvider(): SourceProvider? - = getFacetOrModel("getMultiFlavorSourceProvider") as? SourceProvider - - fun getBuildTypeSourceProvider(): SourceProvider? - = getFacetOrModel("getBuildTypeSourceProvider") as? SourceProvider - - fun getVariantSourceProvider(): SourceProvider? - = getFacetOrModel("getVariantSourceProvider") as? SourceProvider - - private fun getFacetOrModel(methodName: String): Any? { - if (modelClass == null) { - return facet.javaClass.kotlin.memberFunctions - .firstOrNull { it.name == methodName } - ?.call(facet) - } - - val model = model ?: return null - - return modelClass?.kotlin?.memberFunctions - ?.firstOrNull { it.name == methodName } - ?.call(model) - } - - fun getDependsOn(artifact: String): Boolean { - val model = model ?: return false - return GradleUtil::class.staticFunctions.firstOrNull { - val type = it.parameters.firstOrNull()?.type?.toString() ?: "" - it.parameters.size == 2 && "IdeaAndroidProject" in type || "AndroidGradleModel" in type - }?.call(model, artifact) as? Boolean ?: false - } - - fun getAndroidProject(): AndroidProject? { - val model = model ?: return null - return modelClass?.kotlin?.memberFunctions - ?.firstOrNull { it.name == "getDelegate" || it.name == "getAndroidProject" } - ?.call(model) as? AndroidProject - } - - fun getSelectedVariant(): Variant? { - val model = model ?: return null - return modelClass?.kotlin?.memberFunctions - ?.firstOrNull { it.name == "getSelectedVariant" } - ?.call(model) as? Variant - } - - private fun loadModel(): Any? { - try { - val getAndroidProjectInfoFun = AndroidFacet::class.memberFunctions.singleOrNull { - it.name == "getIdeaAndroidProject" || it.name == "getAndroidModel" - } - - return getAndroidProjectInfoFun?.call(facet) - } - catch(e: Throwable) { - return false - } - } - - fun computePackageName(): String? { - return getSelectedVariant()?.mainArtifact?.applicationId - } - -} \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java index 33ea6355c2a..1f85f74075a 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java @@ -7,10 +7,12 @@ import com.android.ide.common.repository.ResourceVisibilityLookup; import com.android.ide.common.res2.AbstractResourceRepository; import com.android.ide.common.res2.ResourceFile; import com.android.ide.common.res2.ResourceItem; +import com.android.sdklib.repositoryv2.AndroidSdkHandler; import com.android.tools.idea.gradle.util.Projects; import com.android.tools.idea.rendering.AppResourceRepository; import com.android.tools.idea.rendering.LocalResourceRepository; import com.android.tools.idea.sdk.IdeSdks; +import com.android.tools.idea.welcome.install.AndroidSdk; import com.android.tools.klint.checks.ApiLookup; import com.android.tools.klint.client.api.*; import com.android.tools.klint.detector.api.*; @@ -332,10 +334,10 @@ public class IntellijLintClient extends LintClient implements Disposable { @Nullable @Override - public SdkWrapper getSdk() { + public AndroidSdkHandler getSdk() { if (mSdk == null) { Module module = getModule(); - SdkWrapper sdk = getLocalSdk(module); + AndroidSdkHandler sdk = getLocalSdk(module); if (sdk != null) { mSdk = sdk; } else { @@ -357,11 +359,14 @@ public class IntellijLintClient extends LintClient implements Disposable { } @Nullable - private static SdkWrapper getLocalSdk(@Nullable Module module) { + private static AndroidSdkHandler getLocalSdk(@Nullable Module module) { if (module != null) { AndroidFacet facet = AndroidFacet.getInstance(module); if (facet != null) { - return IntellijLintUtils.getModelFacade(facet).getLocalSdk(); + AndroidSdkData sdkData = facet.getSdkData(); + if (sdkData != null) { + return sdkData.getSdkHandler(); + } } } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java index b06496db8d8..398e834a9bf 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java @@ -337,10 +337,6 @@ public class IntellijLintUtils { return true; } - public static AndroidModelFacade getModelFacade(AndroidFacet facet) { - return new AndroidModelFacade(facet); - } - private static boolean appendJvmSignature(@NonNull StringBuilder buffer, @Nullable PsiType type) { if (type == null) { return false;