diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1efc5d9..7d75f5c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,5 +1,3 @@
-# https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions
-# Renaming ? Change the README badge.
name: Build
on:
push:
@@ -11,9 +9,14 @@ jobs:
name: Base Checks
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
with:
- java-version: 1.8
- - name: Perform base checks
- run: ./gradlew build publishToDirectory
\ No newline at end of file
+ java-version: 17
+ distribution: temurin
+ cache: gradle
+ - uses: gradle/actions/wrapper-validation@v4
+ - name: Check local deployment
+ run: ./gradlew build deployLocal
+ - name: Check sample app
+ run: cd tests && ../gradlew sample-library:assembleDebug
\ No newline at end of file
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 436687d..ed70656 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -3,17 +3,24 @@ on:
release:
types: [published]
jobs:
- BINTRAY_UPLOAD:
- name: Bintray Upload
+ DEPLOY:
+ name: GitHub and Maven Central publication
runs-on: ubuntu-latest
env:
- BINTRAY_USER: ${{ secrets.BINTRAY_USER }}
- BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
- BINTRAY_REPO: ${{ secrets.BINTRAY_REPO }}
+ SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
+ SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
+ SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
+ SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ GHUB_USER: ${{ secrets.GHUB_USER }}
+ GHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GHUB_PERSONAL_ACCESS_TOKEN }}
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
with:
- java-version: 1.8
- - name: Publish to Bintray
- run: ./gradlew publishToBintray
+ java-version: 17
+ distribution: temurin
+ cache: gradle
+ - name: Publish to Maven Central
+ run: ./gradlew deployNexus
+ - name: Publish to GitHub Packages
+ run: ./gradlew deployGithub
\ No newline at end of file
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
new file mode 100644
index 0000000..24603f3
--- /dev/null
+++ b/.github/workflows/snapshot.yml
@@ -0,0 +1,23 @@
+name: Snapshot
+on:
+ push:
+ branches:
+ - main
+jobs:
+ SNAPSHOT:
+ name: Publish Snapshot
+ runs-on: ubuntu-latest
+ env:
+ SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
+ SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
+ SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
+ SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ java-version: 17
+ distribution: temurin
+ cache: gradle
+ - name: Publish Nexus Snapshot
+ run: ./gradlew deployNexusSnapshot
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 603b140..cbef6a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,7 @@
*.iml
.gradle
-/local.properties
-/.idea/caches
-/.idea/libraries
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-/.idea/assetWizardSettings.xml
+**/local.properties
+**/.idea/
.DS_Store
/build
/captures
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 976bc20..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- xmlns:android
-
- ^$
-
-
-
-
-
-
-
-
- xmlns:.*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*:id
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- .*:name
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- name
-
- ^$
-
-
-
-
-
-
-
-
- style
-
- ^$
-
-
-
-
-
-
-
-
- .*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*
-
- http://schemas.android.com/apk/res/android
-
-
- ANDROID_ATTRIBUTE_ORDER
-
-
-
-
-
-
- .*
-
- .*
-
-
- BY_NAME
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 79ee123..0000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 61a9130..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 02cb845..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
deleted file mode 100644
index 1d6cae2..0000000
--- a/.idea/jarRepositories.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/kotlinScripting.xml b/.idea/kotlinScripting.xml
deleted file mode 100644
index a6fe551..0000000
--- a/.idea/kotlinScripting.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/markdown-navigator-enh.xml b/.idea/markdown-navigator-enh.xml
deleted file mode 100644
index 12fb99d..0000000
--- a/.idea/markdown-navigator-enh.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml
deleted file mode 100644
index 4463382..0000000
--- a/.idea/markdown-navigator.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 3e91cc6..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1.8
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/Grease_grease__publishAllDirectory_.xml b/.idea/runConfigurations/Grease_grease__publishAllDirectory_.xml
deleted file mode 100644
index 373b61a..0000000
--- a/.idea/runConfigurations/Grease_grease__publishAllDirectory_.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0e57478
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2024 DeepMedia Srl
+
+ 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.
\ No newline at end of file
diff --git a/README.md b/README.md
index 2c1486f..66c3381 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,30 @@
-[](https://github.com/deepmedia/Grease/actions)
+[](https://github.com/deepmedia/Grease/actions)
[](https://github.com/deepmedia/Grease/releases)
[](https://github.com/deepmedia/Grease/issues)
# Grease
A Gradle plugin for creating fat AARs, useful for distributing multiple modules in a single file.
-To install the plugin, you must configure the build script classpath:
+To install the plugin, apply it to your Android project:
```kotlin
-buildscript {
+// settings.gradle.kts
+pluginManagement {
repositories {
- jcenter()
- google()
- }
- dependencies {
- classpath("io.deepmedia.tools:grease:0.2.0")
+ mavenCentral()
}
}
-```
-## Usage
+// build.gradle.kts
+plugins {
+ id("com.android.library")
+ id("io.deepmedia.tools.grease") version "0.3.0"
+}
+```
-To apply the plugin, declare it in your build script with the `io.deepmedia.tools.grease` id.
-This must be done after the com.android.library plugin:
+Note: it is important that Grease is applied *after* the Android library plugin.
-```groovy
-apply plugin: 'com.android.library'
-apply plugin: 'io.deepmedia.tools.grease'
-```
+## Usage
Once applied, Grease will create Gradle configurations that you can use to select which of your
dependencies should be bundled in the final fat AAR.
@@ -61,19 +58,21 @@ dependencies that you own, to be sure that they won't be present in the classpat
### Transitivity
-When you add a grease dependency, by default all transitive dependencies are greased as well, so
-they will become part of the fat AARs. To avoid this, you can mark the configuration as non transitive:
+By default, declaring a specific grease dependency **will not** include transitive dependencies.
+To do so, you can use grease configurations ending in `Tree`:
```kotlin
-configurations["grease"].isTransitive = false
-configurations["greaseRelease"].isTransitive = false
-configurations["greaseDebug"].isTransitive = false
-
-// Variant specific configurations are created lazily so you must wait for them to be
-// available before modifying them.
-configurations.configureEach {
- if (name == "greaseBlueCircleDebug") {
- isTransitive = false
- }
+dependencies {
+ grease("my-package:artifact:1.0.0") // not transitive
+ greaseTree("my-other-package:other-artifact:1.0.0") // transitive
}
-```
\ No newline at end of file
+```
+
+### Relocations
+
+We support relocations through the popular [Shadow](https://github.com/GradleUp/shadow) plugin. Inside the `grease`
+extension, you can use:
+
+- `relocate(prefix: String)` to relocate all included packages by prefixing them with the given prefix. Defaults to `"grease"`.
+- `relocate(from: String, to: String, configure: Action)` to register a package-specific relocator,
+ as described in the [shadow plugin docs](https://gradleup.com/shadow/configuration/relocation/#relocating-packages)
diff --git a/build.gradle.kts b/build.gradle.kts
index d664f12..e20df22 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,31 +1,4 @@
-buildscript {
- repositories {
- maven("build/maven")
- maven("https://dl.bintray.com/deepmedia/tools/")
- google()
- jcenter()
- }
-
- configurations.configureEach {
- resolutionStrategy.cacheChangingModulesFor(10, TimeUnit.SECONDS)
- }
-
- dependencies {
- classpath("io.deepmedia.tools:publisher:0.4.0")
- classpath("io.deepmedia.tools:grease:0.2.0") {
- isChanging = true
- }
- }
-}
-
-allprojects {
- repositories {
- mavenCentral()
- google()
- jcenter()
- }
-}
-
-tasks.register("clean", Delete::class) {
- delete(buildDir)
-}
+plugins {
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.kotlin.jvm) apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 23339e0..83df5e3 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,7 +15,5 @@ org.gradle.jvmargs=-Xmx1536m
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..6d8fa58
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,23 @@
+[versions]
+agp = "8.1.4"
+asm-commons = "9.6"
+android-tools = "31.1.4"
+kotlin = "2.0.0"
+shadow = "8.3.0"
+publisher = "0.14.0"
+
+[libraries]
+asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm-commons" }
+gradle-android-common = { module = "com.android.tools:common", version.ref = "android-tools" }
+gradle-android-sdk-common = { module = "com.android.tools:sdk-common", version.ref = "android-tools" }
+gradle-android-layoutlib = { module = "com.android.tools.layoutlib:layoutlib-api", version.ref = "android-tools" }
+gradle-android-build = { module = "com.android.tools.build:gradle", version.ref = "agp" }
+gradle-shadow = { module = "com.gradleup.shadow:shadow-gradle-plugin", version.ref = "shadow" }
+
+[bundles]
+gradle-android = ["gradle-android-sdk-common", "gradle-android-build", "gradle-android-common", "gradle-android-layoutlib"]
+
+[plugins]
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+publisher = { id = "io.deepmedia.tools.deployer", version.ref = "publisher" }
+android-library = { id = "com.android.library", version.ref = "agp" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index f6b961f..2c35211 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e47231f..8838ba9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Wed Mar 18 11:56:36 BRT 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
diff --git a/gradlew b/gradlew
index cccdd3d..f5feea6 100755
--- a/gradlew
+++ b/gradlew
@@ -1,78 +1,130 @@
-#!/usr/bin/env sh
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+# https://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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,92 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=$((i+1))
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
fi
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index e95643d..9b42019 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,4 +1,22 @@
-@if "%DEBUG%" == "" @echo off
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -9,25 +27,29 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -35,48 +57,36 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/grease/build.gradle.kts b/grease/build.gradle.kts
index bc2aacc..1f7f6ab 100644
--- a/grease/build.gradle.kts
+++ b/grease/build.gradle.kts
@@ -1,39 +1,78 @@
-import io.deepmedia.tools.publisher.common.*
-
plugins {
`kotlin-dsl`
- id("io.deepmedia.tools.publisher")
+ alias(libs.plugins.publisher)
+}
+
+group = "io.deepmedia.tools"
+version = "0.3.0"
+
+gradlePlugin {
+ plugins {
+ create("grease") {
+ id = "io.deepmedia.tools.grease"
+ implementationClass = "io.deepmedia.tools.grease.GreasePlugin"
+ }
+ }
}
dependencies {
- api("com.android.tools.build:gradle:4.1.1") // android gradle plugin
- api(gradleApi()) // gradle
- api(gradleKotlinDsl()) // not sure if needed
- api(localGroovy()) // groovy
+ implementation(libs.asm.commons)
+ implementation(libs.gradle.shadow)
+ implementation(libs.bundles.gradle.android)
}
-publisher {
- project.name = "Grease"
- project.artifact = "grease"
- project.description = "Fat AARs for Android."
- project.group = "io.deepmedia.tools"
- project.url = "https://github.com/deepmedia/Grease"
- project.vcsUrl = "https://github.com/deepmedia/Grease.git"
- release.version = "0.2.0"
- release.sources = Release.SOURCES_AUTO
- release.docs = Release.DOCS_AUTO
+deployer {
+ content {
+ gradlePluginComponents {
+ kotlinSources()
+ emptyDocs()
+ }
+ }
+
+ projectInfo {
+ description = "Fat AARs for Android, to distribute multiple library modules in a single file with no dependencies, with relocation support."
+ url = "https://github.com/deepmedia/Grease"
+ scm.fromGithub("deepmedia", "Grease")
+ developer("Mattia Iavarone", "mattia@deepmedia.io", "DeepMedia", "https://deepmedia.io")
+ license(apache2)
+ }
+
+ signing {
+ key = secret("SIGNING_KEY")
+ password = secret("SIGNING_PASSWORD")
+ }
+
+ // use "deployLocal" to deploy to local maven repository
+ localSpec {
+ directory.set(rootProject.layout.buildDirectory.get().dir("inspect"))
+ signing {
+ key = absent()
+ password = absent()
+ }
+ }
- bintray {
- auth.user = "BINTRAY_USER"
- auth.key = "BINTRAY_KEY"
- auth.repo = "BINTRAY_REPO"
+ // use "deployNexus" to deploy to OSSRH / maven central
+ nexusSpec {
+ auth.user = secret("SONATYPE_USER")
+ auth.password = secret("SONATYPE_PASSWORD")
+ syncToMavenCentral = true
}
- directory {
- directory = "../build/maven"
+ // use "deployNexusSnapshot" to deploy to sonatype snapshots repo
+ nexusSpec("snapshot") {
+ auth.user = secret("SONATYPE_USER")
+ auth.password = secret("SONATYPE_PASSWORD")
+ repositoryUrl = ossrhSnapshots1
+ release.version = "latest-SNAPSHOT"
}
- directory("shared") {
- directory = file(repositories.mavenLocal().url).absolutePath
+ // use "deployGithub" to deploy to github packages
+ githubSpec {
+ repository = "Grease"
+ owner = "deepmedia"
+ auth {
+ user = secret("GHUB_USER")
+ token = secret("GHUB_PERSONAL_ACCESS_TOKEN")
+ }
}
}
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/GreaseExtension.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/GreaseExtension.kt
new file mode 100644
index 0000000..bb2ce2b
--- /dev/null
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/GreaseExtension.kt
@@ -0,0 +1,33 @@
+package io.deepmedia.tools.grease
+
+import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
+import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
+import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
+import org.gradle.api.Action
+import org.gradle.api.model.ObjectFactory
+import org.gradle.kotlin.dsl.listProperty
+import org.gradle.kotlin.dsl.newInstance
+import org.gradle.kotlin.dsl.property
+import javax.inject.Inject
+
+abstract class GreaseExtension @Inject constructor(objects: ObjectFactory) {
+
+ internal val prefix = objects.property().convention("")
+ internal val relocators = objects.listProperty()
+ internal val transformers = objects.listProperty()
+
+ fun relocate(prefix: String = "grease") {
+ this.prefix.set(prefix)
+ }
+
+ fun relocate(from: String, to: String, configure: Action = Action { }) {
+ val relocator = SimpleRelocator(from, to, emptyList(), emptyList())
+ configure.execute(relocator)
+ relocators.add(relocator)
+ }
+
+ fun transform(transformer: T, configure: Action = Action { }) {
+ configure.execute(transformer)
+ transformers.add(transformer)
+ }
+}
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/GreasePlugin.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/GreasePlugin.kt
index 0bda43a..b23770d 100644
--- a/grease/src/main/kotlin/io/deepmedia/tools/grease/GreasePlugin.kt
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/GreasePlugin.kt
@@ -2,28 +2,44 @@
package io.deepmedia.tools.grease
+import com.android.build.api.component.analytics.AnalyticsEnabledLibraryVariant
+import com.android.build.api.component.analytics.AnalyticsEnabledVariant
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.Variant
+import com.android.build.api.variant.impl.getApiString
import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.api.LibraryVariant
-import com.android.build.gradle.api.LibraryVariantOutput
import com.android.build.gradle.internal.LibraryTaskManager
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.component.ComponentCreationConfig
+import com.android.build.gradle.internal.manifest.parseManifest
import com.android.build.gradle.internal.publishing.AndroidArtifacts
import com.android.build.gradle.internal.res.GenerateLibraryRFileTask
import com.android.build.gradle.internal.res.ParseLibraryResourcesTask
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.scope.MutableTaskContainer
-import com.android.build.gradle.internal.tasks.*
+import com.android.build.gradle.internal.tasks.LibraryJniLibsTask
+import com.android.build.gradle.internal.tasks.MergeFileTask
+import com.android.build.gradle.internal.tasks.Workers
import com.android.build.gradle.internal.tasks.factory.TaskCreationAction
-import com.android.build.gradle.internal.tasks.manifest.mergeManifestsForApplication
-import com.android.build.gradle.tasks.*
+import com.android.build.gradle.internal.tasks.manifest.mergeManifests
+import com.android.build.gradle.tasks.BundleAar
+import com.android.build.gradle.tasks.MergeResources
+import com.android.build.gradle.tasks.ProcessLibraryManifest
+import com.android.builder.errors.DefaultIssueReporter
+import com.android.ide.common.resources.CopyToOutputDirectoryResourceCompilationService
import com.android.manifmerger.ManifestMerger2
import com.android.manifmerger.ManifestProvider
+import com.android.utils.StdLogger
+import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
-import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.get
+import org.gradle.kotlin.dsl.support.unzipTo
+import org.gradle.kotlin.dsl.support.zipTo
+import java.io.File
/**
* Adds grease configurations for bundling dependencies in AAR files.
@@ -36,7 +52,7 @@ import org.gradle.kotlin.dsl.get
*/
open class GreasePlugin : Plugin {
- private val Project.greaseDir get() = buildDir.folder("grease")
+ private val defaultIssueReporter = DefaultIssueReporter(StdLogger(StdLogger.Level.WARNING))
@Suppress("NAME_SHADOWING")
override fun apply(target: Project) {
@@ -45,25 +61,36 @@ open class GreasePlugin : Plugin {
}
val log = Logger(target, "grease")
val android = target.extensions["android"] as LibraryExtension
- debugConfigurationHierarchy(target, log)
+ val androidComponents = target.extensions.getByType(AndroidComponentsExtension::class.java)
+ val greaseExtension = target.extensions.create("grease", GreaseExtension::class.java)
+
+ debugGreasyConfigurationHierarchy(target, log)
// Create the configurations.
- target.createRootConfiguration(log)
- target.createProductFlavorConfigurations(android.productFlavors, log)
- target.createBuildTypeConfigurations(android.buildTypes, log)
- target.createVariantConfigurations(android.libraryVariants, log)
+ fun createConfigurations(isTransitive: Boolean) {
+ target.createRootConfiguration(isTransitive, log)
+ target.createProductFlavorConfigurations(androidComponents, isTransitive, log)
+ target.createBuildTypeConfigurations(android.buildTypes, isTransitive, log)
+ target.createVariantConfigurations(androidComponents, isTransitive, log)
+ }
+ createConfigurations(false)
+ createConfigurations(true)
+ fun configure(variant: Variant, vararg configurations: Configuration) {
+ configureVariantManifest(target, variant, configurations, log)
+ configureVariantJniLibs(target, variant, configurations, log)
+ configureVariantResources(target, variant, configurations, log)
+ configureVariantSources(target, variant, configurations, greaseExtension, log)
+ configureVariantAssets(target, variant, configurations, log)
+ configureVariantProguardFiles(target, variant, configurations, log)
+ }
// Configure all variants.
- android.libraryVariants.configureEach {
+ androidComponents.onVariants { variant ->
val log = log.child("configureVariant")
- log.i { "Configuring variant ${this.name}..." }
- val configuration = target.greaseOf(this)
- configureVariantManifest(target, this, configuration, log)
- configureVariantJniLibs(target, this, configuration, log)
- configureVariantResources(target, this, configuration, log)
- configureVariantSources(target, this, configuration, log)
- configureVariantAssets(target, this, configuration, log)
- configureVariantProguardFiles(target, this, configuration, log)
+ log.d { "Configuring variant ${variant.name}..." }
+ target.afterEvaluate {
+ configure(variant, target.greaseOf(variant), target.greaseOf(variant, true))
+ }
}
}
@@ -89,79 +116,84 @@ open class GreasePlugin : Plugin {
*/
private fun configureVariantManifest(
target: Project,
- variant: LibraryVariant,
- configuration: Configuration,
+ variant: Variant,
+ configurations: Array,
logger: Logger
) {
val log = logger.child("configureVariantManifest")
- variant.outputs.configureEach {
- val variantOutput = this
- variantOutput as LibraryVariantOutput
- log.i { "Configuring variant output ${variantOutput.name}..." }
+ log.d { "Configuring variant output ${variant.name}..." }
+
+ val componentConfig = variant.componentCreationConfigOrThrow()
+
+ target.locateTask(componentConfig.computeTaskName("process", "Manifest"))?.configure {
+ val processManifestTask = this as ProcessLibraryManifest
- // cast ManifestProcessorTask to ProcessLibraryManifest
- @Suppress("UNCHECKED_CAST")
- val processManifestTask = processManifestProvider as TaskProvider
+ val extraManifests = configurations.artifactsOf(AndroidArtifacts.ArtifactType.MANIFEST)
+ dependsOn(extraManifests)
// After the file is copied we can go on with the actual manifest merging.
// This task will overwrite the original AndroidManifest.xml.
- val reprocessManifestTask = target.tasks.register(processManifestTask.name.greasify()) {
- dependsOn(processManifestTask)
+ doLast {
+
+ val reportFile = target.greaseBuildDir.get().file("manifest_report.txt")
+ target.delete(reportFile)
// To retrieve the secondary files, we must query the configuration artifacts.
- val primaryManifest = processManifestTask.get().manifestOutputFile.asFile // overwrite
- val secondaryManifests = configuration.artifactsOf(AndroidArtifacts.ArtifactType.MANIFEST)
- inputs.file(primaryManifest)
- inputs.files(secondaryManifests)
- outputs.file(primaryManifest)
-
- doLast {
- log.i { "Merging manifests... primary=${primaryManifest.get()}, secondary=${secondaryManifests.files.joinToString()}" }
- mergeManifestsForApplication(
- mainManifest = primaryManifest.get(),
- /* Overlays are other manifests from the current 'source set' of this lib. */
- manifestOverlays = if (false) secondaryManifests.files.toList() else listOf(),
- /* Dependencies are other manifests from other libraries, which should be our
+ val primaryManifest = processManifestTask.manifestOutputFile.asFile // overwrite
+
+ @Suppress("DEPRECATION")
+ val mergedFlavor = componentConfig.oldVariantApiLegacySupport?.mergedFlavor
+
+ log.d { "Merging manifests... primary=${primaryManifest.get()}, secondary=${extraManifests.files.joinToString()}" }
+
+ mergeManifests(
+ mainManifest = primaryManifest.get(),
+ /* Overlays are other manifests from the current 'source set' of this lib. */
+ manifestOverlays = if (false) extraManifests.files.toList() else listOf(),
+ /* Dependencies are other manifests from other libraries, which should be our
* case but it's not clear if we can use them with the LIBRARY merge type. */
- dependencies = if (true) secondaryManifests.files.map { object : ManifestProvider {
+ dependencies = if (true) extraManifests.files.map {
+ object : ManifestProvider {
override fun getManifest() = it
override fun getName() = null
- } } else listOf(),
- /* Not sure what this is but it can be empty. */
- navigationJsons = listOf(),
- /* Probably something about feature modules? Ignore */
- featureName = null,
- /* Need to apply the libraryVariant package name */
- packageOverride = variant.applicationId,
- /* Version data */
- /* The merged flavor represents all flavors plus the default config. */
- versionCode = variant.mergedFlavor.versionCode ?: 1, // Should we inspect the buildType as well?
- versionName = variant.mergedFlavor.versionName ?: "", // Should we inspect the buildType as well?
- minSdkVersion = variant.mergedFlavor.minSdkVersion?.apiString,
- targetSdkVersion = variant.mergedFlavor.targetSdkVersion?.apiString,
- maxSdkVersion = variant.mergedFlavor.maxSdkVersion,
- /* The output destination */
- outMergedManifestLocation = primaryManifest.get().absolutePath,
- /* Extra outputs that can probably be null. */
- outAaptSafeManifestLocation = null,
- /* Either LIBRARY or APPLICATION. When using LIBRARY we can't add lib dependencies */
- mergeType = if (true) ManifestMerger2.MergeType.APPLICATION else ManifestMerger2.MergeType.LIBRARY,
- /* Manifest placeholders. Doing this the way the library manifest does. */
- placeHolders = variant.mergedFlavor.manifestPlaceholders.also {
- it.putAll(variant.buildType.manifestPlaceholders)
- },
- /* Optional features to be enabled. */
- optionalFeatures = setOf(ManifestMerger2.Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT),
- /* Not sure, but it's empty in the lib processor */
- dependencyFeatureNames = setOf(),
- /* Output file with diagnostic info. I think. */
- reportFile = target.greaseDir.file("manifest_report.txt"),
- /* Logging */
- logger = LoggerWrapper(target.logger)
- )
- }
+ }
+ } else listOf(),
+ namespace = variant.namespace.get(),
+ /* Not sure what this is but it can be empty. */
+ navigationJsons = listOf(),
+ /* Probably something about feature modules? Ignore */
+ featureName = null,
+ /* Need to apply the libraryVariant package name */
+ packageOverride = componentConfig.applicationId.get(),
+ /* Version data */
+ /* The merged flavor represents all flavors plus the default config. */
+ versionCode = mergedFlavor?.versionCode,
+ versionName = mergedFlavor?.versionName,
+ minSdkVersion = componentConfig.minSdk.getApiString(),
+ targetSdkVersion = mergedFlavor?.targetSdkVersion?.apiString,
+ maxSdkVersion = mergedFlavor?.maxSdkVersion,
+ testOnly = false,
+ extractNativeLibs = null,
+ generatedLocaleConfigAttribute = null,
+ profileable = false,
+ /* The output destination */
+ outMergedManifestLocation = primaryManifest.get().absolutePath,
+ /* Extra outputs that can probably be null. */
+ outAaptSafeManifestLocation = null,
+ /* Either LIBRARY or APPLICATION. When using LIBRARY we can't add lib dependencies */
+ mergeType = ManifestMerger2.MergeType.FUSED_LIBRARY,
+ /* Manifest placeholders. Doing this the way the library manifest does. */
+ placeHolders = mergedFlavor?.manifestPlaceholders.orEmpty() + variant.manifestPlaceholders.get(),
+ /* Optional features to be enabled. */
+ optionalFeatures = setOf(ManifestMerger2.Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT),
+ /* Not sure, but it's empty in the lib processor */
+ dependencyFeatureNames = setOf(),
+ /* Output file with diagnostic info. I think. */
+ reportFile = reportFile.asFile,
+ /* Logging */
+ logger = LoggerWrapper(target.logger)
+ )
}
- processManifestTask.configure { finalizedBy(reprocessManifestTask) }
}
}
@@ -185,53 +217,28 @@ open class GreasePlugin : Plugin {
*/
private fun configureVariantJniLibs(
target: Project,
- variant: LibraryVariant,
- configuration: Configuration,
+ variant: Variant,
+ configurations: Array,
logger: Logger
) {
val log = logger.child("configureVariantJniLibs")
- log.i { "Configuring variant ${variant.name}..." }
-
- val anchorTaskName = nameOf("copy", variant.name, "JniLibsProjectAndLocalJars")
- val extractorTask = target.tasks.register(anchorTaskName.greasify()) {
- val anchorTask = target.tasks[anchorTaskName] as LibraryJniLibsTask
- inputs.files(configuration.artifactsOf(AndroidArtifacts.ArtifactType.JNI))
- // Option 1: use the directory property. This fails, Gradle can't create this task
- // because it is already the output directory of someone else.
- // outputs.dir(anchorTask.outputDirectory)
- // Option 2: resolve the directory. I think that in this case we MUST add a dependsOn()
- // or we won't be sure that the directory is available.
- dependsOn(anchorTask)
- outputs.dir(anchorTask.outputDirectory.get())
- log.i { "Configured output directory to be ${anchorTask.outputDirectory.get()}." }
- // Option 3: declare the exact files we'll be copying. This was valuable in my opinion,
- // But this configuration is causing deadlocks. Asked about it here
- // https://discuss.gradle.org/t/task-outputs-deadlock-how-to-declare-lazy-outputs-based-on-lazy-inputs/37107
- /* outputs.files(inputs.files.elements.map {
- log.i { "Configuring outputs - input artifacts were resolved, ${it.size}. Mapping them to output files." }
- val outputRoot = anchorTask.outputDirectory.get()
- val outputFiles = it.flatMap { inputRoot ->
- val inputSharedLibraries = inputRoot.asFile.listSharedLibrariesRecursive()
- log.i { "Configuring outputs - found ${inputSharedLibraries.size} libraries in ${inputRoot.asFile}, mapping to ${outputRoot.asFile}..." }
- inputSharedLibraries.map { sharedLibrary ->
- log.i { "Configuring outputs - mapping shared library ${sharedLibrary.toRelativeString(inputRoot.asFile)}..." }
- sharedLibrary.moveHierarchy(inputRoot.asFile, outputRoot.asFile)
- }
- }
- // Spread files and create a new collection, so that we return Provider
- // to the outputs. This is also useful to lose the task dependency information that
- // we don't want to carry over from inputs to outputs.
- target.files(*outputFiles.toTypedArray())
- }) */
+ log.d { "Configuring variant ${variant.name}..." }
- doFirst {
- log.i { "Executing for variant ${variant.name} and ${inputs.files.files.size} roots..." }
- inputs.files.files.forEach { inputRoot ->
- log.i { "Found shared libraries root: $inputRoot" }
- val outputRoot = anchorTask.outputDirectory.get().asFile
+ val creationConfig = variant.componentCreationConfigOrThrow()
+
+ target.locateTask(creationConfig.computeTaskName("copy", "JniLibsProjectAndLocalJars"))?.configure {
+ val copyJniTask = this as LibraryJniLibsTask
+ val extraJniLibs = configurations.artifactsOf(AndroidArtifacts.ArtifactType.JNI)
+ dependsOn(extraJniLibs)
+
+ fun injectJniLibs() {
+ log.d { "Executing for variant ${variant.name} and ${extraJniLibs.files.size} roots..." }
+ extraJniLibs.files.forEach { inputRoot ->
+ log.d { "Found shared libraries root: $inputRoot" }
+ val outputRoot = copyJniTask.outputDirectory.get().asFile
val sharedLibraries = inputRoot.listFilesRecursive("so")
sharedLibraries.forEach {
- log.i { "Copying ${it.toRelativeString(inputRoot)} from inputRoot=$inputRoot to outputRoot=$outputRoot..." }
+ log.d { "Copying ${it.toRelativeString(inputRoot)} from inputRoot=$inputRoot to outputRoot=$outputRoot..." }
target.copy {
from(it)
into(it.relocate(inputRoot, outputRoot).parentFile)
@@ -239,10 +246,12 @@ open class GreasePlugin : Plugin {
}
}
}
- }
- target.tasks.configureEach {
- if (name == anchorTaskName) {
- finalizedBy(extractorTask)
+
+ val files = projectNativeLibs.get().files().files + localJarsNativeLibs?.files.orEmpty()
+ if (files.isNotEmpty()) {
+ doLast { injectJniLibs() }
+ } else {
+ injectJniLibs()
}
}
}
@@ -305,41 +314,52 @@ open class GreasePlugin : Plugin {
*/
private fun configureVariantResources(
target: Project,
- variant: LibraryVariant,
- configuration: Configuration,
+ variant: Variant,
+ configurations: Array,
logger: Logger
) {
+
val log = logger.child("configureVariantResources")
- log.i { "Configuring variant ${variant.name}..." }
- val anchorTaskName = nameOf("package", variant.name, "Resources")
- val greaseTask = target.tasks.register(anchorTaskName.greasify()) {
- val anchorTask = target.tasks[anchorTaskName] as MergeResources
- dependsOn(anchorTask)
- inputs.files(configuration.artifactsOf(AndroidArtifacts.ArtifactType.ANDROID_RES))
- outputs.dir(anchorTask.outputDir.get())
- doFirst {
- log.i { "Executing for variant ${variant.name} and ${inputs.files.files.size} roots..." }
- inputs.files.files.forEach { inputRoot ->
- log.i { "Found resources root: $inputRoot" }
- val outputRoot = anchorTask.outputDir.get().asFile
- val outputPrefix = inputRoot.parentFile.name.map { char ->
- if (char.isLetterOrDigit()) char else '_'
- }.joinToString(separator = "")
- val resources = inputRoot.listFilesRecursive("xml")
- resources.forEach {
- log.i { "Copying ${it.toRelativeString(inputRoot)} (prefix=$outputPrefix) from inputRoot=$inputRoot to outputRoot=$outputRoot..." }
- target.copy {
- from(it)
- into(it.relocate(inputRoot, outputRoot).parentFile)
- rename { "${outputPrefix}_$it" }
- }
- }
- }
+ log.d { "Configuring variant ${variant.name}..." }
+ val creationConfig = variant.componentCreationConfigOrThrow()
+
+ target.locateTask(creationConfig.computeTaskName("package", "Resources"))?.configure {
+ this as MergeResources
+
+ val resourcesMergingWorkdir = target.greaseBuildDir.get().dir(variant.name).dir("resources")
+ val mergedResourcesDir = resourcesMergingWorkdir.dir("merged")
+ val blameDir = resourcesMergingWorkdir.dir("blame")
+ val extraAndroidRes = configurations.artifactsOf(AndroidArtifacts.ArtifactType.ANDROID_RES)
+ dependsOn(extraAndroidRes)
+
+ outputs.upToDateWhen { false } // always execute
+
+ fun injectResources() {
+ target.delete(resourcesMergingWorkdir)
+
+ val executorFacade = Workers.withGradleWorkers(
+ creationConfig.services.projectInfo.path,
+ path,
+ workerExecutor,
+ analyticsService
+ )
+ log.d { "Merge additional resources into $mergedResourcesDir" }
+ mergeResourcesWithCompilationService(
+ resCompilerService = CopyToOutputDirectoryResourceCompilationService,
+ incrementalMergedResources = mergedResourcesDir.asFile,
+ mergedResources = outputDir.asFile.get(),
+ resourceSets = extraAndroidRes.files.toList(),
+ minSdk = minSdk.get(),
+ aaptWorkerFacade = executorFacade,
+ blameLogOutputFolder = blameDir.asFile,
+ logger = this.logger
+ )
}
- }
- target.tasks.configureEach {
- if (name == anchorTaskName) {
- finalizedBy(greaseTask)
+
+ if (inputs.hasInputs) {
+ doLast { injectResources() }
+ } else {
+ injectResources()
}
}
}
@@ -363,35 +383,173 @@ open class GreasePlugin : Plugin {
*/
private fun configureVariantSources(
target: Project,
- variant: LibraryVariant,
- configuration: Configuration,
+ variant: Variant,
+ configurations: Array,
+ greaseExtension: GreaseExtension,
logger: Logger
) {
val log = logger.child("configureVariantSources")
- log.i { "Configuring variant ${variant.name}..." }
- val compileTask = variant.javaCompileProvider // compile<>JavaWithJavac
- val bundleTaskName = nameOf("sync", variant.name, "LibJars")
- val greaseTask = target.tasks.register(compileTask.name.greasify()) {
- dependsOn(compileTask)
+ log.d { "Configuring variant ${variant.name}..." }
+
+ val creationConfig = variant.componentCreationConfigOrThrow()
+
+ val workdir = target.greaseBuildDir.get().dir(variant.name)
+ val aarExtractWorkdir = workdir.dir("extract").dir("aar")
+ val jarExtractWorkdir = workdir.dir("extract").dir("jar")
+ val jarFileName = "classes.jar"
+
+ val bundleLibraryTask = creationConfig.taskContainer.bundleLibraryTask
+
+ val greaseExpandTask = target.tasks.locateOrRegisterTask(
+ creationConfig.computeTaskName("extract", "Aar").greasify(),
+ ) {
+ val bundleAar = bundleLibraryTask?.get() as BundleAar
+
+ outputs.upToDateWhen { false } // always execute
+
+ inputs.file(bundleAar.archiveFile)
+ outputs.file(aarExtractWorkdir.file(jarFileName).asFile)
+
+ doFirst {
+ target.delete(aarExtractWorkdir)
+ unzipTo(aarExtractWorkdir.asFile, bundleAar.archiveFile.get().asFile)
+ }
+ }
+
+ val greaseProcessTask = target.tasks.locateOrRegisterTask(
+ creationConfig.computeTaskName("process", "Jar").greasify(),
+ ) {
+
// There are many options here. PROCESSED_JAR, PROCESSED_AAR, CLASSES, CLASSES_JAR ...
// CLASSES_JAR seems to be the best though it's not clear if it's jetified or not.
- inputs.files(configuration.artifactsOf(AndroidArtifacts.ArtifactType.CLASSES_JAR))
- outputs.dir(compileTask.get().destinationDirectory.get())
+ val extraJars = configurations.artifactsOf(AndroidArtifacts.ArtifactType.CLASSES_JAR)
+ dependsOn(extraJars)
+ dependsOn(greaseExpandTask)
+
+ outputs.upToDateWhen { false } // always execute
+ inputs.files(greaseExpandTask.get().outputs.files)
+ outputs.dir(jarExtractWorkdir)
+
+ fun injectClasses(inputJar: File) {
+ log.d { "Processing inputJar=$inputJar outputDir=${jarExtractWorkdir}..." }
+ val inputFiles = target.zipTree(inputJar).matching { include("**/*.class") }
+ target.copy {
+ from(inputFiles)
+ into(jarExtractWorkdir)
+ }
+ }
+
doFirst {
- log.i { "Executing for variant ${variant.name} and ${inputs.files.files.size} roots..." }
- inputs.files.files.forEach { inputJar ->
- log.i { "Processing inputJar=$inputJar outputDir=${compileTask.get().destinationDirectory.get()}..." }
- val inputFiles = target.zipTree(inputJar).matching { include("**/*.class") }
- target.copy {
- from(inputFiles)
- into(outputs.files.singleFile)
+ target.delete(jarExtractWorkdir)
+ log.d { "Executing merging for variant ${variant.name} and ${extraJars.files.size} roots..." }
+ extraJars.files.forEach(::injectClasses)
+ aarExtractWorkdir.file(jarFileName).asFile.run(::injectClasses)
+ }
+ }
+
+ val greaseShadowTask = target.tasks.locateOrRegisterTask(
+ creationConfig.computeTaskName("shadow", "Aar").greasify(),
+ ShadowJar::class.java
+ ) {
+ val compileTask = creationConfig.taskContainer.javacTask
+ val extraManifests = configurations.artifactsOf(AndroidArtifacts.ArtifactType.MANIFEST)
+ val greaseShadowDir = workdir.dir("shadow")
+ val bundleAar = bundleLibraryTask?.get() as BundleAar
+
+ outputs.upToDateWhen { false } // always execute
+
+ dependsOn(extraManifests)
+ dependsOn(greaseExpandTask)
+ dependsOn(greaseProcessTask)
+
+ archiveFileName.set(jarFileName)
+ destinationDirectory.set(greaseShadowDir)
+
+ from(greaseProcessTask.get().outputs)
+ val packagesToReplace = mutableMapOf()
+
+ doFirst {
+ target.delete(greaseShadowDir)
+ greaseShadowDir.asFile.mkdirs()
+
+ log.d { "Executing shadowing for variant ${variant.name} and ${extraManifests.files.size} roots with namespace ${variant.namespace.get()}..." }
+ extraManifests.forEach { inputFile ->
+ val manifestData = parseManifest(inputFile, true, { true }, defaultIssueReporter)
+ manifestData.packageName?.let { fromPackageName ->
+ log.d { "Processing R class from $fromPackageName manifestInput=${inputFile.path} outputDir=${compileTask.get().destinationDirectory.get()}..." }
+ relocate(RClassRelocator(fromPackageName, variant.namespace.get(), log))
}
}
+
+ val relocationPrefix = greaseExtension.prefix.get()
+ if (relocationPrefix.isNotEmpty()) {
+ greaseProcessTask.get().outputs.files
+ .asSequence()
+ .flatMap { inputFile -> inputFile.packageNames }
+ .distinct()
+ .forEach { packageName ->
+ val newPackageName = "${relocationPrefix}.$packageName"
+ log.d { "Relocate package from $packageName to $newPackageName" }
+ relocate(packageName, newPackageName)
+ packagesToReplace[packageName] = newPackageName
+ }
+ }
+
+ greaseExtension.relocators.get().forEach { relocator ->
+ relocate(relocator)
+ if (relocator is SimpleRelocator) {
+ packagesToReplace[relocator.pattern] = relocator.shadedPattern
+ }
+ }
+
+ greaseExtension.transformers.get().forEach(::transform)
+ }
+
+ doLast {
+ val shadowJar = greaseShadowDir.file(jarFileName).asFile
+ val shadowManifest = greaseShadowDir.file("AndroidManifest.xml").asFile
+ log.d { "Copy shaded inputJar=${shadowJar} outputDir=$aarExtractWorkdir..." }
+ target.copy {
+ from(shadowJar)
+ into(aarExtractWorkdir)
+ }
+
+ val manifestWriter = shadowManifest.bufferedWriter()
+ val manifestReader = aarExtractWorkdir.file("AndroidManifest.xml").asFile.bufferedReader()
+
+ manifestReader.useLines { strings ->
+ strings
+ .map { string ->
+ packagesToReplace.entries.fold(string) { acc, (from, to) ->
+ acc.replace(from, to)
+ }
+ }.forEach {
+ manifestWriter.write(it)
+ manifestWriter.newLine()
+ }
+ }
+ manifestWriter.close()
+ target.copy {
+ from(shadowManifest)
+ into(aarExtractWorkdir)
+ }
+
+ val oldArchive = bundleAar.archiveFile.get().asFile
+ val archiveParent = oldArchive.parentFile
+ val archiveName = oldArchive.name
+ target.delete(oldArchive)
+ zipTo(archiveParent.file(archiveName), aarExtractWorkdir.asFile)
}
}
- compileTask.configure { finalizedBy(greaseTask) }
- target.tasks.configureEach {
- if (name == bundleTaskName) dependsOn(greaseTask)
+
+ bundleLibraryTask?.configure {
+ finalizedBy(greaseExpandTask)
+ }
+ greaseExpandTask.configure {
+ finalizedBy(greaseProcessTask)
+ }
+ greaseProcessTask.configure {
+ finalizedBy(greaseShadowTask)
}
}
@@ -406,31 +564,33 @@ open class GreasePlugin : Plugin {
*/
private fun configureVariantAssets(
target: Project,
- variant: LibraryVariant,
- configuration: Configuration,
+ variant: Variant,
+ configurations: Array,
logger: Logger
) {
val log = logger.child("configureVariantAssets")
- log.i { "Configuring variant ${variant.name}..." }
- val compileTask = variant.mergeAssetsProvider // package<>Assets
- val greaseTask = target.tasks.register(compileTask.name.greasify()) {
- dependsOn(compileTask)
- inputs.files(configuration.artifactsOf(AndroidArtifacts.ArtifactType.ASSETS))
- outputs.dir(compileTask.get().outputDir.get())
- doFirst {
- log.i { "Executing for variant ${variant.name} and ${inputs.files.files.size} roots..." }
- inputs.files.files.forEach { inputRoot ->
- log.i { "Found asset folder root: $inputRoot" }
- // TODO crash/warn if any asset is overwritten
+ log.d { "Configuring variant ${variant.name}..." }
+ val creationConfig = variant.componentCreationConfigOrThrow()
+ creationConfig.taskContainer.mergeAssetsTask.configure {
+ val extraAssets = configurations.artifactsOf(AndroidArtifacts.ArtifactType.ASSETS)
+ dependsOn(extraAssets)
+ fun injectAssets() {
+ log.d { "Executing for variant ${variant.name} and ${extraAssets.files.size} roots..." }
+ extraAssets.files.forEach { inputRoot ->
+ log.d { "Found asset folder root: $inputRoot" }
val inputFiles = target.fileTree(inputRoot)
target.copy {
from(inputFiles)
- into(outputs.files.singleFile)
+ into(outputDir.get())
}
}
}
+ if (inputs.hasInputs) {
+ doLast { injectAssets() }
+ } else {
+ injectAssets()
+ }
}
- compileTask.configure { finalizedBy(greaseTask) }
}
/**
@@ -458,28 +618,33 @@ open class GreasePlugin : Plugin {
*/
private fun configureVariantProguardFiles(
target: Project,
- variant: LibraryVariant,
- configuration: Configuration,
+ variant: Variant,
+ configurations: Array,
logger: Logger
) {
val log = logger.child("configureVariantProguardFiles")
- log.i { "Configuring variant ${variant.name}..." }
- target.tasks.configureEach {
- if (name == nameOf("merge", variant.name, "ConsumerProguardFiles")) {
- val task = this as MergeFileTask
- // UNFILTERED_PROGUARD_RULES, FILTERED_PROGUARD_RULES, AAPT_PROGUARD_RULES, ...
- // UNFILTERED_PROGUARD_RULES is output of the AarTransform. FILTERED_PROGUARD_RULES
- // is processed by another transform and is probably what we want in the end.
- val extraInputs = configuration.artifactsOf(AndroidArtifacts.ArtifactType.FILTERED_PROGUARD_RULES)
- val inputs = task.inputFiles.plus(extraInputs)
- task.inputFiles = inputs
- task.doFirst {
- require (task.inputFiles === inputs) {
- "Input proguard files have been changed after our configureEach callback!"
- }
- log.i { "Input proguard files: ${inputFiles.files.joinToString()}" }
- }
+ log.d { "Configuring variant ${variant.name}..." }
+ val creationConfig = variant.componentCreationConfigOrThrow()
+ target.locateTask(creationConfig.computeTaskName("merge", "ConsumerProguardFiles"))?.configure {
+ val mergeFileTask = this as MergeFileTask
+ // UNFILTERED_PROGUARD_RULES, FILTERED_PROGUARD_RULES, AAPT_PROGUARD_RULES, ...
+ // UNFILTERED_PROGUARD_RULES is output of the AarTransform. FILTERED_PROGUARD_RULES
+ // is processed by another transform and is probably what we want in the end.
+ val extraInputs = configurations.artifactsOf(AndroidArtifacts.ArtifactType.FILTERED_PROGUARD_RULES)
+ dependsOn(extraInputs)
+
+ mergeFileTask.inputs.files(extraInputs + mergeFileTask.inputFiles.files)
+ mergeFileTask.doFirst {
+ log.d { "Input proguard files: ${mergeFileTask.inputs.files.joinToString()}" }
}
}
}
}
+
+private fun Variant.componentCreationConfigOrThrow(): ComponentCreationConfig {
+ return when (this) {
+ is ComponentCreationConfig -> this
+ is AnalyticsEnabledVariant -> this.delegate.componentCreationConfigOrThrow()
+ else -> error("Could not find ComponentCreationConfig in $this.")
+ }
+}
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/Logger.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/Logger.kt
index b94c0b4..8dcb041 100644
--- a/grease/src/main/kotlin/io/deepmedia/tools/grease/Logger.kt
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/Logger.kt
@@ -4,25 +4,17 @@ import org.gradle.api.Project
import org.gradle.api.logging.LogLevel
internal class Logger(private val project: Project, private val tag: String) {
- companion object {
- val INFO = LogLevel.INFO
- val WARNING = LogLevel.WARN
- val ERROR = LogLevel.ERROR
- }
fun log(level: LogLevel, message: () -> String?) {
message.invoke()?.let {
- if (level == INFO) {
- println("$tag: $it")
- } else {
- project.logger.log(level, "$tag: $it")
- }
+ project.logger.log(level, "$tag: $it")
}
}
- fun i(message: () -> String?) = log(INFO, message)
- fun w(message: () -> String?) = log(WARNING, message)
- fun e(message: () -> String?) = log(ERROR, message)
+ fun i(message: () -> String?) = log(LogLevel.INFO, message)
+ fun d(message: () -> String?) = log(LogLevel.DEBUG, message)
+ fun w(message: () -> String?) = log(LogLevel.WARN, message)
+ fun e(message: () -> String?) = log(LogLevel.ERROR, message)
fun child(tag: String) = Logger(project, "${this.tag} > $tag")
}
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/RClassRelocator.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/RClassRelocator.kt
new file mode 100644
index 0000000..4272338
--- /dev/null
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/RClassRelocator.kt
@@ -0,0 +1,33 @@
+package io.deepmedia.tools.grease
+
+import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext
+import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
+
+internal class RClassRelocator(
+ fromPackage: String,
+ toPackage: String,
+ logger: Logger
+) : SimpleRelocator(fromPackage, toPackage, emptyList(), emptyList()) {
+
+ private val logger = logger.child("R-relocator")
+ private val fromRPath = fromPackage.replace(".", "/")
+ private val toRPath = toPackage.replace(".", "/") + "/R"
+ private val fromRPathRegex = "$fromRPath.*/R".toRegex()
+
+ init {
+ include( "%regex[$fromRPathRegex\\$.*]")
+ include( "%regex[$fromRPathRegex]")
+ }
+
+ override fun canRelocateClass(className: String?): Boolean = false
+
+ override fun relocatePath(context: RelocatePathContext): String {
+ val foundedPath = fromRPathRegex.find(context.path)?.value
+ if (foundedPath == null) {
+ logger.d { "Can't move from ${context.path} to $toRPath" }
+ return context.path
+ }
+ return context.path.replaceFirst(foundedPath, toRPath)
+ }
+
+}
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/configurations.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/configurations.kt
index 03833fa..0bf055e 100644
--- a/grease/src/main/kotlin/io/deepmedia/tools/grease/configurations.kt
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/configurations.kt
@@ -1,38 +1,52 @@
package io.deepmedia.tools.grease
import com.android.build.api.attributes.BuildTypeAttr
-import com.android.build.gradle.api.LibraryVariant
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.Variant
import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.ProductFlavor
import com.android.build.gradle.internal.publishing.AndroidArtifacts
-import org.gradle.api.DomainObjectSet
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Usage
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.file.CompositeFileCollection
+import org.gradle.api.internal.file.FileCollectionInternal
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.named
+import java.util.function.Consumer
-internal fun Configuration.artifactsOf(type: AndroidArtifacts.ArtifactType)
- = incoming.artifactView {
+internal fun Configuration.artifactsOf(type: AndroidArtifacts.ArtifactType): FileCollection = incoming.artifactView {
attributes {
attribute(AndroidArtifacts.ARTIFACT_TYPE, type.type)
}
}.files
-internal val Project.grease get() = greaseOf("")
+internal fun Array.artifactsOf(type: AndroidArtifacts.ArtifactType): FileCollection = map {
+ it.incoming.artifactView {
+ attributes {
+ attribute(AndroidArtifacts.ARTIFACT_TYPE, type.type)
+ }
+ }.files as FileCollectionInternal
+}.let {
+ ArtifactsFileCollection(it)
+}
-internal fun Project.greaseOf(flavor: com.android.builder.model.ProductFlavor)
- = greaseOf(flavor.name)
+private class ArtifactsFileCollection(private val fileCollections: List) : CompositeFileCollection() {
+ override fun getDisplayName(): String = "grease file collection"
-internal fun Project.greaseOf(buildType: com.android.builder.model.BuildType)
- = greaseOf(buildType.name)
+ override fun visitChildren(visitor: Consumer) {
+ fileCollections.forEach(visitor::accept)
+ }
+}
-internal fun Project.greaseOf(variant: LibraryVariant)
- = greaseOf(variant.name)
+internal fun Project.grease(isTransitive: Boolean) = greaseOf("".configurationName(isTransitive))
-private fun Project.greaseOf(name: String)
- = configurations[name.greasify()]
+internal fun Project.greaseOf(variant: Variant, isTransitive: Boolean = false) =
+ greaseOf(variant.name.configurationName(isTransitive))
+
+private fun Project.greaseOf(name: String, isTransitive: Boolean = false) =
+ configurations[name.configurationName(isTransitive).greasify()]
/**
@@ -50,8 +64,13 @@ private fun Project.greaseOf(name: String)
* Note that the counterpart for "compileClasspath" also exists and it's called "runtimeClasspath".
* They map to the org.gradle.usage attribute of java-api and java-runtime respectively.
*/
-private fun Project.createGrease(name: String): Configuration {
- val configuration = configurations.create(name.greasify())
+private fun Project.createGrease(name: String, isTransitive: Boolean): Configuration {
+ val greasifiedName = name.configurationName(isTransitive).greasify()
+ val existed = configurations.findByName(greasifiedName)
+ if (existed != null) return existed
+
+ val configuration = configurations.create(greasifiedName)
+ configuration.isTransitive = isTransitive
configuration.attributes {
// This should make sure that we don't pull in compileOnly dependencies that should not be in
// the final bundle. The other usage, JAVA_API, would only include exposed dependencies,
@@ -62,39 +81,62 @@ private fun Project.createGrease(name: String): Configuration {
configurations.configureEach {
val other = this
if (other.name == nameOf(name, "compileClasspath")) {
- extendsFrom(configuration)
+ other.extendsFrom(configuration)
}
}
return configuration
}
+private fun String.configurationName(isTransitive: Boolean) = if (isTransitive) nameOf(this, "tree") else this
+
// Create the root configuration. Make compileOnly extend from it so that grease
// artifacts are in the classpath and we don't have compile issues.
-internal fun Project.createRootConfiguration(log: Logger) {
- log.i { "Creating root configuration..." }
- createGrease("")
+internal fun Project.createRootConfiguration(isTransitive: Boolean, log: Logger) {
+ log.d { "Creating root configuration..." }
+ createGrease("", isTransitive)
}
// Create one configuration per product flavor.
// Make it extend the root configuration so that artifacts are inherited.
internal fun Project.createProductFlavorConfigurations(
- flavors: NamedDomainObjectContainer, log: Logger) {
- flavors.configureEach {
- log.i { "Creating product flavor configuration ${this.name.greasify()}..." }
- val config = createGrease(this.name)
- config.extendsFrom(grease)
+ androidComponent: AndroidComponentsExtension<*, *, *>,
+ isTransitive: Boolean,
+ log: Logger,
+) = androidComponent.onVariants { variant ->
+ variant.flavorName?.let { flavorName ->
+ log.d { "Creating product flavor configuration ${flavorName.greasify()}..." }
+ val flavorConfiguration = createGrease(flavorName, isTransitive)
+ flavorConfiguration.extendsFromSafely(grease(isTransitive), log)
+
+ variant.productFlavors.forEach { (_, subFlavor) ->
+ log.d { "Creating sub product flavor configuration ${subFlavor.greasify()}..." }
+ val config = createGrease(subFlavor, isTransitive)
+ config.extendsFromSafely(grease(isTransitive), log)
+ flavorConfiguration.extendsFromSafely(config, log)
+ }
+ variant.productFlavors.forEach { (_, subFlavor) ->
+ val buildTypedSubFlavor = nameOf(subFlavor, variant.buildType.orEmpty())
+ log.d { "Creating buildTyped sub product flavor configuration ${buildTypedSubFlavor.greasify()}..." }
+ val config = createGrease(buildTypedSubFlavor, isTransitive)
+ config.extendsFromSafely(grease(isTransitive), log)
+ config.extendsFromSafely(greaseOf(subFlavor, isTransitive), log)
+ config.extendsFromSafely(flavorConfiguration, log)
+ }
}
}
// Create one configuration per build type.
// Make it extend the root configuration so that artifacts are inherited.
internal fun Project.createBuildTypeConfigurations(
- buildTypes: NamedDomainObjectContainer, log: Logger) {
+ buildTypes: NamedDomainObjectContainer,
+ isTransitive: Boolean,
+ log: Logger
+) {
buildTypes.configureEach {
val buildType = this
- log.i { "Creating build type configuration ${buildType.name.greasify()}..." }
- val config = createGrease(buildType.name)
- config.extendsFrom(grease)
+ log.d { "Creating build type configuration ${buildType.name.greasify()}..." }
+ val config = createGrease(buildType.name, isTransitive)
+ config.extendsFromSafely(grease(isTransitive), log)
config.attributes {
attribute(BuildTypeAttr.ATTRIBUTE, objects.named(BuildTypeAttr::class, buildType.name))
}
@@ -105,17 +147,27 @@ internal fun Project.createBuildTypeConfigurations(
// In addition to the root configuration, we should inherit from the build type config
// and the configurations for all flavors that make this variant.
internal fun Project.createVariantConfigurations(
- variants: DomainObjectSet, log: Logger) {
- variants.configureEach {
- val flavors = this.productFlavors
- if (flavors.isEmpty()) {
- log.i { "Variant has no flavors, reusing the build type configuration..." }
- } else {
- log.i { "Creating variant configuration ${this.name.greasify()}..." }
- val config = createGrease(this.name)
- config.extendsFrom(grease)
- config.extendsFrom(greaseOf(buildType))
- config.extendsFrom(*flavors.map { greaseOf(it) }.toTypedArray())
+ androidComponent: AndroidComponentsExtension<*, *, *>,
+ isTransitive: Boolean,
+ log: Logger
+) = androidComponent.onVariants { variant ->
+ log.d { "Creating variant configuration ${variant.name.greasify()}..." }
+ val config = createGrease(variant.name, isTransitive)
+ config.extendsFromSafely(grease(isTransitive), log)
+ config.extendsFromSafely(greaseOf(variant.buildType.orEmpty(), isTransitive), log)
+ variant.flavorName?.let { flavor ->
+ config.extendsFromSafely(greaseOf(flavor, isTransitive), log)
+ variant.productFlavors.forEach { (_, subFlavor) ->
+ config.extendsFromSafely(greaseOf(subFlavor, isTransitive), log)
+ config.extendsFromSafely(greaseOf(nameOf(subFlavor, variant.buildType.orEmpty()), isTransitive), log)
}
}
+
}
+
+private fun Configuration.extendsFromSafely(configuration: Configuration, log: Logger? = null) {
+ if (configuration.name != name) {
+ log?.d { "Extend $name from ${configuration.name}..." }
+ extendsFrom(configuration)
+ }
+}
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/debug.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/debug.kt
index f2f0d61..28f701e 100644
--- a/grease/src/main/kotlin/io/deepmedia/tools/grease/debug.kt
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/debug.kt
@@ -16,7 +16,7 @@ internal fun debugConfigurationHierarchy(target: Project, logger: Logger) {
val log = logger.child(this.name)
val attrKeys = attributes.keySet()
val attrs = attrKeys.map { it to attributes.getAttribute(it) }
- log.i {
+ log.d {
"Configuration added - " +
"canBeResolved=${isCanBeResolved} " +
"canBeConsumed=${isCanBeConsumed} " +
@@ -27,21 +27,34 @@ internal fun debugConfigurationHierarchy(target: Project, logger: Logger) {
}
}
+internal fun debugGreasyConfigurationHierarchy(target: Project, logger: Logger) {
+ target.afterEvaluate {
+ configurations.configureEach {
+ if (name.startsWith("grease")) {
+ logger.d { name }
+ extendsFrom.forEach {sub ->
+ logger.d { "| ${sub.name}" }
+ }
+ }
+ }
+ }
+}
+
internal fun debugSourcesTasks(target: Project, logger: Logger) {
target.tasks.configureEach {
val log = logger.child(this.name).child(this::class.java.simpleName)
when (val task = this) {
is LibraryAarJarsTask -> doFirst {
- log.i { "mainScopeClassFiles (i): ${task.mainScopeClassFiles.files.joinToString()}" }
- log.i { "mainClassLocation (o): ${task.mainClassLocation.orNull}" }
- log.i { "localJarsLocation (o): ${task.localJarsLocation.orNull}" }
+ log.d { "mainScopeClassFiles (i): ${task.mainScopeClassFiles.files.joinToString()}" }
+ log.d { "mainClassLocation (o): ${task.mainClassLocation.orNull}" }
+ log.d { "localJarsLocation (o): ${task.localJarsLocation.orNull}" }
}
is JavaCompile -> doFirst {
- log.i { "source (i): ${task.source.files.joinToString()}" }
- log.i { "source (i): ${task.source.files.joinToString()}" }
- log.i { "generatedSourceOutputDirectory (o): ${task.options.generatedSourceOutputDirectory.orNull}" }
- log.i { "headerOutputDirectory (o): ${task.options.headerOutputDirectory.orNull}" }
- log.i { "destinationDirectory (o): ${task.destinationDirectory.orNull}" }
+ log.d { "source (i): ${task.source.files.joinToString()}" }
+ log.d { "source (i): ${task.source.files.joinToString()}" }
+ log.d { "generatedSourceOutputDirectory (o): ${task.options.generatedSourceOutputDirectory.orNull}" }
+ log.d { "headerOutputDirectory (o): ${task.options.headerOutputDirectory.orNull}" }
+ log.d { "destinationDirectory (o): ${task.destinationDirectory.orNull}" }
}
}
}
@@ -52,42 +65,42 @@ internal fun debugResourcesTasks(target: Project, logger: Logger) {
val log = logger.child(this.name).child(this::class.java.simpleName)
when (val task = this) {
is GenerateLibraryRFileTask -> doFirst {
- log.i { "localResourcesFile (i): ${task.localResourcesFile.orNull}" }
- log.i { "dependencies (i): ${task.dependencies.files.joinToString()}" }
- log.i { "rClassOutputJar (o): ${task.rClassOutputJar.orNull}" }
- log.i { "sourceOutputDir (o): ${task.sourceOutputDir}" }
- log.i { "textSymbolOutputFileProperty (o): ${task.textSymbolOutputFileProperty.orNull}" }
- log.i { "symbolsWithPackageNameOutputFile (o): ${task.symbolsWithPackageNameOutputFile.orNull}" }
- log.i { "symbolsWithPackageNameOutputFile (o): ${task.symbolsWithPackageNameOutputFile.orNull}" }
+ log.d { "localResourcesFile (i): ${task.localResourcesFile.orNull}" }
+ log.d { "dependencies (i): ${task.dependencies.files.joinToString()}" }
+ log.d { "rClassOutputJar (o): ${task.rClassOutputJar.orNull}" }
+ log.d { "sourceOutputDir (o): ${task.sourceOutputDir}" }
+ log.d { "textSymbolOutputFileProperty (o): ${task.textSymbolOutputFileProperty.orNull}" }
+ log.d { "symbolsWithPackageNameOutputFile (o): ${task.symbolsWithPackageNameOutputFile.orNull}" }
+ log.d { "symbolsWithPackageNameOutputFile (o): ${task.symbolsWithPackageNameOutputFile.orNull}" }
}
is ParseLibraryResourcesTask -> doFirst {
- log.i { "inputResourcesDir (i): ${task.inputResourcesDir.orNull}" }
- log.i { "librarySymbolsFile (o): ${task.librarySymbolsFile.orNull}" }
+ log.d { "inputResourcesDir (i): ${task.inputResourcesDir.orNull}" }
+ log.d { "librarySymbolsFile (o): ${task.librarySymbolsFile.orNull}" }
}
is GenerateBuildConfig -> doFirst {
- log.i { "mergedManifests (i): ${task.mergedManifests.orNull}" } // empty
- log.i { "items (i): ${task.items.orNull}" } // empty
- log.i { "sourceOutputDir (o): ${task.sourceOutputDir}" } // generated/source/. contains the BuildConfig file
+ log.d { "mergedManifests (i): ${task.mergedManifests.orNull}" } // empty
+ log.d { "items (i): ${task.items.orNull}" } // empty
+ log.d { "sourceOutputDir (o): ${task.sourceOutputDir}" } // generated/source/. contains the BuildConfig file
}
is GenerateResValues -> doFirst {
- log.i { "items (i): ${task.items.orNull}" } // empty
- log.i { "resOutputDir (o): ${task.resOutputDir}" } // generated/res/resValues/. nothing there for now
+ log.d { "items (i): ${task.items.orNull}" } // empty
+ log.d { "resOutputDir (o): ${task.resOutputDir}" } // generated/res/resValues/. nothing there for now
}
is MergeResources -> doFirst {
// When packageResources: intermediates/packaged_res/.
// There we find a copy of the resources.
// When mergeResources (soon after): intermediates/res/merged/.
// This is clearly the input of the verify task below.
- log.i { "outputDir (o): ${task.outputDir.orNull}" }
+ log.d { "outputDir (o): ${task.outputDir.orNull}" }
// When packageResources: intermediates/public_res//public.txt
// When mergeResources: empty.
- log.i { "publicFile (o): ${task.publicFile.orNull}" }
+ log.d { "publicFile (o): ${task.publicFile.orNull}" }
}
is VerifyLibraryResourcesTask -> doFirst {
- log.i { "manifestFiles (i): ${task.manifestFiles.orNull}" } // intermediates/aapt_friendly_merged_manifests//aapt
- log.i { "inputDirectory (i): ${task.inputDirectory.orNull}" } // intermediates/res/merged// . Contains a copy of the resources, "merged" probably in the sense that all values are in a single file called values.xml ?
- log.i { "compiledDependenciesResources (i): ${task.compiledDependenciesResources.files.joinToString()}" } // empty
- log.i { "compiledDirectory (o): ${task.compiledDirectory}" } // intermediates/res/compiled// . Contains the input resources compiled to a misterious *.flat format, probably used in APKs.
+ log.d { "manifestFiles (i): ${task.manifestFiles.orNull}" } // intermediates/aapt_friendly_merged_manifests//aapt
+ log.d { "inputDirectory (i): ${task.inputDirectory.orNull}" } // intermediates/res/merged// . Contains a copy of the resources, "merged" probably in the sense that all values are in a single file called values.xml ?
+ log.d { "compiledDependenciesResources (i): ${task.compiledDependenciesResources.files.joinToString()}" } // empty
+ log.d { "compiledDirectory (o): ${task.compiledDirectory}" } // intermediates/res/compiled// . Contains the input resources compiled to a misterious *.flat format, probably used in APKs.
}
}
}
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/files.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/files.kt
index bd79870..73b9241 100644
--- a/grease/src/main/kotlin/io/deepmedia/tools/grease/files.kt
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/files.kt
@@ -1,6 +1,7 @@
package io.deepmedia.tools.grease
import java.io.File
+import java.util.jar.JarFile
internal fun File.folder(name: String) = File(this, name).also { it.mkdirs() }
@@ -28,4 +29,21 @@ internal fun File.listFilesRecursive(extension: String): List {
internal fun File.relocate(from: File, to: File): File {
assert(absolutePath.startsWith(from.absolutePath)) { "File not contained in $from!" }
return File(absolutePath.replaceFirst(from.absolutePath, to.absolutePath))
-}
\ No newline at end of file
+}
+
+val JarFile.packageNames: Set
+ get() = entries().asSequence().mapNotNull { entry ->
+ if (entry.name.endsWith(".class") && entry.name != "module-info.class") {
+ entry.name.substring(0 until entry.name.lastIndexOf('/')).replace('/', '.')
+ } else null
+ }.toSet()
+
+val File.packageNames: Set
+ get() = listFilesRecursive("class").mapNotNull { file ->
+ if (file.name != "module-info.class") {
+ val cleanedPath = file.path.removePrefix(this.path).removePrefix("/")
+ cleanedPath
+ .substring(0 until cleanedPath.lastIndexOf('/'))
+ .replace('/', '.')
+ } else null
+ }.toSet()
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/merge.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/merge.kt
new file mode 100644
index 0000000..1d3b9de
--- /dev/null
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/merge.kt
@@ -0,0 +1,61 @@
+package io.deepmedia.tools.grease
+
+import com.android.build.gradle.internal.LoggerWrapper
+import com.android.ide.common.blame.MergingLog
+import com.android.ide.common.rendering.api.ResourceNamespace
+import com.android.ide.common.resources.MergedResourceWriter
+import com.android.ide.common.resources.MergedResourceWriterRequest
+import com.android.ide.common.resources.ResourceCompilationService
+import com.android.ide.common.resources.ResourceMerger
+import com.android.ide.common.resources.ResourceSet
+import com.android.ide.common.workers.WorkerExecutorFacade
+import org.gradle.api.logging.Logger
+import java.io.File
+
+internal fun mergeResourcesWithCompilationService(
+ resCompilerService: ResourceCompilationService,
+ incrementalMergedResources: File,
+ mergedResources: File,
+ resourceSets: List,
+ minSdk: Int,
+ aaptWorkerFacade: WorkerExecutorFacade,
+ blameLogOutputFolder: File,
+ logger: Logger) {
+ val mergedResourcesDir = mergedResources.also {
+ it.mkdirs()
+ }
+ val sourcesResourceSet = ResourceSet(
+ null, ResourceNamespace.RES_AUTO, null, false, null
+ ).apply {
+ addSources(resourceSets.reversed())
+ }
+ val resourceMerger = ResourceMerger(minSdk).apply {
+ sourcesResourceSet.loadFromFiles(LoggerWrapper(logger))
+ addDataSet(sourcesResourceSet)
+ }
+ aaptWorkerFacade.use { workerExecutorFacade ->
+ resCompilerService.use { resCompilationService ->
+ val mergeResourcesWriterRequest = MergedResourceWriterRequest(
+ workerExecutor = workerExecutorFacade,
+ rootFolder = mergedResourcesDir,
+ publicFile = null,
+ blameLog = getCleanBlameLog(blameLogOutputFolder),
+ preprocessor = null,
+ resourceCompilationService = resCompilationService,
+ temporaryDirectory = incrementalMergedResources,
+ dataBindingExpressionRemover = null,
+ notCompiledOutputDirectory = null,
+ pseudoLocalesEnabled = false,
+ crunchPng = false,
+ moduleSourceSets = emptyMap()
+ )
+ val writer = MergedResourceWriter(mergeResourcesWriterRequest)
+ resourceMerger.mergeData(writer, true)
+ resourceMerger.writeBlobTo(incrementalMergedResources, writer, false)
+ }
+ }
+}
+
+private fun getCleanBlameLog(blameLogOutputFolder: File): MergingLog {
+ return MergingLog(blameLogOutputFolder)
+}
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/project.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/project.kt
new file mode 100644
index 0000000..6a56952
--- /dev/null
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/project.kt
@@ -0,0 +1,8 @@
+package io.deepmedia.tools.grease
+
+import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
+
+internal val Project.greaseBuildDir: Provider
+ get() = layout.buildDirectory.dir("grease")
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/strings.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/strings.kt
index 6041c4c..d6fb9e3 100644
--- a/grease/src/main/kotlin/io/deepmedia/tools/grease/strings.kt
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/strings.kt
@@ -1,9 +1,13 @@
package io.deepmedia.tools.grease
+import java.util.*
+
internal fun String.greasify() = nameOf("grease", this)
internal fun nameOf(vararg values: String) = values
.filter { it.isNotBlank() }
- .mapIndexed { index, string ->
- if (index == 0) string.decapitalize() else string.capitalize()
- }.joinToString(separator = "")
\ No newline at end of file
+ .mapIndexed { index, string -> if (index == 0) string.decapitalize() else string.capitalize()
+ }.joinToString(separator = "")
+
+private fun String.decapitalize() = replaceFirstChar { it.lowercase(Locale.getDefault()) }
+private fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
\ No newline at end of file
diff --git a/grease/src/main/kotlin/io/deepmedia/tools/grease/task.kt b/grease/src/main/kotlin/io/deepmedia/tools/grease/task.kt
new file mode 100644
index 0000000..fe10791
--- /dev/null
+++ b/grease/src/main/kotlin/io/deepmedia/tools/grease/task.kt
@@ -0,0 +1,70 @@
+package io.deepmedia.tools.grease
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.api.tasks.TaskProvider
+
+/**
+ * Locates a task by [name] and [type], without triggering its creation or configuration.
+ */
+internal fun Project.locateTask(name: String, type: Class): TaskProvider? = tasks.locateTask(name, type)
+
+internal fun Project.locateTask(name: String): TaskProvider? = tasks.locateTask(name)
+
+/**
+ * Locates a task by [name] and [type], without triggering its creation or configuration.
+ */
+internal fun TaskContainer.locateTask(name: String, type: Class): TaskProvider? =
+ if (names.contains(name)) named(name, type) else null
+
+internal fun TaskContainer.locateTask(name: String): TaskProvider? =
+ if (names.contains(name)) named(name) else null
+
+/**
+ * Locates a task by [name] and [type], without triggering its creation or configuration or registers new task
+ * with [name], type [T] and initialization script [body]
+ */
+internal fun Project.locateOrRegisterTask(
+ name: String,
+ type: Class,
+ body: T.() -> (Unit)
+): TaskProvider {
+ return project.locateTask(name, type) ?: project.registerTask(name, type, body = body)
+}
+
+internal fun TaskContainer.locateOrRegisterTask(
+ name: String,
+ type: Class,
+ body: T.() -> (Unit)
+): TaskProvider {
+ return locateTask(name, type) ?: registerTask(name, type, body = body)
+}
+
+@Suppress("UNCHECKED_CAST")
+internal fun TaskContainer.locateOrRegisterTask(name: String, body: Task.() -> (Unit)): TaskProvider {
+ return (locateTask(name, DefaultTask::class.java) ?: registerTask(name, DefaultTask::class.java, body = body)) as TaskProvider
+}
+
+internal fun Project.registerTask(
+ name: String,
+ type: Class,
+ constructorArgs: List = emptyList(),
+ body: (T.() -> (Unit))? = null
+): TaskProvider {
+ return project.tasks.registerTask(name, type, constructorArgs, body)
+}
+
+internal fun TaskContainer.registerTask(
+ name: String,
+ type: Class,
+ constructorArgs: List = emptyList(),
+ body: (T.() -> (Unit))? = null
+): TaskProvider {
+ val resultProvider = register(name, type, *constructorArgs.toTypedArray())
+ if (body != null) {
+ resultProvider.configure(body)
+ }
+ return resultProvider
+}
\ No newline at end of file
diff --git a/grease/src/main/resources/META-INF/gradle-plugins/io.deepmedia.tools.grease.properties b/grease/src/main/resources/META-INF/gradle-plugins/io.deepmedia.tools.grease.properties
deleted file mode 100644
index 7f2d77b..0000000
--- a/grease/src/main/resources/META-INF/gradle-plugins/io.deepmedia.tools.grease.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=io.deepmedia.tools.grease.GreasePlugin
diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts
deleted file mode 100644
index 46124b8..0000000
--- a/sample/build.gradle.kts
+++ /dev/null
@@ -1,60 +0,0 @@
-plugins {
- id("com.android.library")
- id("io.deepmedia.tools.grease")
-}
-
-android {
- setCompileSdkVersion(29)
- ndkVersion = "20.1.5948944"
- defaultConfig {
- setMinSdkVersion(21)
- setTargetSdkVersion(29)
- versionCode = 1
- versionName = "1.0"
-
- // Configure manifest placeholders to test that they are correctly replaced
- // in our manifest processing step.
- manifestPlaceholders["placeholder"] = "replacement"
-
- // Configure native library libgrease to test that it's correctly packed
- // in the output together with those of our dependencies.
- ndk {
- abiFilters.addAll(setOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a"))
- }
-
- // Configure proguard files.
- proguardFiles(getDefaultProguardFile(com.android.build.gradle.ProguardFiles.ProguardFile.OPTIMIZE.fileName), "proguard-rules.pro")
- consumerProguardFile("consumer-rules.pro")
-
- // Configure some flavors for testing configurations.
- flavorDimensions("color", "shape")
- productFlavors.create("blue") { dimension = "color" }
- productFlavors.create("green") { dimension = "color" }
- productFlavors.create("circle") { dimension = "shape" }
- productFlavors.create("triangle") { dimension = "shape" }
- }
-
- buildTypes["debug"].isMinifyEnabled = false
- buildTypes["release"].isMinifyEnabled = true
-
- externalNativeBuild {
- cmake {
- path = file("src/main/CMakeLists.txt")
- }
- }
-}
-
-configurations.configureEach {
- if (name == "greaseGreenCircleDebug") isTransitive = false
-}
-
-dependencies {
- // Includes resource and some manifest changes
- greaseDebug("androidx.core:core:1.3.2")
- // Includes native libraries
- greaseRelease("org.tensorflow:tensorflow-lite:2.3.0")
- // Manifest changes, layout resources
- afterEvaluate {
- add("greaseGreenCircleDebug","com.otaliastudios:cameraview:2.6.3")
- }
-}
\ No newline at end of file
diff --git a/sample/consumer-rules.pro b/sample/consumer-rules.pro
deleted file mode 100644
index 57eb197..0000000
--- a/sample/consumer-rules.pro
+++ /dev/null
@@ -1,3 +0,0 @@
--keepclassmembers class sample.class.from.consumer.rules {
- public *;
-}
\ No newline at end of file
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
deleted file mode 100644
index 7d9d656..0000000
--- a/sample/proguard-rules.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-
--keepattributes SourceFile,LineNumberTable
--keepclassmembers class sample.class.from.default.rules {
- public *;
-}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index daccef8..8c8f6c4 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,2 +1,23 @@
-include(":sample")
-include(":grease")
+pluginManagement {
+ repositories {
+ google()
+ gradlePluginPortal()
+ mavenCentral()
+ }
+}
+
+dependencyResolutionManagement {
+ @Suppress("UnstableApiUsage")
+ repositories {
+ google()
+ gradlePluginPortal()
+ mavenCentral()
+ }
+ versionCatalogs {
+ create("libs")
+ }
+}
+
+rootProject.name = "Grease"
+
+include(":grease")
\ No newline at end of file
diff --git a/tests/gradle.properties b/tests/gradle.properties
new file mode 100644
index 0000000..83df5e3
--- /dev/null
+++ b/tests/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
diff --git a/sample/.gitignore b/tests/sample-dependency-pure/.gitignore
similarity index 100%
rename from sample/.gitignore
rename to tests/sample-dependency-pure/.gitignore
diff --git a/tests/sample-dependency-pure/build.gradle.kts b/tests/sample-dependency-pure/build.gradle.kts
new file mode 100644
index 0000000..c3b88ba
--- /dev/null
+++ b/tests/sample-dependency-pure/build.gradle.kts
@@ -0,0 +1,15 @@
+plugins {
+ alias(libs.plugins.android.library)
+}
+
+android {
+ namespace = "io.deepmedia.tools.grease.sample.dependency.pure"
+ compileSdk = 34
+ defaultConfig {
+ minSdk = 21
+ }
+}
+
+dependencies {
+ // Empty
+}
\ No newline at end of file
diff --git a/tests/sample-dependency-pure/src/main/AndroidManifest.xml b/tests/sample-dependency-pure/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..972c3a8
--- /dev/null
+++ b/tests/sample-dependency-pure/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/tests/sample-dependency-pure/src/main/java/io/deepmedia/tools/grease/sample/dependency/pure/PureDependencyClass.kt b/tests/sample-dependency-pure/src/main/java/io/deepmedia/tools/grease/sample/dependency/pure/PureDependencyClass.kt
new file mode 100644
index 0000000..dd8276f
--- /dev/null
+++ b/tests/sample-dependency-pure/src/main/java/io/deepmedia/tools/grease/sample/dependency/pure/PureDependencyClass.kt
@@ -0,0 +1,5 @@
+package io.deepmedia.tools.grease.sample.dependency.pure
+
+object PureDependencyClass {
+ fun foo() = "bar"
+}
\ No newline at end of file
diff --git a/tests/sample-library/.gitignore b/tests/sample-library/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/tests/sample-library/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/tests/sample-library/build.gradle.kts b/tests/sample-library/build.gradle.kts
new file mode 100644
index 0000000..55b2e61
--- /dev/null
+++ b/tests/sample-library/build.gradle.kts
@@ -0,0 +1,74 @@
+plugins {
+ alias(libs.plugins.android.library)
+ id("io.deepmedia.tools.grease")
+}
+
+grease {
+ relocate()
+}
+
+android {
+ namespace = "io.deepmedia.tools.grease.sample.library"
+ ndkVersion = "23.1.7779620"
+ compileSdk = 34
+
+ defaultConfig {
+ minSdk = 21
+
+ // Configure manifest placeholders to test that they are correctly replaced
+ // in our manifest processing step.
+ manifestPlaceholders["placeholder"] = "replacement"
+
+ // Configure native library libgrease to test that it's correctly packed
+ // in the output together with those of our dependencies.
+ ndk {
+ abiFilters.addAll(setOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a"))
+ }
+
+ // Configure proguard files.
+ proguardFiles(
+ getDefaultProguardFile(com.android.build.gradle.ProguardFiles.ProguardFile.OPTIMIZE.fileName),
+ "proguard-rules.pro"
+ )
+ consumerProguardFile("consumer-rules.pro")
+
+ // Configure some flavors for testing configurations.
+ flavorDimensions.addAll(listOf("color", "shape"))
+ productFlavors {
+ create("blue") { dimension = "color" }
+ create("green") { dimension = "color" }
+ create("circle") { dimension = "shape" }
+ create("triangle") { dimension = "shape" }
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path = file("src/main/CMakeLists.txt")
+ }
+ }
+}
+
+dependencies {
+ grease("androidx.core:core:1.0.0")
+
+ // include deps to pom when publishing
+ api("com.google.android.material:material:1.0.0")
+ // Includes resource and some manifest changes
+ implementation("androidx.lifecycle:lifecycle-runtime:2.8.4")
+
+ // Includes native libraries
+ grease("org.tensorflow:tensorflow-lite:2.3.0")
+ // Manifest changes, layout resources
+ grease("com.otaliastudios:cameraview:2.7.2")
+
+ // Doesn't work. TODO: we need to configure grease configurations so that in case of multiple matching
+ // variants, they prefer one where com.android.build.api.attributes.BuildTypeAttr is set to release
+ // grease(project(":sample-dependency-pure"))
+}
\ No newline at end of file
diff --git a/sample/src/main/assets/sample_asset_2.png b/tests/sample-library/consumer-rules.pro
similarity index 100%
rename from sample/src/main/assets/sample_asset_2.png
rename to tests/sample-library/consumer-rules.pro
diff --git a/tests/sample-library/proguard-rules.pro b/tests/sample-library/proguard-rules.pro
new file mode 100644
index 0000000..c14a015
--- /dev/null
+++ b/tests/sample-library/proguard-rules.pro
@@ -0,0 +1,2 @@
+
+-keepattributes SourceFile,LineNumberTable
\ No newline at end of file
diff --git a/sample/src/main/AndroidManifest.xml b/tests/sample-library/src/main/AndroidManifest.xml
similarity index 90%
rename from sample/src/main/AndroidManifest.xml
rename to tests/sample-library/src/main/AndroidManifest.xml
index b9035a1..706adb1 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/tests/sample-library/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
+
diff --git a/sample/src/main/CMakeLists.txt b/tests/sample-library/src/main/CMakeLists.txt
similarity index 100%
rename from sample/src/main/CMakeLists.txt
rename to tests/sample-library/src/main/CMakeLists.txt
diff --git a/sample/src/main/assets/sample_asset_1 b/tests/sample-library/src/main/assets/sample_asset_1
similarity index 100%
rename from sample/src/main/assets/sample_asset_1
rename to tests/sample-library/src/main/assets/sample_asset_1
diff --git a/sample/src/main/assets/sample_asset_3.log b/tests/sample-library/src/main/assets/sample_asset_2.png
similarity index 100%
rename from sample/src/main/assets/sample_asset_3.log
rename to tests/sample-library/src/main/assets/sample_asset_2.png
diff --git a/tests/sample-library/src/main/assets/sample_asset_3.log b/tests/sample-library/src/main/assets/sample_asset_3.log
new file mode 100644
index 0000000..e69de29
diff --git a/sample/src/main/cc/grease-api.cc b/tests/sample-library/src/main/cc/grease-api.cc
similarity index 100%
rename from sample/src/main/cc/grease-api.cc
rename to tests/sample-library/src/main/cc/grease-api.cc
diff --git a/sample/src/main/java/io/deepmedia/tools/grease/sample/SampleActivity.java b/tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleActivity.java
similarity index 70%
rename from sample/src/main/java/io/deepmedia/tools/grease/sample/SampleActivity.java
rename to tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleActivity.java
index 3923a35..c7e6ec2 100644
--- a/sample/src/main/java/io/deepmedia/tools/grease/sample/SampleActivity.java
+++ b/tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleActivity.java
@@ -1,7 +1,6 @@
-package io.deepmedia.tools.grease.sample;
+package io.deepmedia.tools.grease.sample.library;
import android.app.Activity;
-import android.app.Application;
import android.os.Bundle;
import android.util.Log;
@@ -10,6 +9,7 @@ class SampleActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_layout);
+// int abcActionBarTitleItem = androidx.appcompat.R.layout.abc_action_bar_title_item;
Log.i("SampleActivity", "Something.");
}
}
diff --git a/sample/src/main/java/io/deepmedia/tools/grease/sample/SampleApplication.java b/tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleApplication.java
similarity index 82%
rename from sample/src/main/java/io/deepmedia/tools/grease/sample/SampleApplication.java
rename to tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleApplication.java
index dd06af7..1525fa3 100644
--- a/sample/src/main/java/io/deepmedia/tools/grease/sample/SampleApplication.java
+++ b/tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleApplication.java
@@ -1,4 +1,4 @@
-package io.deepmedia.tools.grease.sample;
+package io.deepmedia.tools.grease.sample.library;
import android.app.Application;
import android.util.Log;
diff --git a/sample/src/main/java/io/deepmedia/tools/grease/sample/SampleView.java b/tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleView.java
similarity index 82%
rename from sample/src/main/java/io/deepmedia/tools/grease/sample/SampleView.java
rename to tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleView.java
index 459666c..6921d51 100644
--- a/sample/src/main/java/io/deepmedia/tools/grease/sample/SampleView.java
+++ b/tests/sample-library/src/main/java/io/deepmedia/tools/grease/sample/library/SampleView.java
@@ -1,4 +1,4 @@
-package io.deepmedia.tools.grease.sample;
+package io.deepmedia.tools.grease.sample.library;
import android.content.Context;
import android.util.Log;
diff --git a/sample/src/main/res/layout/sample_layout.xml b/tests/sample-library/src/main/res/layout/sample_layout.xml
similarity index 82%
rename from sample/src/main/res/layout/sample_layout.xml
rename to tests/sample-library/src/main/res/layout/sample_layout.xml
index 94c192d..08dd18d 100644
--- a/sample/src/main/res/layout/sample_layout.xml
+++ b/tests/sample-library/src/main/res/layout/sample_layout.xml
@@ -1,5 +1,5 @@
-