diff --git a/.github/workflows/pr-check-gradle-plugin.yml b/.github/workflows/pr-check-gradle-plugin.yml index 29f89ba68..886992c9b 100644 --- a/.github/workflows/pr-check-gradle-plugin.yml +++ b/.github/workflows/pr-check-gradle-plugin.yml @@ -30,13 +30,26 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: revert gradle wrapper mirror setting working-directory: projects/test/gradle-plugin-agp-compat-test - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties - - name: test AGP compatibility when core.gradle-plugin changed + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + - name: test AGP compatibility when core.gradle-plugin changed with JDK17 + working-directory: projects/test/gradle-plugin-agp-compat-test + run: ./test_JDK17.sh + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + cache: 'gradle' + - name: test AGP compatibility when core.gradle-plugin changed with JDK11 working-directory: projects/test/gradle-plugin-agp-compat-test - run: ./test.sh + run: ./test_JDK11.sh - name: stop gradle deamon for actions/cache run: ./gradlew --stop diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index e03825f7d..e3416e84f 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -23,10 +23,10 @@ jobs: uses: rlespinasse/github-slug-action@v3.x - name: checkout uses: actions/checkout@v2 - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'gradle' - uses: actions/cache@v2 with: @@ -37,7 +37,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: buildSdk run: ./gradlew buildSdk -S - name: build sample/source @@ -64,10 +64,10 @@ jobs: uses: rlespinasse/github-slug-action@v3.x - name: checkout uses: actions/checkout@v2 - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'gradle' - uses: actions/cache@v2 with: @@ -78,7 +78,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: buildSdk run: ./gradlew buildSdk -S - name: build sample/source @@ -103,7 +103,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: revert gradle wrapper mirror setting for sample host-project working-directory: projects/sample/maven/host-project run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip' > gradle/wrapper/gradle-wrapper.properties diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e0891630..bb48c4ff0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,10 +15,10 @@ jobs: uses: rlespinasse/github-slug-action@v3.x - name: checkout uses: actions/checkout@v2 - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'gradle' - uses: actions/cache@v2 with: @@ -29,7 +29,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: buildSdk run: ./gradlew buildSdk - name: build sample/source @@ -59,10 +59,10 @@ jobs: uses: rlespinasse/github-slug-action@v3.x - name: checkout uses: actions/checkout@v2 - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'gradle' - uses: actions/cache@v2 with: @@ -73,7 +73,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: publish run: ./gradlew publish - name: stop gradle deamon for actions/cache diff --git a/.github/workflows/test-avd.yml b/.github/workflows/test-avd.yml index 0cd7d2e03..f5f579e87 100644 --- a/.github/workflows/test-avd.yml +++ b/.github/workflows/test-avd.yml @@ -19,10 +19,10 @@ jobs: uses: rlespinasse/github-slug-action@v3.x - name: checkout uses: actions/checkout@v2 - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'gradle' - uses: actions/cache@v2 with: @@ -33,7 +33,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: revert gradle wrapper mirror setting - run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip' > gradle/wrapper/gradle-wrapper.properties + run: echo 'distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip' > gradle/wrapper/gradle-wrapper.properties - name: buildSdk run: ./gradlew buildSdk -S - name: build sample/source diff --git a/build.gradle b/build.gradle index 82b253df7..41f9f11e1 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,8 @@ buildscript { def versions_properties_path = 'buildScripts/gradle/versions.properties' def versions = new Properties() versions.load(file(versions_properties_path).newReader()) - versions.forEach { key, value -> + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue ext.set(key, value) } } diff --git a/buildScripts/gradle/common.gradle b/buildScripts/gradle/common.gradle index 699478409..2ee0d1607 100644 --- a/buildScripts/gradle/common.gradle +++ b/buildScripts/gradle/common.gradle @@ -8,25 +8,21 @@ def gitShortRev() { } allprojects { - ext.COMPILE_SDK_VERSION = 31 - ext.MIN_SDK_VERSION = 14 - ext.TARGET_SDK_VERSION = 28 - ext.VERSION_CODE = 1 - + def versionName, versionSuffix if ("${System.env.CI}".equalsIgnoreCase("true")) { - ext.VERSION_NAME = System.getenv("GITHUB_REF_SLUG") + versionName = System.getenv("GITHUB_REF_SLUG") } else { - ext.VERSION_NAME = "local" + versionName = project.VERSION_NAME } if ("${System.env.PUBLISH_RELEASE}".equalsIgnoreCase("true")) { - ext.VERSION_SUFFIX = "" + versionSuffix = "" } else if ("${System.env.CI}".equalsIgnoreCase("true")) { - ext.VERSION_SUFFIX = "-${System.env.GITHUB_SHA_SHORT}-SNAPSHOT" + versionSuffix = "-${System.env.GITHUB_SHA_SHORT}-SNAPSHOT" } else { - ext.VERSION_SUFFIX = "-${gitShortRev()}-SNAPSHOT" + versionSuffix = "-${gitShortRev()}-SNAPSHOT" } - ext.ARTIFACT_VERSION = ext.VERSION_NAME + ext.VERSION_SUFFIX + ext.ARTIFACT_VERSION = versionName + versionSuffix ext.TEST_HOST_APP_APPLICATION_ID = 'com.tencent.shadow.test.hostapp' ext.SAMPLE_HOST_APP_APPLICATION_ID = 'com.tencent.shadow.sample.host' repositories { diff --git a/buildScripts/gradle/versions.properties b/buildScripts/gradle/versions.properties index 4af3bd71e..cd827dcef 100644 --- a/buildScripts/gradle/versions.properties +++ b/buildScripts/gradle/versions.properties @@ -1,3 +1,8 @@ +COMPILE_SDK_VERSION=31 +MIN_SDK_VERSION=14 +TARGET_SDK_VERSION=28 +VERSION_CODE=1 +VERSION_NAME=local android_build_tools_version=30.0.3 android_support_annotations_version=28.0.0 android_support_version=27.1.1 @@ -5,7 +10,7 @@ androidx_activity_version=1.4.0 androidx_appcompat_version=1.4.1 androidx_test_junit_version=1.1.2 androidx_test_version=1.3.0 -build_gradle_version=7.0.3 +build_gradle_version=7.4.2 commons_io_android_version=2.5 commons_io_jvm_version=2.9.0 espresso_version=3.3.0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b21bddc71..4126a5d0d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -#distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip -distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.0.2-bin.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/projects/sample/source/sample-plugin/third-party/pinnedheaderexpandablelistview/build.gradle b/projects/sample/source/sample-plugin/third-party/pinnedheaderexpandablelistview/build.gradle index 39f5f4bfc..897ca6f79 100644 --- a/projects/sample/source/sample-plugin/third-party/pinnedheaderexpandablelistview/build.gradle +++ b/projects/sample/source/sample-plugin/third-party/pinnedheaderexpandablelistview/build.gradle @@ -5,12 +5,10 @@ android { defaultConfig { - //noinspection MinSdkTooLow - minSdkVersion 8 - //noinspection ExpiredTargetSdkVersion - targetSdkVersion 15 - versionCode 1 - versionName "1.0" + minSdkVersion project.MIN_SDK_VERSION + targetSdkVersion project.TARGET_SDK_VERSION + versionCode project.VERSION_CODE + versionName project.VERSION_NAME testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/projects/sample/source/sample-plugin/third-party/slidingmenu/build.gradle b/projects/sample/source/sample-plugin/third-party/slidingmenu/build.gradle index aee880bd5..051a58b41 100644 --- a/projects/sample/source/sample-plugin/third-party/slidingmenu/build.gradle +++ b/projects/sample/source/sample-plugin/third-party/slidingmenu/build.gradle @@ -5,12 +5,10 @@ android { defaultConfig { - //noinspection MinSdkTooLow - minSdkVersion 5 - //noinspection ExpiredTargetSdkVersion - targetSdkVersion 17 - versionCode 1 - versionName "1.0" + minSdkVersion project.MIN_SDK_VERSION + targetSdkVersion project.TARGET_SDK_VERSION + versionCode project.VERSION_CODE + versionName project.VERSION_NAME testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/projects/sdk/coding/build.gradle b/projects/sdk/coding/build.gradle index 0ec96697e..140f24820 100644 --- a/projects/sdk/coding/build.gradle +++ b/projects/sdk/coding/build.gradle @@ -8,7 +8,8 @@ buildscript { def versions_properties_path = '../../../buildScripts/gradle/versions.properties' def versions = new Properties() versions.load(file(versions_properties_path).newReader()) - versions.forEach { key, value -> + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue ext.set(key, value) } } diff --git a/projects/sdk/coding/code-generator/build.gradle b/projects/sdk/coding/code-generator/build.gradle index ef2c64fec..886f03218 100644 --- a/projects/sdk/coding/code-generator/build.gradle +++ b/projects/sdk/coding/code-generator/build.gradle @@ -9,3 +9,15 @@ dependencies { compileOnly project(':android-jar') testImplementation project(':android-jar') } + +compileKotlin { + kotlinOptions { + jvmTarget = "1.6" + } +} + +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.6" + } +} diff --git a/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/coding/code_generator/ActivityCodeGenerator.kt b/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/coding/code_generator/ActivityCodeGenerator.kt index b976674f4..35577ff30 100644 --- a/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/coding/code_generator/ActivityCodeGenerator.kt +++ b/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/coding/code_generator/ActivityCodeGenerator.kt @@ -10,7 +10,16 @@ import android.content.ComponentCallbacks2 import android.view.ContextThemeWrapper import android.view.KeyEvent import android.view.Window -import com.squareup.javapoet.* +import com.squareup.javapoet.AnnotationSpec +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterSpec +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.TypeVariableName +import com.tencent.shadow.core.runtime.NeighborClass import javassist.ClassMap import javassist.ClassPool import javassist.LoaderClassPath @@ -82,7 +91,8 @@ class ActivityCodeGenerator { val cl = Thread.currentThread().contextClassLoader classPool.appendClassPath(LoaderClassPath(cl)) } - classPool.makeClass("$RUNTIME_PACKAGE.ShadowApplication").toClass() + classPool.makeClass("$RUNTIME_PACKAGE.ShadowApplication") + .toClass(NeighborClass::class.java) } val ActivityClass = Activity::class.java @@ -115,7 +125,7 @@ class ActivityCodeGenerator { } ctClass.replaceClassName(renameMap) - return ctClass.toClass() + return ctClass.toClass(NeighborClass::class.java) } fun getActivityMethods(clazz: Class<*>): List { diff --git a/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/core/runtime/NeighborClass.kt b/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/core/runtime/NeighborClass.kt new file mode 100644 index 000000000..0e1fa81d0 --- /dev/null +++ b/projects/sdk/coding/code-generator/src/main/kotlin/com/tencent/shadow/core/runtime/NeighborClass.kt @@ -0,0 +1,7 @@ +package com.tencent.shadow.core.runtime + +/** + * 为了兼容JDK 17和javassist + * https://github.com/jboss-javassist/javassist/issues/369 + */ +class NeighborClass \ No newline at end of file diff --git a/projects/sdk/coding/get-android-jar/build.gradle b/projects/sdk/coding/get-android-jar/build.gradle index 26ba575f2..c9bad8efe 100644 --- a/projects/sdk/coding/get-android-jar/build.gradle +++ b/projects/sdk/coding/get-android-jar/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion project.COMPILE_SDK_VERSION } -def sdkPath = android.sdkComponents.getSdkDirectory().get().asFile.absolutePath +def sdkPath = android.sdkDirectory def androidJarPath = new File(sdkPath, "platforms/${android.compileSdkVersion}/android.jar") if (!androidJarPath.exists()) { diff --git a/projects/sdk/coding/gradle/wrapper/gradle-wrapper.properties b/projects/sdk/coding/gradle/wrapper/gradle-wrapper.properties index b21bddc71..4126a5d0d 100644 --- a/projects/sdk/coding/gradle/wrapper/gradle-wrapper.properties +++ b/projects/sdk/coding/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -#distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip -distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.0.2-bin.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/projects/sdk/core/build.gradle b/projects/sdk/core/build.gradle index f0c1563bc..6bbb55830 100644 --- a/projects/sdk/core/build.gradle +++ b/projects/sdk/core/build.gradle @@ -8,7 +8,8 @@ buildscript { def versions_properties_path = '../../../buildScripts/gradle/versions.properties' def versions = new Properties() versions.load(file(versions_properties_path).newReader()) - versions.forEach { key, value -> + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue ext.set(key, value) } } diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt index 1e822dbc6..0088123a0 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt @@ -33,4 +33,5 @@ internal interface AGPCompat { fun getProcessResourcesTask(output: BaseVariantOutput): Task fun getAaptAdditionalParameters(processResourcesTask: Task): List fun getMinSdkVersion(pluginVariant: ApplicationVariant): Int + fun hasDeprecatedTransformApi(): Boolean } \ No newline at end of file diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt index 39fe43d4f..e9c6f0621 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt @@ -71,6 +71,20 @@ internal class AGPCompatImpl : AGPCompat { return mergedFlavor.minSdkVersion?.apiLevel ?: VersionCodes.BASE } + override fun hasDeprecatedTransformApi(): Boolean { + try { + val version = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION + val majorVersion = version.substringBefore('.', "0").toInt() + if (majorVersion >= 8) { + return false//能parse出来主版本号大于等于8,我们就认为旧版Transform API不可用了。 + } + } catch (ignored: Error) { + } + + //读取版本号失败,就推测是旧版本的AGP,就应该有旧版本的Transform API + return true + } + companion object { fun getStringFromProperty(x: Any?): String { return when (x) { diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt index f7e090cd4..93df1a3b6 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt @@ -18,12 +18,17 @@ package com.tencent.shadow.core.gradle +import com.android.build.api.artifact.ScopedArtifact +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.ScopedArtifacts import com.android.build.gradle.AppExtension import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.ApplicationVariant import com.android.sdklib.AndroidVersion.VersionCodes import com.tencent.shadow.core.gradle.extensions.PackagePluginExtension import com.tencent.shadow.core.manifest_parser.generatePluginManifest +import com.tencent.shadow.core.transform.DeprecatedTransformWrapper +import com.tencent.shadow.core.transform.GradleTransformWrapper import com.tencent.shadow.core.transform.ShadowTransform import com.tencent.shadow.core.transform_kit.AndroidClassPoolBuilder import com.tencent.shadow.core.transform_kit.ClassPoolBuilder @@ -51,11 +56,43 @@ class ShadowPlugin : Plugin { val shadowExtension = project.extensions.create("shadow", ShadowExtension::class.java) if (!project.hasProperty("disable_shadow_transform")) { - baseExtension.registerTransform(ShadowTransform( + val shadowTransform = ShadowTransform( project, lateInitBuilder, { shadowExtension.transformConfig.useHostContext } - )) + ) + if (agpCompat.hasDeprecatedTransformApi()) { + baseExtension.registerTransform( + DeprecatedTransformWrapper( + project, + shadowTransform + ) + ) + } else { + val androidComponentsExtension = + project.extensions.getByName("androidComponents") as ApplicationAndroidComponentsExtension + androidComponentsExtension.onVariants( + selector = androidComponentsExtension.selector() + .withFlavor( + ShadowTransform.DimensionName + to ShadowTransform.ApplyShadowTransformFlavorName + ) + ) { variant -> + val taskProvider = project.tasks.register( + "${variant.name}ShadowTransform", + GradleTransformWrapper::class.java, + shadowTransform + ) + variant.artifacts.forScope(ScopedArtifacts.Scope.ALL) + .use(taskProvider) + .toTransform( + ScopedArtifact.CLASSES, + GradleTransformWrapper::allJars, + GradleTransformWrapper::allDirectories, + GradleTransformWrapper::output + ) + } + } } addFlavorForTransform(baseExtension) diff --git a/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/build.gradle b/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/build.gradle index c878b7170..74d8f1d63 100644 --- a/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/build.gradle +++ b/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/build.gradle @@ -1,5 +1,14 @@ buildscript { - ext.build_gradle_version = '7.0.3' + loadVersions: + {// 读取versions.properties到ext中,供项目中直接用变量引用版本号 + def versions_properties_path = '../../../../../../../../buildScripts/gradle/versions.properties' + def versions = new Properties() + versions.load(file(versions_properties_path).newReader()) + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue + ext.set(key, value) + } + } repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } @@ -18,11 +27,6 @@ plugins { } allprojects { - ext.COMPILE_SDK_VERSION = 29 - ext.MIN_SDK_VERSION = 16 - ext.TARGET_SDK_VERSION = 28 - ext.VERSION_CODE = 1 - ext.VERSION_NAME = "local" repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } diff --git a/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin1/build.gradle b/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin1/build.gradle index e39d1a135..16be8b226 100644 --- a/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin1/build.gradle +++ b/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin1/build.gradle @@ -1,5 +1,14 @@ buildscript { - ext.build_gradle_version = '7.0.3' + loadVersions: + {// 读取versions.properties到ext中,供项目中直接用变量引用版本号 + def versions_properties_path = '../../../../../../../../../buildScripts/gradle/versions.properties' + def versions = new Properties() + versions.load(file(versions_properties_path).newReader()) + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue + ext.set(key, value) + } + } repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } @@ -19,11 +28,6 @@ plugins { } allprojects { - ext.COMPILE_SDK_VERSION = 29 - ext.MIN_SDK_VERSION = 16 - ext.TARGET_SDK_VERSION = 28 - ext.VERSION_CODE = 1 - ext.VERSION_NAME = "local" repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } diff --git a/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin2/build.gradle b/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin2/build.gradle index c63568bfd..4ca7cc2b6 100644 --- a/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin2/build.gradle +++ b/projects/sdk/core/gradle-plugin/src/test/testProjects/case1/plugin2/build.gradle @@ -1,5 +1,14 @@ buildscript { - ext.build_gradle_version = '7.0.3' + loadVersions: + {// 读取versions.properties到ext中,供项目中直接用变量引用版本号 + def versions_properties_path = '../../../../../../../../../buildScripts/gradle/versions.properties' + def versions = new Properties() + versions.load(file(versions_properties_path).newReader()) + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue + ext.set(key, value) + } + } repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } @@ -19,11 +28,6 @@ plugins { } allprojects { - ext.COMPILE_SDK_VERSION = 29 - ext.MIN_SDK_VERSION = 16 - ext.TARGET_SDK_VERSION = 28 - ext.VERSION_CODE = 1 - ext.VERSION_NAME = "local" repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } diff --git a/projects/sdk/core/gradle/wrapper/gradle-wrapper.properties b/projects/sdk/core/gradle/wrapper/gradle-wrapper.properties index b21bddc71..4126a5d0d 100644 --- a/projects/sdk/core/gradle/wrapper/gradle-wrapper.properties +++ b/projects/sdk/core/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -#distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip -distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.0.2-bin.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/projects/sdk/core/transform-kit/build.gradle b/projects/sdk/core/transform-kit/build.gradle index 57e044d06..47129551b 100644 --- a/projects/sdk/core/transform-kit/build.gradle +++ b/projects/sdk/core/transform-kit/build.gradle @@ -22,6 +22,11 @@ compileTestKotlin { } } +java { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 +} + task testJar(type: Jar, dependsOn: testClasses) { baseName = "test-${project.archivesBaseName}" from sourceSets.test.output diff --git a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransform.kt b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransform.kt index 38c65cb90..f2d171bb7 100644 --- a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransform.kt +++ b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransform.kt @@ -18,11 +18,15 @@ package com.tencent.shadow.core.transform_kit -import com.android.build.api.transform.TransformInvocation import javassist.ClassPool import javassist.CtClass import org.gradle.api.Project -import java.io.* +import java.io.BufferedWriter +import java.io.DataOutputStream +import java.io.File +import java.io.FileOutputStream +import java.io.FileWriter +import java.io.OutputStream import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream import kotlin.system.measureTimeMillis @@ -46,8 +50,8 @@ abstract class AbstractTransform( mDebugClassJarZOS = ZipOutputStream(FileOutputStream(mDebugClassJar)) } - override fun beforeTransform(invocation: TransformInvocation) { - super.beforeTransform(invocation) + override fun beforeTransform() { + super.beforeTransform() ReplaceClassName.resetErrorCount() cleanDebugClassFileDir() } @@ -57,12 +61,12 @@ abstract class AbstractTransform( //原本预期是不会产生任何影响的。造成了ApplicationInfoTest失败,测试Activity没有被修改superclass。 // mOverrideCheck.prepare(mCtClassInputMap.keys.toSet()) - mTransformManager.setupAll() - mTransformManager.fireAll() + mTransformManager.setupAll(allInputCtClass) + mTransformManager.fireAll(allInputCtClass) } - override fun afterTransform(invocation: TransformInvocation) { - super.afterTransform(invocation) + override fun afterTransform() { + super.afterTransform() mDebugClassJarZOS.flush() mDebugClassJarZOS.close() @@ -71,18 +75,18 @@ abstract class AbstractTransform( //所以需要重新创建一个ClassPool,加载转换后的类,用于各种转换后的检查。 val debugClassPool = classPoolBuilder.build() debugClassPool.appendClassPath(mDebugClassJar.absolutePath) - val inputClassNames = mCtClassInputMap.keys.map { it.name } + val inputClassNames = allInputCtClass.map { it.name } onCheckTransformedClasses(debugClassPool, inputClassNames) } - override fun onOutputClass(entryName: String?, className: String, outputStream: OutputStream) { - classPool[className].debugWriteJar(entryName, mDebugClassJarZOS) - super.onOutputClass(entryName, className, outputStream) + override fun onOutputClass(className: String, outputStream: OutputStream) { + classPool[className].debugWriteJar(mDebugClassJarZOS) + super.onOutputClass(className, outputStream) } - private fun CtClass.debugWriteJar(outputEntryName: String?, outputStream: ZipOutputStream) { + private fun CtClass.debugWriteJar(outputStream: ZipOutputStream) { try { - val entryName = outputEntryName ?: (name.replace('.', '/') + ".class") + val entryName = name.replace('.', '/') + ".class" outputStream.putNextEntry(ZipEntry(entryName)) val p = stopPruning(true) toBytecode(DataOutputStream(outputStream)) diff --git a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransformManager.kt b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransformManager.kt index df6f7e66e..277d77356 100644 --- a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransformManager.kt +++ b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/AbstractTransformManager.kt @@ -21,24 +21,19 @@ package com.tencent.shadow.core.transform_kit import javassist.ClassPool import javassist.CtClass -abstract class AbstractTransformManager( - ctClassInputMap: Map, - private val classPool: ClassPool -) { - private val allInputClass = ctClassInputMap.keys - +abstract class AbstractTransformManager(private val classPool: ClassPool) { abstract val mTransformList: List - fun setupAll() { + fun setupAll(allInputCtClass: Set) { mTransformList.forEach { it.mClassPool = classPool - it.setup(allInputClass) + it.setup(allInputCtClass) } } - fun fireAll() { + fun fireAll(allInputCtClass: Set) { mTransformList.flatMap { it.list }.forEach { transform -> - transform.filter(allInputClass).forEach { + transform.filter(allInputCtClass).forEach { transform.transform(it) } } diff --git a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/ClassTransform.kt b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/ClassTransform.kt index 8ca4ccf2d..c6bb297ea 100644 --- a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/ClassTransform.kt +++ b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/ClassTransform.kt @@ -19,279 +19,120 @@ package com.tencent.shadow.core.transform_kit import com.android.SdkConstants -import com.android.build.api.transform.* -import com.android.build.api.transform.QualifiedContent.ContentType -import com.android.build.api.transform.QualifiedContent.Scope -import com.android.build.gradle.internal.pipeline.TransformManager import com.android.utils.FileUtils -import com.google.common.collect.ImmutableList -import com.google.common.io.Files import org.gradle.api.Project import java.io.File import java.io.FileInputStream -import java.io.FileOutputStream import java.io.OutputStream import java.util.zip.ZipEntry import java.util.zip.ZipInputStream -import java.util.zip.ZipOutputStream /** * 类转换基类 * * @author cubershi */ -abstract class ClassTransform(val project: Project) : Transform() { - val inputSet: MutableSet = mutableSetOf() +abstract class ClassTransform(val project: Project) { /** * 获取输入文件对应的输出文件路径.即将文件this路径中的inputDir部分替换为outputDir. */ - fun File.toOutputFile(inputDir: File, outputDir: File): File { + private fun File.toOutputFile(inputDir: File, outputDir: File): File { return File(outputDir, this.toRelativeString(inputDir)) } - fun input( - inputs: Collection, - outputProvider: TransformOutputProvider - ) { + fun input(inputs: Iterable) { val logger = project.logger if (logger.isInfoEnabled) { val sb = StringBuilder() sb.appendln() inputs.forEach { - it.directoryInputs.forEach { - sb.appendln(it.file.absolutePath) - } - it.jarInputs.forEach { - sb.appendln(it.file.absolutePath) - } + sb.appendln(it.asFile().absolutePath) } logger.info("ClassTransform input paths:$sb") } - inputs.forEach { - it.directoryInputs.forEach { - val inputDir = it.file - val transformInput = TransformInput(it) - inputSet.add(transformInput) - val allFiles = FileUtils.getAllFiles(it.file) - allFiles.filter { - it?.name?.endsWith(SdkConstants.DOT_CLASS) ?: false - }.forEach { - val inputClass = DirInputClass() - inputClass.onInputClass( - it, - it.toOutputFile(inputDir, transformInput.toOutput(outputProvider)) - ) - transformInput.addInputClass(inputClass) + inputs.forEach { transformInput -> + when (transformInput.kind) { + TransformInput.Kind.DIRECTORY -> { + val inputDir = transformInput.asFile() + val allFiles = FileUtils.getAllFiles(inputDir) + allFiles.filter { + it?.name?.endsWith(SdkConstants.DOT_CLASS) ?: false + }.forEach { + val className = loadDotClassFile(it) + transformInput.inputClassNames.add(className) + } } - } - it.jarInputs.forEach { - val transformInput = TransformInput(it) - inputSet.add(transformInput) - ZipInputStream(FileInputStream(it.file)).use { zis -> - var entry: ZipEntry? - while (true) { - entry = zis.nextEntry - if (entry == null) break + TransformInput.Kind.JAR -> { + ZipInputStream(FileInputStream(transformInput.asFile())).use { zis -> + var entry: ZipEntry? + while (true) { + entry = zis.nextEntry + if (entry == null) break - val name = entry.name + val entryName = entry.name - // 忽略一些实际上不会进入编译classpath的文件 - if (entry.isDirectory) continue - if (!name.endsWith(SdkConstants.DOT_CLASS)) continue - if (name.startsWith("META-INF/", true)) continue - if (name.endsWith("module-info.class", true)) continue - if (name.endsWith("package-info.class", true)) continue - - // 记录好entry和name的关系,添加再添加成transform的输入 - val inputClass = JarInputClass() - inputClass.onInputClass(zis, name) - transformInput.addInputClass(inputClass) - } - } - } - } - } + // 忽略一些实际上不会进入编译classpath的文件 + if (entry.isDirectory) continue + if (!entryName.endsWith(SdkConstants.DOT_CLASS)) continue + if (entryName.startsWith("META-INF/", true)) continue + if (entryName.endsWith("module-info.class", true)) continue + if (entryName.endsWith("package-info.class", true)) continue - fun output(outputProvider: TransformOutputProvider) { - inputSet.forEach { input -> - when (input.format) { - Format.DIRECTORY -> { - input.getInputClass().forEach { - val dirInputClass = it as DirInputClass - dirInputClass.getOutput().forEach { - val className = it.first - val file = it.second - Files.createParentDirs(file) - FileOutputStream(file).use { - onOutputClass(null, className, it) - } - } - } - } - Format.JAR -> { - val outputJar = input.toOutput(outputProvider) - Files.createParentDirs(outputJar) - ZipOutputStream(FileOutputStream(outputJar)).use { zos -> - input.getInputClass().forEach { - val jarInputClass = it as JarInputClass - jarInputClass.getOutput().forEach { - val className = it.first - val entryName = it.second - zos.putNextEntry(ZipEntry(entryName)) - onOutputClass(entryName, className, zos) - } + val className = loadClassFromJar(zis) + transformInput.inputClassNames.add(className) } } } } } - } - - abstract fun onOutputClass(entryName: String?, className: String, outputStream: OutputStream) - - abstract fun DirInputClass.onInputClass(classFile: File, outputFile: File) - abstract fun JarInputClass.onInputClass(zipInputStream: ZipInputStream, entryName: String) - abstract fun onTransform() + } - override fun getName(): String = this::class.simpleName!! + abstract fun onOutputClass(className: String, outputStream: OutputStream) - override fun getInputTypes(): MutableSet = TransformManager.CONTENT_CLASS + /** + * 让子类实现的字节码编辑框架加载.class文件,加载后返回类名 + */ + abstract fun loadDotClassFile(classFile: File): String - override fun isIncremental(): Boolean = false + /** + * 让子类实现的字节码编辑框架加载jar中的class,加载后返回类名 + */ + abstract fun loadClassFromJar(zipInputStream: ZipInputStream): String - override fun getScopes(): MutableSet = TransformManager.SCOPE_FULL_PROJECT + abstract fun onTransform() /** * 每一次执行transform前调用。在一次构建中可能有多个Variant,多个Variant会共用同一个 * Transform对象(就是这个类的对象)。在这里提供一个时机清理transform过程中产生的缓存, * 避免对下一次transform产生影响。 */ - open fun beforeTransform(invocation: TransformInvocation) { - invocation.outputProvider.deleteAll() - inputSet.clear() - } - - open fun afterTransform(invocation: TransformInvocation) { - } - - final override fun transform(invocation: TransformInvocation) { - beforeTransform(invocation) - input(invocation.inputs, invocation.outputProvider) - onTransform() - output(invocation.outputProvider) - afterTransform(invocation) - } - - override fun getSecondaryFiles(): ImmutableList? { - val transformJar = File(this::class.java.protectionDomain.codeSource.location.toURI()) - val transformKitJar = - File(ClassTransform::class.java.protectionDomain.codeSource.location.toURI()) - - return ImmutableList.of( - //将当前类运行所在的jar本身作为转换输入的SecondaryFiles,也就作为了这个transform task的inputs的 - //一部分,这使得当这个Transform程序变化时,构建能检测到这个Transform需要重新执行。这是直接编辑这个 - //Transform源码后,应用了这个Plugin的debug工程能直接生效的关键。 - SecondaryFile.nonIncremental(project.files(transformJar)), - SecondaryFile.nonIncremental(project.files(transformKitJar)) - ) - } -} -typealias ClassName_OutputFile = Pair -typealias ClassName_EntryName = Pair - -abstract class InputClass() { - abstract fun renameOutput(oldName: String, newName: String) -} - -class DirInputClass() : InputClass() { - private val outputs = mutableMapOf() - fun getOutput(): Set { - val mutableSet = mutableSetOf() - return outputs.mapTo(mutableSet) { - ClassName_OutputFile(it.key, it.value) - } - } - - fun addOutput(className: String, file: File) { - outputs[className] = file - } - - fun getOutput(className: String): File { - return outputs[className]!! + open fun beforeTransform() { } - override fun renameOutput(oldName: String, newName: String) { - val file = outputs.remove(oldName)!! - val newFileName = file.name.replace(getSimpleName(oldName), getSimpleName(newName)) - outputs[newName] = File(file.parent, newFileName) + open fun afterTransform() { } - private fun getSimpleName(name: String): String { - val i = name.lastIndexOf('.') - if (i == -1) { - return name - } else { - return name.substring(i + 1) - } - } } -class JarInputClass() : InputClass() { - private val outputs = mutableMapOf() - fun getOutput(): Set { - val mutableSet = mutableSetOf() - return outputs.mapTo(mutableSet) { - ClassName_EntryName(it.key, it.value) - } - } - - fun addOutput(className: String, entryName: String) { - outputs[className] = entryName - } - - fun getOutput(className: String): String { - return outputs[className]!! - } - - override fun renameOutput(oldName: String, newName: String) { - outputs[newName] = newName.replace('.', '/') + ".class" - } - -} - - -class TransformInput( - val name: String, - val contentTypes: Set, - val scopes: MutableSet, - val format: Format -) { - constructor(di: DirectoryInput) : this( - di.name, di.contentTypes, di.scopes, Format.DIRECTORY - ) - - constructor(ji: JarInput) : this( - ji.name, ji.contentTypes, ji.scopes, Format.JAR - ) - - private val inputClassSet = mutableSetOf() - - fun addInputClass(inputClass: InputClass) { - inputClassSet.add(inputClass) - } +/** + * 输入数据的封装 + * 外部Transform框架的适配器DeprecatedTransformWrapper或者GradleTransformWrapper + * 把它们的输入封装成这个抽象类的子类 + */ +abstract class TransformInput { + /** + * 一个TransformInput可能是Dir包含多个class文件,也可能是一个jar包包含多个class文件。 + * 这里把它们记下来,等输出的时候要按这个名单输出 + */ + val inputClassNames = mutableSetOf() - fun getInputClass() = inputClassSet.toSet() + enum class Kind { DIRECTORY, JAR } - fun toOutput(outputProvider: TransformOutputProvider) = - outputProvider.getContentLocation( - name, - contentTypes, - scopes, - format - ) + abstract val kind: Kind + abstract fun asFile(): File } diff --git a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/JavassistTransform.kt b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/JavassistTransform.kt index e25b42af3..53d302f34 100644 --- a/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/JavassistTransform.kt +++ b/projects/sdk/core/transform-kit/src/main/kotlin/com/tencent/shadow/core/transform_kit/JavassistTransform.kt @@ -18,7 +18,6 @@ package com.tencent.shadow.core.transform_kit -import com.android.build.api.transform.TransformInvocation import javassist.ClassPool import javassist.CtClass import org.gradle.api.Project @@ -28,30 +27,35 @@ import java.util.zip.ZipInputStream open class JavassistTransform(project: Project, val classPoolBuilder: ClassPoolBuilder) : ClassTransform(project) { - val mCtClassInputMap = mutableMapOf() + /** + * 所有classPool.makeClass生成的CtClass存起来, + * 等AbstractTransformManager处理类时直接从这里 + * 拿到所有CtClass作为字节码编辑的输入数据。 + */ + val allInputCtClass = mutableSetOf() lateinit var classPool: ClassPool - override fun onOutputClass(entryName: String?, className: String, outputStream: OutputStream) { + override fun onOutputClass(className: String, outputStream: OutputStream) { classPool[className].writeOut(outputStream) } - override fun DirInputClass.onInputClass(classFile: File, outputFile: File) { - classFile.inputStream().use { - val ctClass: CtClass = classPool.makeClass(it) - addOutput(ctClass.name, outputFile) - mCtClassInputMap[ctClass] = this + override fun loadDotClassFile(classFile: File): String { + val ctClass: CtClass = classFile.inputStream().use { + classPool.makeClass(it) } + allInputCtClass.add(ctClass) + return ctClass.name } - override fun JarInputClass.onInputClass(zipInputStream: ZipInputStream, entryName: String) { + override fun loadClassFromJar(zipInputStream: ZipInputStream): String { val ctClass = classPool.makeClass(zipInputStream) - addOutput(ctClass.name, entryName) - mCtClassInputMap[ctClass] = this + allInputCtClass.add(ctClass) + return ctClass.name } - override fun beforeTransform(invocation: TransformInvocation) { - super.beforeTransform(invocation) - mCtClassInputMap.clear() + override fun beforeTransform() { + super.beforeTransform() + allInputCtClass.clear() classPool = classPoolBuilder.build() } diff --git a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/DeprecatedTransformWrapper.kt b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/DeprecatedTransformWrapper.kt new file mode 100644 index 000000000..539f4cfba --- /dev/null +++ b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/DeprecatedTransformWrapper.kt @@ -0,0 +1,157 @@ +@file:Suppress("DEPRECATION") + +package com.tencent.shadow.core.transform + +import com.android.build.api.transform.DirectoryInput +import com.android.build.api.transform.Format +import com.android.build.api.transform.JarInput +import com.android.build.api.transform.QualifiedContent +import com.android.build.api.transform.SecondaryFile +import com.android.build.api.transform.Transform +import com.android.build.api.transform.TransformInvocation +import com.android.build.api.transform.TransformOutputProvider +import com.android.build.api.variant.VariantInfo +import com.android.build.gradle.internal.pipeline.TransformManager +import com.google.common.collect.ImmutableList +import com.google.common.io.Files +import com.tencent.shadow.core.transform_kit.ClassTransform +import com.tencent.shadow.core.transform_kit.TransformInput +import org.gradle.api.Project +import java.io.File +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +/** + * 适配ClassTransform到AGP 8之前的Transform API + * 这是最初开发的、长期使用的比较稳定的实现。因为AGP 8去掉了这个API,所以不得不做两种适配。 + */ +class DeprecatedTransformWrapper( + val project: Project, val classTransform: ClassTransform +) : Transform() { + override fun getName(): String = "ShadowTransform" + + override fun getInputTypes(): MutableSet = + TransformManager.CONTENT_CLASS + + override fun isIncremental(): Boolean = false + + override fun isCacheable(): Boolean { + return true + } + + override fun applyToVariant(variant: VariantInfo): Boolean { + return if (variant.isTest) false + else variant.flavorNames.contains(ShadowTransform.ApplyShadowTransformFlavorName) + } + + override fun getScopes(): MutableSet = + TransformManager.SCOPE_FULL_PROJECT + + override fun transform(invocation: TransformInvocation) { + //before Transform clean output + val outputProvider = invocation.outputProvider + outputProvider.deleteAll() + + val inputs: List = invocation.inputs.flatMap { transformInput -> + transformInput.directoryInputs.map { + TransformInputImpl(it) + } + transformInput.jarInputs.map { + TransformInputImpl(it) + } + } + + classTransform.beforeTransform() + classTransform.input(inputs) + classTransform.onTransform() + output(inputs, outputProvider) + classTransform.afterTransform() + } + + /** + * 这个旧版Transform API需要把DIR里的class和jar里的class分别输出到DIR和对应的jar里, + * 这个行为是API决定的,不是通用的,因此要写在这里。对于ClassTransform来说,唯一可以复用的 + * 就是把class输出到OutputStream中。 + */ + private fun output(inputs: Iterable, outputProvider: TransformOutputProvider) { + inputs.forEach { transformInput -> + transformInput as TransformInputImpl + + val outputLocation = outputProvider.getContentLocation( + transformInput.name, + transformInput.contentTypes, + transformInput.scopes, + transformInput.format + ) + + when (transformInput.kind) { + TransformInput.Kind.DIRECTORY -> { + transformInput.inputClassNames.forEach { className -> + val relativePath = className.replace('.', File.separatorChar) + ".class" + val outputClassFile = File(outputLocation, relativePath) + Files.createParentDirs(outputClassFile) + FileOutputStream(outputClassFile).use { + classTransform.onOutputClass(className, it) + } + } + } + + TransformInput.Kind.JAR -> { + Files.createParentDirs(outputLocation) + ZipOutputStream(FileOutputStream(outputLocation)).use { zos -> + transformInput.inputClassNames.forEach { className -> + val entryName = className.replace('.', '/') + ".class" + zos.putNextEntry(ZipEntry(entryName)) + classTransform.onOutputClass(className, zos) + } + } + } + } + } + } + + + override fun getSecondaryFiles(): ImmutableList? { + val transformJar = File(this::class.java.protectionDomain.codeSource.location.toURI()) + val transformKitJar = + File(ClassTransform::class.java.protectionDomain.codeSource.location.toURI()) + + return ImmutableList.of( + //将当前类运行所在的jar本身作为转换输入的SecondaryFiles,也就作为了这个transform task的inputs的 + //一部分,这使得当这个Transform程序变化时,构建能检测到这个Transform需要重新执行。这是直接编辑这个 + //Transform源码后,应用了这个Plugin的debug工程能直接生效的关键。 + SecondaryFile.nonIncremental(project.files(transformJar)), + SecondaryFile.nonIncremental(project.files(transformKitJar)) + ) + } + + private class TransformInputImpl( + val file: File, + val name: String, + val contentTypes: Set, + val scopes: MutableSet, + val format: Format, + override val kind: Kind, + ) : TransformInput() { + constructor(di: DirectoryInput) : this( + di.file, + di.name, + di.contentTypes, + di.scopes, + Format.DIRECTORY, + Kind.DIRECTORY + ) + + constructor(ji: JarInput) : this( + ji.file, + ji.name, + ji.contentTypes, + ji.scopes, + Format.JAR, + Kind.JAR + ) + + override fun asFile(): File = file + + } +} \ No newline at end of file diff --git a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt new file mode 100644 index 000000000..5f7e9163c --- /dev/null +++ b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/GradleTransformWrapper.kt @@ -0,0 +1,80 @@ +package com.tencent.shadow.core.transform + +import com.tencent.shadow.core.transform_kit.ClassTransform +import com.tencent.shadow.core.transform_kit.TransformInput +import org.gradle.api.DefaultTask +import org.gradle.api.file.Directory +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream +import java.util.jar.JarOutputStream +import java.util.zip.ZipEntry +import javax.inject.Inject + +/** + * 适配AGP 7.2引入的新的Transform API,AGP没给这个API特别命名,但我们知道它是Gradle直接提供的Transform + * 接口。与之对应的是DeprecatedTransformWrapper适配旧的Transform API接口。 + */ +abstract class GradleTransformWrapper @Inject constructor(@Internal val classTransform: ClassTransform) : + DefaultTask() { + // This property will be set to all Jar files available in scope + @get:InputFiles + abstract val allJars: ListProperty + + // Gradle will set this property with all class directories that available in scope + @get:InputFiles + abstract val allDirectories: ListProperty + + // Task will put all classes from directories and jars after optional modification into single jar + @get:OutputFile + abstract val output: RegularFileProperty + + @TaskAction + fun taskAction() { + val inputs: List = allDirectories.get().map { + TransformInputImpl(it.asFile, TransformInput.Kind.DIRECTORY) + } + allJars.get().map { + TransformInputImpl(it.asFile, TransformInput.Kind.JAR) + } + + classTransform.beforeTransform() + classTransform.input(inputs) + classTransform.onTransform() + output(inputs) + classTransform.afterTransform() + } + + private fun output(inputs: Iterable) { + val jarOutput = JarOutputStream( + BufferedOutputStream(FileOutputStream(output.get().asFile)) + ) + jarOutput.use { + val outputClassNames = inputs.flatMap { + it.inputClassNames + } + + outputClassNames.forEach { className -> + val entryName = className.replace('.', '/') + ".class" + jarOutput.putNextEntry(ZipEntry(entryName)) + classTransform.onOutputClass(className, jarOutput) + } + } + } + + private class TransformInputImpl( + val file: File, + override val kind: Kind + ) : TransformInput() { + + override fun asFile(): File = file + + } +} + diff --git a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/ShadowTransform.kt b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/ShadowTransform.kt index 5d9f098b6..f235f49c3 100644 --- a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/ShadowTransform.kt +++ b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/ShadowTransform.kt @@ -18,8 +18,6 @@ package com.tencent.shadow.core.transform -import com.android.build.api.transform.TransformInvocation -import com.android.build.api.variant.VariantInfo import com.tencent.shadow.core.transform_kit.AbstractTransform import com.tencent.shadow.core.transform_kit.AbstractTransformManager import com.tencent.shadow.core.transform_kit.ClassPoolBuilder @@ -43,20 +41,10 @@ class ShadowTransform( override val mTransformManager: AbstractTransformManager get() = _mTransformManager - override fun beforeTransform(invocation: TransformInvocation) { - super.beforeTransform(invocation) - _mTransformManager = TransformManager(mCtClassInputMap, classPool, useHostContext) + override fun beforeTransform() { + super.beforeTransform() + _mTransformManager = TransformManager(classPool, useHostContext) classPool.makeInterface(SelfClassNamePlaceholder) } - override fun isCacheable(): Boolean { - return true - } - - override fun getName(): String = "ShadowTransform" - - override fun applyToVariant(variant: VariantInfo): Boolean { - return if (variant.isTest) false - else variant.flavorNames.contains(ApplyShadowTransformFlavorName) - } } \ No newline at end of file diff --git a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/TransformManager.kt b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/TransformManager.kt index 3e3622073..d0790952a 100644 --- a/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/TransformManager.kt +++ b/projects/sdk/core/transform/src/main/kotlin/com/tencent/shadow/core/transform/TransformManager.kt @@ -18,18 +18,30 @@ package com.tencent.shadow.core.transform -import com.tencent.shadow.core.transform.specific.* +import com.tencent.shadow.core.transform.specific.ActivityOptionsSupportTransform +import com.tencent.shadow.core.transform.specific.ActivityTransform +import com.tencent.shadow.core.transform.specific.AppComponentFactoryTransform +import com.tencent.shadow.core.transform.specific.ApplicationTransform +import com.tencent.shadow.core.transform.specific.ContentProviderTransform +import com.tencent.shadow.core.transform.specific.DialogSupportTransform +import com.tencent.shadow.core.transform.specific.FragmentSupportTransform +import com.tencent.shadow.core.transform.specific.InstrumentationTransform +import com.tencent.shadow.core.transform.specific.IntentServiceTransform +import com.tencent.shadow.core.transform.specific.KeepHostContextTransform +import com.tencent.shadow.core.transform.specific.LayoutInflaterTransform +import com.tencent.shadow.core.transform.specific.PackageItemInfoTransform +import com.tencent.shadow.core.transform.specific.PackageManagerTransform +import com.tencent.shadow.core.transform.specific.ReceiverSupportTransform +import com.tencent.shadow.core.transform.specific.ServiceTransform +import com.tencent.shadow.core.transform.specific.WebViewTransform import com.tencent.shadow.core.transform_kit.AbstractTransformManager -import com.tencent.shadow.core.transform_kit.InputClass import com.tencent.shadow.core.transform_kit.SpecificTransform import javassist.ClassPool -import javassist.CtClass class TransformManager( - ctClassInputMap: Map, classPool: ClassPool, useHostContext: () -> Array -) : AbstractTransformManager(ctClassInputMap, classPool) { +) : AbstractTransformManager(classPool) { /** * 按这个列表的顺序应用各子Transform逻辑。 diff --git a/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt b/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt index 8eabf8b27..fef10e0ab 100644 --- a/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt +++ b/projects/sdk/core/transform/src/test/kotlin/com/tencent/shadow/core/transform/specific/ReceiverSupportTransformTest.kt @@ -43,7 +43,16 @@ class ReceiverSupportTransformTest : AbstractTransformTest() { val ctClass = dLoader[testClassName] val loader = Loader(this.javaClass.classLoader, dLoader) loader.delegateLoadingOf("android.content.") - val clazz = ctClass.toClass(loader) + val clazz = try { + loader.loadClass(testClassName) + } catch (e: ClassNotFoundException) { + ctClass.classPool.toClass( + ctClass, + test.TestActivity::class.java, + loader, + test.TestActivity::class.java.protectionDomain + ) + } //构造实例,调用onReceive方法,检查log记录的字符串List是否符合预期 val constructor = clazz.getDeclaredConstructor(List::class.java) diff --git a/projects/sdk/dynamic/build.gradle b/projects/sdk/dynamic/build.gradle index 9e1cabf18..da5576914 100644 --- a/projects/sdk/dynamic/build.gradle +++ b/projects/sdk/dynamic/build.gradle @@ -8,7 +8,8 @@ buildscript { def versions_properties_path = '../../../buildScripts/gradle/versions.properties' def versions = new Properties() versions.load(file(versions_properties_path).newReader()) - versions.forEach { key, value -> + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue ext.set(key, value) } } diff --git a/projects/sdk/dynamic/gradle/wrapper/gradle-wrapper.properties b/projects/sdk/dynamic/gradle/wrapper/gradle-wrapper.properties index b21bddc71..4126a5d0d 100644 --- a/projects/sdk/dynamic/gradle/wrapper/gradle-wrapper.properties +++ b/projects/sdk/dynamic/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -#distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip -distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.0.2-bin.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/projects/test/gradle-plugin-agp-compat-test/README.md b/projects/test/gradle-plugin-agp-compat-test/README.md index 8de94b2a2..5dead02cf 100644 --- a/projects/test/gradle-plugin-agp-compat-test/README.md +++ b/projects/test/gradle-plugin-agp-compat-test/README.md @@ -2,10 +2,14 @@ 准备一个桩工程`stub-project`,通过命令行参数控制其AGP版本和Shadow版本。 -自动化测试脚本: `test.sh`。其中先编译Shadow,发布到本地Maven,然后用这个Shadow版本进行测试。 +自动化测试脚本: `test_JDK11.sh`和`test_JDK17.sh`。其中先编译Shadow,发布到本地Maven,然后用这个Shadow版本进行测试。 注意脚本会echo出执行的命令,如果遇到测试失败,可复制命令手工重新执行。 +`test_JDK11.sh`需要JDK 11环境,`test_JDK17.sh`需要JDK 17环境。 + +`test_clean.sh`可以还原测试脚本对Gradle文件对改动。 + ### 确定实际使用的AGP版本: 查看`stub-project/build/intermediates/app_metadata/pluginDebug/app-metadata.properties` diff --git a/projects/test/gradle-plugin-agp-compat-test/gradle/wrapper/gradle-wrapper.properties b/projects/test/gradle-plugin-agp-compat-test/gradle/wrapper/gradle-wrapper.properties index b21bddc71..4126a5d0d 100644 --- a/projects/test/gradle-plugin-agp-compat-test/gradle/wrapper/gradle-wrapper.properties +++ b/projects/test/gradle-plugin-agp-compat-test/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -#distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip -distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.0.2-bin.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://mirrors.tencent.com/gradle/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle b/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle index 826b38448..ff6c437b4 100644 --- a/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle +++ b/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle @@ -1,4 +1,14 @@ buildscript { + loadVersions: + {// 读取versions.properties到ext中,供项目中直接用变量引用版本号 + def versions_properties_path = '../../../../buildScripts/gradle/versions.properties' + def versions = new Properties() + versions.load(file(versions_properties_path).newReader()) + versions.forEach { key, stringValue -> + def value = stringValue?.isInteger() ? stringValue as Integer : stringValue + ext.set(key, value) + } + } repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } @@ -18,11 +28,6 @@ apply plugin: 'com.android.application' apply plugin: 'com.tencent.shadow.plugin' allprojects { - ext.COMPILE_SDK_VERSION = 29 - ext.MIN_SDK_VERSION = 16 - ext.TARGET_SDK_VERSION = 28 - ext.VERSION_CODE = 1 - ext.VERSION_NAME = "local" repositories { if (!System.getenv().containsKey("DISABLE_TENCENT_MAVEN_MIRROR")) { maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public/' } @@ -36,6 +41,12 @@ allprojects { ext.disable_shadow_transform = true android { + try { + namespace 'app' + } catch (Exception ignored) { + // AGP 8之前找不到namespace这个方法的,不用设置。 + } + compileSdkVersion COMPILE_SDK_VERSION defaultConfig { diff --git a/projects/test/gradle-plugin-agp-compat-test/test_JDK11.sh b/projects/test/gradle-plugin-agp-compat-test/test_JDK11.sh new file mode 100755 index 000000000..f49475796 --- /dev/null +++ b/projects/test/gradle-plugin-agp-compat-test/test_JDK11.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Shadow工程因需要支持更高版本AGP,本身也需要用更高版本AGP构建。从AGP 8开始要求用JDK 17了。 +# 但使用Shadow的工程应该可以使用JDK 11。此脚本需要在JDK 11下测试低版本AGP工程的兼容性。 + +JAVA_MAJOR_VERSION=$(javap -verbose java.lang.Object | grep "major version" | cut -d " " -f5) +if [[ $JAVA_MAJOR_VERSION -ne 55 ]]; then + echo "需要JDK 11!" + exit 1 +fi + +source ./test_prepare.sh + +# 测试版本来源 +# AGP release页面:https://developer.android.com/studio/releases/gradle-plugin +# AGP Maven仓库:https://mvnrepository.com/artifact/com.android.tools.build/gradle +# Gradle下载:https://services.gradle.org/distributions/ +setGradleVersion 7.5 +testUnderAGPVersion 7.4.1 +setGradleVersion 7.4 +testUnderAGPVersion 7.3.1 +setGradleVersion 7.3.3 +testUnderAGPVersion 7.2.2 +setGradleVersion 7.2 +testUnderAGPVersion 7.1.1 +setGradleVersion 7.0.2 +testUnderAGPVersion 7.0.0 +testUnderAGPVersion 4.2.0 +testUnderAGPVersion 4.1.0 +setGradleVersion 6.1.1 +testUnderAGPVersion 4.0.0 +setGradleVersion 5.6.4 +testUnderAGPVersion 3.6.0 +setGradleVersion 5.4.1 +testUnderAGPVersion 3.5.0 +testUnderAGPVersion 3.4.0 +testUnderAGPVersion 3.3.0 +testUnderAGPVersion 3.2.0 + +# 3.1.0在配合aapt2使用--package-id选项时不能设置小于0x7f的值,因此不再支持 +#testUnderAGPVersion 3.1.0 + +#更低的版本支持成本过高 diff --git a/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh b/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh new file mode 100755 index 000000000..e59a95d99 --- /dev/null +++ b/projects/test/gradle-plugin-agp-compat-test/test_JDK17.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# 从Gradle 7.5和AGP 8.0开始支持和要求使用JDK 17,所以这个脚本中的待测版本需要在JDK 17环境下测试。 + +JAVA_MAJOR_VERSION=$(javap -verbose java.lang.Object | grep "major version" | cut -d " " -f5) +if [[ $JAVA_MAJOR_VERSION -ne 61 ]]; then + echo "需要JDK 17!" + exit 1 +fi + +source ./test_prepare.sh + +# 测试版本来源 +# AGP release页面:https://developer.android.com/studio/releases/gradle-plugin +# AGP Maven仓库:https://mvnrepository.com/artifact/com.android.tools.build/gradle +# Gradle下载:https://services.gradle.org/distributions/ +setGradleVersion 8.4 +testUnderAGPVersion 8.3.0-alpha16 +setGradleVersion 8.2.1 +testUnderAGPVersion 8.2.0 +setGradleVersion 8.0.2 +testUnderAGPVersion 8.1.4 +testUnderAGPVersion 8.0.2 +setGradleVersion 7.5.1 +testUnderAGPVersion 7.4.1 diff --git a/projects/test/gradle-plugin-agp-compat-test/test_clean.sh b/projects/test/gradle-plugin-agp-compat-test/test_clean.sh new file mode 100755 index 000000000..6897dd907 --- /dev/null +++ b/projects/test/gradle-plugin-agp-compat-test/test_clean.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eo pipefail + +git restore gradle/wrapper gradlew gradlew.bat +git clean -fdx . \ No newline at end of file diff --git a/projects/test/gradle-plugin-agp-compat-test/test.sh b/projects/test/gradle-plugin-agp-compat-test/test_prepare.sh similarity index 57% rename from projects/test/gradle-plugin-agp-compat-test/test.sh rename to projects/test/gradle-plugin-agp-compat-test/test_prepare.sh index 1f7db5230..812ea05d8 100755 --- a/projects/test/gradle-plugin-agp-compat-test/test.sh +++ b/projects/test/gradle-plugin-agp-compat-test/test_prepare.sh @@ -26,32 +26,3 @@ function testUnderAGPVersion() { echo ./gradlew -PSetGradleVersion=false -PTestAGPVersion=$TestAGPVersion -PShadowVersion=$ShadowVersion :stub-project:assemblePluginA1B2Debug ./gradlew -PSetGradleVersion=false -PTestAGPVersion=$TestAGPVersion -PShadowVersion=$ShadowVersion :stub-project:assemblePluginA1B2Debug } - -# 测试版本来源 -# https://developer.android.com/studio/releases/gradle-plugin -setGradleVersion 7.5 -testUnderAGPVersion 7.4.1 -setGradleVersion 7.4 -testUnderAGPVersion 7.3.1 -setGradleVersion 7.3.3 -testUnderAGPVersion 7.2.2 -setGradleVersion 7.2 -testUnderAGPVersion 7.1.1 -setGradleVersion 7.0.2 -testUnderAGPVersion 7.0.0 -testUnderAGPVersion 4.2.0 -testUnderAGPVersion 4.1.0 -setGradleVersion 6.1.1 -testUnderAGPVersion 4.0.0 -setGradleVersion 5.6.4 -testUnderAGPVersion 3.6.0 -setGradleVersion 5.4.1 -testUnderAGPVersion 3.5.0 -testUnderAGPVersion 3.4.0 -testUnderAGPVersion 3.3.0 -testUnderAGPVersion 3.2.0 - -# 3.1.0在配合aapt2使用--package-id选项时不能设置小于0x7f的值,因此不再支持 -#testUnderAGPVersion 3.1.0 - -#更低的版本支持成本过高