001/* 002 * Copyright (C) 2015-2021 KeepSafe Software 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.getkeepsafe.dexcount; 017 018import com.android.repository.Revision; 019import com.android.repository.Revision.Precision; 020import com.getkeepsafe.dexcount.plugin.TaskApplicator; 021import com.getkeepsafe.dexcount.plugin.TaskApplicators; 022import org.gradle.api.Plugin; 023import org.gradle.api.Project; 024import org.gradle.api.logging.configuration.ShowStacktrace; 025import org.gradle.util.GradleVersion; 026import org.jetbrains.annotations.NotNull; 027 028import java.lang.reflect.Field; 029import java.util.Arrays; 030import java.util.List; 031import java.util.Optional; 032 033public class DexMethodCountPlugin implements Plugin<Project> { 034 private static final String VERSION_3_ZERO_FIELD = "com.android.builder.Version"; // <= 3.0 035 private static final String VERSION_3_ONE_FIELD = "com.android.builder.model.Version"; // > 3.1 036 private static final String VERSION_7_0_FIELD = "com.android.Version"; // >= 7.0 037 private static final String AGP_VERSION_FIELD = "ANDROID_GRADLE_PLUGIN_VERSION"; 038 039 private static final GradleVersion MIN_GRADLE_VERSION = GradleVersion.version("6.0"); 040 private static final Revision MIN_AGP_VERSION = new Revision(3, 4, 0); 041 042 @Override 043 public void apply(@NotNull Project project) { 044 GradleVersion gradleVersion = GradleVersion.version(project.getGradle().getGradleVersion()); 045 if (gradleVersion.compareTo(MIN_GRADLE_VERSION) < 0) { 046 project.getLogger().error("dexcount requires Gradle {} or above", MIN_GRADLE_VERSION); 047 return; 048 } 049 050 Revision gradlePluginRevision = getCurrentAgpRevision(); 051 DexCountExtension ext = project.getExtensions().create("dexcount", DexCountExtension.class); 052 053 // If the user has passed '--stacktrace' or '--full-stacktrace', assume 054 // that they are trying to report a dexcount bug. Help them help us out 055 // by printing the current plugin title and version. 056 if (project.getGradle().getStartParameter().getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS) { 057 ext.getPrintVersion().set(true); 058 } 059 060 // We need to do this check *after* we create the 'dexcount' Gradle extension. 061 // If we bail on instant run builds any earlier, then the build will break 062 // for anyone configuring dexcount due to the extension not being registered. 063 if (ProjectUtils.isInstantRun(project)) { 064 project.getLogger().info("Instant Run detected; disabling dexcount"); 065 return; 066 } 067 068 if (gradlePluginRevision.compareTo(MIN_AGP_VERSION) < 0) { 069 project.getLogger().error("dexcount requires Android Gradle Plugin {} or above", MIN_AGP_VERSION); 070 return; 071 } 072 073 Optional<TaskApplicator.Factory> maybeFactory = TaskApplicators.getFactory(gradlePluginRevision); 074 if (maybeFactory.isPresent()) { 075 TaskApplicator applicator = maybeFactory.get().create(project, ext); 076 applicator.apply(); 077 } else { 078 project.getLogger().error( 079 "No dexcount TaskApplicator configured for Gradle version {} and AGP version {}", 080 gradleVersion, 081 gradlePluginRevision); 082 } 083 } 084 085 private Revision getCurrentAgpRevision() throws RuntimeException { 086 List<String> versionClassNames = Arrays.asList(VERSION_7_0_FIELD, VERSION_3_ONE_FIELD, VERSION_3_ZERO_FIELD); 087 Exception thrown = null; 088 089 for (String className : versionClassNames) { 090 try { 091 Class<?> versionClass = Class.forName(className); 092 Field agpVersionField = versionClass.getDeclaredField(AGP_VERSION_FIELD); 093 String agpVersion = agpVersionField.get(null).toString(); 094 return Revision.parseRevision(agpVersion, Precision.PREVIEW); 095 } catch (Exception e) { 096 thrown = e; 097 } 098 } 099 100 throw new IllegalStateException("dexcount requires the Android plugin to be configured", thrown); 101 } 102}