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.jetbrains.annotations.NotNull; 026 027import java.lang.reflect.Field; 028import java.util.Arrays; 029import java.util.List; 030import java.util.Optional; 031 032public class DexMethodCountPlugin implements Plugin<Project> { 033 private static final String VERSION_3_ZERO_FIELD = "com.android.builder.Version"; // <= 3.0 034 private static final String VERSION_3_ONE_FIELD = "com.android.builder.model.Version"; // > 3.1 035 private static final String VERSION_7_0_FIELD = "com.android.Version"; // >= 7.0 036 private static final String AGP_VERSION_FIELD = "ANDROID_GRADLE_PLUGIN_VERSION"; 037 038 private static final GradleVersion MIN_GRADLE_VERSION = new GradleVersion(6, 0); 039 private static final Revision MIN_AGP_VERSION = new Revision(3, 4, 0); 040 041 @Override 042 public void apply(@NotNull Project project) { 043 GradleVersion gradleVersion = ProjectUtils.gradleVersion(project); 044 if (gradleVersion.compareTo(MIN_GRADLE_VERSION) < 0) { 045 project.getLogger().error("dexcount requires Gradle {} or above", MIN_GRADLE_VERSION); 046 return; 047 } 048 049 Revision gradlePluginRevision = getCurrentAgpRevision(); 050 DexCountExtension ext = project.getExtensions().create("dexcount", DexCountExtension.class); 051 052 // If the user has passed '--stacktrace' or '--full-stacktrace', assume 053 // that they are trying to report a dexcount bug. Help them help us out 054 // by printing the current plugin title and version. 055 if (project.getGradle().getStartParameter().getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS) { 056 ext.getPrintVersion().set(true); 057 } 058 059 // We need to do this check *after* we create the 'dexcount' Gradle extension. 060 // If we bail on instant run builds any earlier, then the build will break 061 // for anyone configuring dexcount due to the extension not being registered. 062 if (ProjectUtils.isInstantRun(project)) { 063 project.getLogger().info("Instant Run detected; disabling dexcount"); 064 return; 065 } 066 067 if (gradlePluginRevision.compareTo(MIN_AGP_VERSION) < 0) { 068 project.getLogger().error("dexcount requires Android Gradle Plugin {} or above", MIN_AGP_VERSION); 069 return; 070 } 071 072 Optional<TaskApplicator.Factory> maybeFactory = TaskApplicators.getFactory(gradlePluginRevision); 073 if (maybeFactory.isPresent()) { 074 TaskApplicator applicator = maybeFactory.get().create(project, ext); 075 applicator.apply(); 076 } else { 077 project.getLogger().error( 078 "No dexcount TaskApplicator configured for Gradle version {} and AGP version {}", 079 gradleVersion, 080 gradlePluginRevision); 081 } 082 } 083 084 private Revision getCurrentAgpRevision() throws RuntimeException { 085 List<String> versionClassNames = Arrays.asList(VERSION_7_0_FIELD, VERSION_3_ONE_FIELD, VERSION_3_ZERO_FIELD); 086 Exception thrown = null; 087 088 for (String className : versionClassNames) { 089 try { 090 Class<?> versionClass = Class.forName(className); 091 Field agpVersionField = versionClass.getDeclaredField(AGP_VERSION_FIELD); 092 String agpVersion = agpVersionField.get(null).toString(); 093 return Revision.parseRevision(agpVersion, Precision.PREVIEW); 094 } catch (Exception e) { 095 thrown = e; 096 } 097 } 098 099 throw new IllegalStateException("dexcount requires the Android plugin to be configured", thrown); 100 } 101}