├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── build.sh ├── common-build-scripts ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── LoadProperties.kt │ ├── MavenRepositoriesDeclaration.kt │ ├── PomConfiguration.kt │ └── Signing.kt ├── common.properties ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── publish.sh ├── publishLocal.sh ├── recomposition-logger-plugin ├── .gitignore ├── build.gradle.kts ├── buildSrc │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── PluginConfig.kt │ │ └── plugin-options-config.gradle.kts ├── compiler-plugin │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── com │ │ └── welltech │ │ └── compiler_plugin │ │ ├── PluginCommandLineProcessor.kt │ │ ├── PluginComponentRegistrar.kt │ │ ├── debug_logger │ │ ├── EmptyLogger.kt │ │ ├── FileLogger.kt │ │ └── Logger.kt │ │ └── generations │ │ ├── Extensions.kt │ │ ├── HighlightGenerationExtension.kt │ │ ├── LogsGenerationExtension.kt │ │ └── logging │ │ ├── DefaultLogProvider.kt │ │ ├── LogProvider.kt │ │ └── RebuggerLogProvider.kt ├── gradle-plugin │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── com │ │ └── welltech │ │ └── gradle_plugin │ │ ├── Dsl.kt │ │ ├── RecompositionLoggerGradlePlugin.kt │ │ └── extension │ │ └── RecompositionLoggerGradleExtension.kt ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle.kts ├── recomposition-logger-support ├── .gitignore ├── build.gradle.kts ├── buildSrc │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── ProjectConfig.kt │ │ ├── Versions.kt │ │ └── kotlin-publish.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── local.properties ├── recomposition-logger-annotations │ ├── .gitignore │ ├── build.gradle.kts │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── welltech │ │ └── recomposition_logger_annotations │ │ ├── DisableLogs.kt │ │ ├── LogArgument.kt │ │ └── highlight │ │ └── HighlightOptions.kt ├── recomposition-logger-runtime │ ├── build.gradle.kts │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── welltech │ │ └── recomposition_logger_runtime │ │ ├── CompositionCounter.kt │ │ ├── LogComposition.kt │ │ └── highlight │ │ └── HighlightComposition.kt └── settings.gradle.kts ├── sample ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── proguard-rules.pro ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── welltech │ │ └── recomposition_logger_sample │ │ └── MainActivity.kt │ └── res │ ├── drawable │ ├── ic_launcher_background.xml │ └── ic_launcher_foreground.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ └── values │ └── strings.xml ├── sample_record.mov └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*.{kt,kts}] 4 | insert_final_newline = true 5 | max_line_length = 140 6 | indent_size = 4 7 | indent_style = space 8 | ij_kotlin_allow_trailing_comma = true 9 | ij_kotlin_allow_trailing_comma_on_call_site = true -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: appBuild 2 | 3 | on: 4 | # Triggers the workflow on pull request events but only for the "main" branch 5 | pull_request: 6 | branches: [ "main" ] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | buildApp: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: zulu 17 | java-version: 17 18 | - uses: android-actions/setup-android@v2 19 | 20 | - name: Run script file 21 | run: | 22 | chmod +x ./build.sh 23 | ./build.sh 24 | shell: bash -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: appRelease 2 | 3 | on: 4 | # Triggers the workflow on push events but only for the "main" branch 5 | push: 6 | branches: [ "main" ] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | buildAppAndPublishToNexus: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: zulu 17 | java-version: 17 18 | - uses: android-actions/setup-android@v2 19 | 20 | - name: Run script file 21 | run: | 22 | chmod +x ./publish.sh 23 | ./publish.sh 24 | shell: bash 25 | env: 26 | OSS_USERNAME: ${{ secrets.OSS_USERNAME }} 27 | OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }} 28 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 29 | SIGNING_PASS: ${{ secrets.SIGNING_PASS }} 30 | 31 | releaseApp: 32 | needs: buildAppAndPublishToNexus 33 | permissions: 34 | contents: write 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v3 38 | 39 | - name: Retrieve the release version 40 | run: | 41 | echo "TAG_NAME=$( (grep -w "version" | cut -d= -f2) > $GITHUB_OUTPUT 42 | id: version 43 | 44 | - name: Retrieve the release body 45 | run: | 46 | message=$(cat CHANGELOG.md | tr '\n' ';;' | grep -oE "\#{1,2}\ *$TAG_NAME[^\#]*(\#|\Z)" | tr ';;' '\n' | sed '/^#/d' | tr -d '-' | sed 's/ //' | tr '\n' ' ' | sed 's/ / - /') 47 | echo "GITHUB_RELEASE_BODY=$message" >> $GITHUB_OUTPUT 48 | id: gitHubReleaseBody 49 | env: 50 | TAG_NAME: ${{ steps.version.outputs.TAG_NAME }} 51 | 52 | - name: Create GitHub Release 53 | uses: ncipollo/release-action@v1 54 | with: 55 | tag: "v${{ steps.version.outputs.TAG_NAME }}" 56 | name: "v${{ steps.version.outputs.TAG_NAME }}" 57 | body: "${{ steps.gitHubReleaseBody.outputs.GITHUB_RELEASE_BODY }}" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | build/ 3 | .gradle/ 4 | 5 | # IDEA 6 | out/ 7 | .idea/ 8 | *.iml 9 | 10 | /local.properties 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.7.4 2 | 3 | - updated kotlin to 1.9.24 and compose compiler to 1.5.14 4 | 5 | # 1.7.3 6 | 7 | - updated kotlin to 1.9.22 and compose compiler to 1.5.9 8 | 9 | # 1.7.2 10 | 11 | - updated kotlin to 1.9.20 and compose compiler to 1.5.5 12 | 13 | # 1.7.1 14 | 15 | - updated kotlin to 1.9.10 and compose compiler to 1.5.3 16 | 17 | # 1.7.0 18 | 19 | - updated kotlin to 1.9.0 and compose compiler to 1.5.2 20 | 21 | # 1.6.1 22 | 23 | - fixed ClassNotFoundException in compose preview 24 | - supportLibDependency replaced with supportLibConfigurationName 25 | 26 | # 1.6.0 27 | 28 | - updated kotlin to 1.8.21 and compose compiler to 1.4.7 29 | 30 | # 1.6.0-alpha01 31 | 32 | - added experimental support of [Rebugger](https://github.com/theapache64/rebugger) 33 | 34 | # 1.5.0 35 | 36 | - updated kotlin to 1.8.20 and compose compiler to 1.4.6 37 | - 38 | # 1.4.0 39 | 40 | - updated kotlin to 1.8.10 and compose compiler to 1.4.3 41 | 42 | # 1.3.0 43 | 44 | - updated kotlin to 1.8.0 and compose compiler to 1.4.0 45 | 46 | # 1.2.1 47 | 48 | - configured CI/CD pipelines for the project 49 | 50 | # 1.2.0 51 | 52 | - updated kotlin to 1.7.20 and compose compiler to 1.3.2 53 | 54 | # 1.1.0 55 | 56 | - updated kotlin to 1.7.0 and compose to 1.2.0 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recomposition Logger 2 | 3 | This plugin makes it easy to debug composable functions by adding logs to each function. 4 | The plugin also highlights composable functions during recomposition 5 | 6 | 7 | 8 | https://user-images.githubusercontent.com/105854390/235658540-90d394cd-9154-4a20-83ae-e94e718da580.mov 9 | 10 | 11 | 12 | ## requirements: 13 | - v1.7.4: kotlin 1.9.24 14 | - v1.7.3: kotlin 1.9.22 15 | - v1.7.2: kotlin 1.9.20 16 | - v1.7.1: kotlin 1.9.10 17 | - v1.7.0: kotlin 1.9.0 18 | - v1.6.x: kotlin 1.8.21 19 | - v1.5.0: kotlin 1.8.20 20 | - v1.4.0: kotlin 1.8.10 21 | - v1.3.0: kotlin 1.8.0 22 | - v1.2.0: kotlin 1.7.20 23 | - v1.1.0: kotlin 1.7.0 24 | 25 | # Integration 26 | ### root build.gradle 27 | ```kotlin 28 | buildscript { 29 | repositories { 30 | // ... 31 | mavenCentral() 32 | } 33 | dependencies { 34 | // ... 35 | classpath("com.welltech:recomposition-logger-plugin:$version") 36 | } 37 | } 38 | ``` 39 | 40 | ### app build.gradle.kts 41 | ```kotlin 42 | plugins { 43 | id("com.welltech.recomposition-logger-plugin") 44 | } 45 | ``` 46 | 47 | Also, you can configure plugin in your app:build.gradle.kts 48 | ```kotlin 49 | recompositionLogger { 50 | tag = "SampleRecomposition" // tag for recomposition logs 51 | } 52 | ``` 53 | 54 | Other plugin options: 55 | - `enabled [Boolean]` - when false, the plugin doesn't add any additional code. By default, it's `false` for release build and `true` for debug build 56 | - `tag [String, default: "RecompositionLog"]` - tag for recomposition logs. 57 | - `useRebugger [Boolean]` - use [Rebugger](https://github.com/theapache64/rebugger) for logging (experimental) 58 | 59 | # Features 60 | 61 | ## Highlighting recomposition 62 | 63 | To enable highlight you should update `debugHighlightOptions` property 64 | 65 | ```kotlin 66 | class YourApplication : Application() { 67 | 68 | override fun onCreate() { 69 | super.onCreate() 70 | debugHighlightOptions = debugHighlightOptions.copy(enabled = true) 71 | } 72 | } 73 | ``` 74 | 75 | You can change `debugHighlightOptions` in runtime: 76 | ```kotlin 77 | @Composable 78 | fun ScreenContent() { 79 | //... 80 | //... 81 | Button( 82 | onClick = { debugHighlightOptions = debugHighlightOptions.copy(enabled = !debugHighlightOptions.enabled) }, 83 | content = { Text(text = "Change highlighting") } 84 | ) 85 | } 86 | ``` 87 | 88 | You can disable logs and highlighting for specific function using annotation `@DisableLogs`. 89 | It may be useful for root composables (like scaffold) 90 | ```kotlin 91 | @Composable 92 | @DisableLogs 93 | fun AppScaffold( 94 | //... 95 | ) { 96 | //... 97 | } 98 | ``` 99 | 100 | You can improve logs by `LogArgument` annotation: 101 | ```kotlin 102 | @Composable 103 | fun Item( 104 | @LogArgument title: String 105 | ) { 106 | Text(text = title) 107 | } 108 | ``` 109 | 110 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | ./gradlew :recomposition-logger-plugin:compiler-plugin:assemble 2 | ./gradlew :recomposition-logger-plugin:gradle-plugin:assemble 3 | ./gradlew :recomposition-logger-support:recomposition-logger-runtime:assemble 4 | ./gradlew :recomposition-logger-support:recomposition-logger-annotations:assemble -------------------------------------------------------------------------------- /common-build-scripts/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | `kotlin-dsl-precompiled-script-plugins` 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } -------------------------------------------------------------------------------- /common-build-scripts/src/main/kotlin/LoadProperties.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Project 2 | import java.io.FileInputStream 3 | import java.util.* 4 | 5 | private val propertiesCache = mutableMapOf() 6 | 7 | private data class PropertiesCache(val properties: Properties, val lastModified: Long) 8 | 9 | fun Project.loadProperties(path: String): Properties { 10 | val cacheKey = "${rootProject.name}/$path" 11 | val cache = propertiesCache[cacheKey] 12 | 13 | val propsFile = rootProject.file(path) 14 | val lastModified = propsFile.lastModified() 15 | if (cache != null && cache.lastModified == lastModified) { 16 | return cache.properties 17 | } 18 | 19 | val newProperties = Properties().apply { 20 | load(FileInputStream(propsFile)) 21 | } 22 | 23 | propertiesCache[cacheKey] = PropertiesCache(newProperties, lastModified) 24 | return newProperties 25 | } -------------------------------------------------------------------------------- /common-build-scripts/src/main/kotlin/MavenRepositoriesDeclaration.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Project 2 | import org.gradle.api.publish.PublishingExtension 3 | 4 | fun PublishingExtension.setupPublishingRepositories(project: Project) { 5 | with(project) { 6 | val ossUsername = System.getenv()["OSS_USERNAME"] 7 | val ossPassword = System.getenv()["OSS_PASSWORD"] 8 | this@setupPublishingRepositories.repositories { 9 | mavenLocal() 10 | maven { 11 | name = "Staging" 12 | mavenContent { 13 | releasesOnly() 14 | } 15 | url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") 16 | credentials { 17 | username = ossUsername 18 | password = ossPassword 19 | } 20 | } 21 | maven { 22 | name = "Snapshot" 23 | mavenContent { 24 | snapshotsOnly() 25 | } 26 | url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") 27 | credentials { 28 | username = ossUsername 29 | password = ossPassword 30 | } 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /common-build-scripts/src/main/kotlin/PomConfiguration.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.publish.maven.MavenPom 2 | 3 | fun MavenPom.setupDefaultPom() { 4 | licenses { 5 | license { 6 | name.set("The Apache License, Version 2.0") 7 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 8 | } 9 | } 10 | 11 | developers { 12 | listOf( 13 | "Evgeniy Kurinnoy", 14 | "Aleksandr Trykashnyi" 15 | ).forEach { devName -> 16 | developer { 17 | name.set(devName) 18 | organization.set("Welltech") 19 | organizationUrl.set("https://welltech.com/") 20 | } 21 | } 22 | } 23 | 24 | scm { 25 | connection.set("scm:git:git://github.com/welltech-com/recompose-logger.git") 26 | developerConnection.set("scm:git:ssh://github.com:welltech-com/recompose-logger.git") 27 | url.set("https://github.com/welltech-com/recompose-logger") 28 | } 29 | } -------------------------------------------------------------------------------- /common-build-scripts/src/main/kotlin/Signing.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.plugins.signing.SigningExtension 2 | 3 | fun SigningExtension.configureGpg() { 4 | isRequired = false 5 | useInMemoryPgpKeys(System.getenv("SIGNING_KEY"), System.getenv("SIGNING_PASS")) 6 | } 7 | -------------------------------------------------------------------------------- /common.properties: -------------------------------------------------------------------------------- 1 | kotlin_version=1.9.24 2 | version=1.7.4 3 | group_id=com.welltech 4 | runtime_artifact=recomposition-logger-runtime 5 | annotations_artifact=recomposition-logger-annotations 6 | plugin_artifact=recomposition-logger-plugin 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 28 15:47:41 EEST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | properties="common.properties" 2 | 3 | while IFS='=' read -r key value 4 | do 5 | key=$(echo $key | tr '.' '_') 6 | eval ${key}=\${value} 7 | done < "$properties" 8 | 9 | if [[ $version == *"-SNAPSHOT" ]]; then 10 | ./gradlew :recomposition-logger-plugin:compiler-plugin:buildAndPublishToSnapshotRepository 11 | ./gradlew :recomposition-logger-plugin:gradle-plugin:buildAndPublishToSnapshotRepository 12 | ./gradlew :recomposition-logger-support:recomposition-logger-runtime:buildAndPublishToSnapshotRepository 13 | ./gradlew :recomposition-logger-support:recomposition-logger-annotations:buildAndPublishToSnapshotRepository 14 | else 15 | ./gradlew :recomposition-logger-plugin:compiler-plugin:buildAndPublishToMavenRepository 16 | ./gradlew :recomposition-logger-plugin:gradle-plugin:buildAndPublishToMavenRepository 17 | ./gradlew :recomposition-logger-support:recomposition-logger-runtime:buildAndPublishToMavenRepository 18 | ./gradlew :recomposition-logger-support:recomposition-logger-annotations:buildAndPublishToMavenRepository 19 | fi -------------------------------------------------------------------------------- /publishLocal.sh: -------------------------------------------------------------------------------- 1 | ./gradlew :recomposition-logger-plugin:compiler-plugin:buildAndPublishToMavenLocal 2 | ./gradlew :recomposition-logger-plugin:gradle-plugin:buildAndPublishToMavenLocal 3 | ./gradlew :recomposition-logger-support:recomposition-logger-runtime:buildAndPublishToMavenLocal 4 | ./gradlew :recomposition-logger-support:recomposition-logger-annotations:buildAndPublishToMavenLocal -------------------------------------------------------------------------------- /recomposition-logger-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | build/ 3 | .gradle/ 4 | 5 | # IDEA 6 | out/ 7 | .idea/ 8 | *.iml 9 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${pluginConfig.kotlinVersion}") 8 | } 9 | } 10 | 11 | subprojects { 12 | repositories { 13 | mavenCentral() 14 | } 15 | 16 | tasks.register("buildAndPublishToMavenLocal") { 17 | dependsOn(tasks.named("assemble"), tasks.named("publishDefaultPublicationToMavenLocal")) 18 | } 19 | 20 | tasks.register("buildAndPublishToMavenRepository") { 21 | dependsOn(tasks.named("assemble"), tasks.named("publishDefaultPublicationToStagingRepository")) 22 | } 23 | 24 | tasks.register("buildAndPublishToSnapshotRepository") { 25 | dependsOn(tasks.named("assemble"), tasks.named("publishDefaultPublicationToSnapshotRepository")) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | import java.io.* 3 | 4 | plugins { 5 | `kotlin-dsl` 6 | `kotlin-dsl-precompiled-script-plugins` 7 | } 8 | 9 | repositories { 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | 14 | dependencies { 15 | implementation(":common-build-scripts") 16 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${getCommonProperties().getProperty("kotlin_version")}") 17 | implementation("com.github.gmazzo:gradle-buildconfig-plugin:3.0.3") 18 | } 19 | 20 | fun getCommonProperties(): Properties { 21 | val propsFile = File(rootDir.parentFile.parentFile, "common.properties") 22 | return Properties().apply { 23 | load(FileInputStream(propsFile)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/buildSrc/src/main/kotlin/PluginConfig.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Project 2 | 3 | val Project.pluginConfig: PluginConfig 4 | get() = PluginConfig(this) 5 | 6 | class PluginConfig internal constructor(private val project: Project) { 7 | private val commonProperties = project.loadProperties("../common.properties") 8 | 9 | val kotlinVersion = commonProperties.getProperty("kotlin_version") 10 | 11 | val group = commonProperties.getProperty("group_id") 12 | val version = commonProperties.getProperty("version") 13 | 14 | val gradlePluginName = commonProperties.getProperty("plugin_artifact") 15 | val gradlePluginId = "$group.$gradlePluginName" 16 | 17 | val compilerPluginName = "recomposition-logger-compiler-plugin" 18 | val compilerPluginId = "$group.$compilerPluginName" 19 | 20 | private val runtimeLibArtifact = commonProperties.getProperty("runtime_artifact") 21 | private val annotationsLibArtifact = commonProperties.getProperty("annotations_artifact") 22 | 23 | val runtimeLib = "$group:$runtimeLibArtifact:$version" 24 | val annotationsLib = "$group:$annotationsLibArtifact:$version" 25 | 26 | val rebuggerLib = "com.github.theapache64:rebugger:1.0.0-alpha02" 27 | } -------------------------------------------------------------------------------- /recomposition-logger-plugin/buildSrc/src/main/kotlin/plugin-options-config.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.github.gmazzo.buildconfig") 3 | } 4 | 5 | buildConfig { 6 | buildConfigField("String", "KEY_RECOMPOSITION_LOGS_ENABLED", "\"recomposition_log_enabled\"") 7 | buildConfigField("boolean", "DEFAULT_RECOMPOSITION_LOGS_ENABLED", "false") 8 | 9 | buildConfigField("String", "KEY_RECOMPOSITION_LOGS_TAG", "\"recomposition_log_tag\"") 10 | buildConfigField("String", "DEFAULT_RECOMPOSITION_LOGS_TAG", "\"RecompositionLog\"") 11 | 12 | buildConfigField("String", "KEY_USE_REBUGGER", "\"use_rebugger\"") 13 | buildConfigField("boolean", "DEFAULT_USE_REBUGGER", "false") 14 | 15 | buildConfigField("String", "KEY_LOG_FILE", "\"log_file\"") 16 | } -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.internal.KaptTask 2 | import org.jetbrains.kotlin.gradle.tasks.Kapt 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 4 | 5 | plugins { 6 | kotlin("jvm") 7 | kotlin("kapt") 8 | id("com.github.gmazzo.buildconfig") 9 | `maven-publish` 10 | `signing` 11 | id("plugin-options-config") 12 | id("org.jetbrains.dokka") version "1.8.20" 13 | } 14 | 15 | group = pluginConfig.group 16 | version = pluginConfig.version 17 | 18 | dependencies { 19 | compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable") 20 | 21 | compileOnly("com.google.auto.service:auto-service-annotations:1.0.1") 22 | kapt("com.google.auto.service:auto-service:1.0.1") 23 | } 24 | 25 | buildConfig { 26 | packageName(pluginConfig.group + ".compiler_plugin") 27 | buildConfigField("String", "KOTLIN_PLUGIN_ID", "\"${pluginConfig.compilerPluginId}\"") 28 | } 29 | 30 | tasks.withType { 31 | kotlinOptions.jvmTarget = "17" 32 | } 33 | 34 | val dokkaHtml by tasks.getting(org.jetbrains.dokka.gradle.DokkaTask::class) { 35 | dependsOn(tasks.withType()) 36 | } 37 | 38 | val javadocJar: TaskProvider by tasks.registering(Jar::class) { 39 | dependsOn(dokkaHtml) 40 | archiveClassifier.set("javadoc") 41 | from(dokkaHtml.outputDirectory) 42 | } 43 | 44 | java { 45 | sourceCompatibility = JavaVersion.VERSION_17 46 | targetCompatibility = JavaVersion.VERSION_17 47 | } 48 | 49 | extensions.configure { 50 | configureGpg() 51 | sign(publishing.publications) 52 | } 53 | 54 | publishing { 55 | setupPublishingRepositories(project) 56 | 57 | publications { 58 | create("default") { 59 | artifactId = pluginConfig.compilerPluginName 60 | from(components["java"]) 61 | artifact(tasks.kotlinSourcesJar) 62 | artifact(javadocJar) 63 | 64 | pom { 65 | setupDefaultPom() 66 | name.set("Recomposition Logger compiler plugin") 67 | description.set("Kotlin compiler plugin, that inject logs into composable functions") 68 | url.set("https://github.com/welltech-com/recompose-logger") 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/PluginCommandLineProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin 2 | 3 | import com.google.auto.service.AutoService 4 | import com.welltech.compiler_plugin.BuildConfig.KEY_RECOMPOSITION_LOGS_ENABLED 5 | import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption 6 | import org.jetbrains.kotlin.compiler.plugin.CliOption 7 | import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor 8 | import org.jetbrains.kotlin.config.CompilerConfiguration 9 | import org.jetbrains.kotlin.config.CompilerConfigurationKey 10 | import com.welltech.compiler_plugin.BuildConfig.KEY_RECOMPOSITION_LOGS_TAG 11 | import com.welltech.compiler_plugin.BuildConfig.KEY_LOG_FILE 12 | import com.welltech.compiler_plugin.BuildConfig.KOTLIN_PLUGIN_ID 13 | import com.welltech.compiler_plugin.BuildConfig.KEY_USE_REBUGGER 14 | import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi 15 | import java.io.File 16 | 17 | @OptIn(ExperimentalCompilerApi::class) 18 | @AutoService(CommandLineProcessor::class) 19 | class PluginCommandLineProcessor : CommandLineProcessor { 20 | object Keys { 21 | val logFile = KEY_LOG_FILE.toConfigKey() 22 | val logEnabled = KEY_RECOMPOSITION_LOGS_ENABLED.toConfigKey() 23 | val logTag = KEY_RECOMPOSITION_LOGS_TAG.toConfigKey() 24 | val useRebugger = KEY_USE_REBUGGER.toConfigKey() 25 | private inline fun String.toConfigKey() = CompilerConfigurationKey(this) 26 | } 27 | 28 | override val pluginId: String = KOTLIN_PLUGIN_ID 29 | 30 | override val pluginOptions: Collection = listOf( 31 | CliOption( 32 | optionName = KEY_LOG_FILE, 33 | valueDescription = "absolute file path", 34 | description = "file for printing compiler debug logs", 35 | required = false, 36 | ), 37 | CliOption( 38 | optionName = KEY_RECOMPOSITION_LOGS_ENABLED, 39 | valueDescription = "bool ", 40 | description = "Add logging for @Composable functions", 41 | required = false, 42 | ), 43 | CliOption( 44 | optionName = KEY_RECOMPOSITION_LOGS_TAG, 45 | valueDescription = "String", 46 | description = "tag for android.util.Log", 47 | required = false, 48 | ), 49 | CliOption( 50 | optionName = KEY_USE_REBUGGER, 51 | valueDescription = "bool ", 52 | description = "use rebugger for logging recompositions. More details: https://github.com/theapache64/rebugger", 53 | required = false, 54 | ), 55 | ) 56 | 57 | override fun processOption( 58 | option: AbstractCliOption, 59 | value: String, 60 | configuration: CompilerConfiguration, 61 | ) { 62 | return when (option.optionName) { 63 | KEY_RECOMPOSITION_LOGS_ENABLED -> configuration.put(Keys.logEnabled, value.toBoolean()) 64 | KEY_RECOMPOSITION_LOGS_TAG -> configuration.put(Keys.logTag, value) 65 | KEY_LOG_FILE -> configuration.put(Keys.logFile, File(value)) 66 | KEY_USE_REBUGGER -> configuration.put(Keys.useRebugger, value.toBoolean()) 67 | else -> throw IllegalArgumentException("Unexpected config option ${option.optionName}") 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/PluginComponentRegistrar.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin 2 | 3 | import com.google.auto.service.AutoService 4 | import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 5 | import org.jetbrains.kotlin.com.intellij.mock.MockProject 6 | import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar 7 | import org.jetbrains.kotlin.config.CompilerConfiguration 8 | import com.welltech.compiler_plugin.generations.HighlightGenerationExtension 9 | import com.welltech.compiler_plugin.generations.LogsGenerationExtension 10 | import com.welltech.compiler_plugin.debug_logger.EmptyLogger 11 | import com.welltech.compiler_plugin.debug_logger.FileLogger 12 | import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi 13 | 14 | @OptIn(ExperimentalCompilerApi::class) 15 | @AutoService(ComponentRegistrar::class) 16 | class PluginComponentRegistrar : ComponentRegistrar { 17 | 18 | override fun registerProjectComponents( 19 | project: MockProject, 20 | configuration: CompilerConfiguration, 21 | ) { 22 | val enabled = configuration.get( 23 | PluginCommandLineProcessor.Keys.logEnabled, 24 | BuildConfig.DEFAULT_RECOMPOSITION_LOGS_ENABLED, 25 | ) 26 | val logTag = configuration.get( 27 | PluginCommandLineProcessor.Keys.logTag, 28 | BuildConfig.DEFAULT_RECOMPOSITION_LOGS_TAG, 29 | ) 30 | 31 | val logFile = configuration.get( 32 | PluginCommandLineProcessor.Keys.logFile, 33 | ) 34 | 35 | val useRebugger = configuration.get( 36 | PluginCommandLineProcessor.Keys.useRebugger, 37 | BuildConfig.DEFAULT_USE_REBUGGER, 38 | ) 39 | 40 | val logger = when (logFile) { 41 | null -> EmptyLogger() 42 | else -> FileLogger(logFile) 43 | } 44 | 45 | if (enabled) { 46 | IrGenerationExtension.registerExtension( 47 | project = project, 48 | extension = LogsGenerationExtension(tag = logTag, useRebugger = useRebugger), 49 | ) 50 | IrGenerationExtension.registerExtension( 51 | project = project, 52 | extension = HighlightGenerationExtension(logger), 53 | ) 54 | } 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/debug_logger/EmptyLogger.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.debug_logger 2 | 3 | class EmptyLogger : Logger { 4 | override fun logMsg(msg: String) { 5 | // do nothing 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/debug_logger/FileLogger.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.debug_logger 2 | 3 | import java.io.File 4 | import java.io.FileOutputStream 5 | 6 | class FileLogger( 7 | logFile: File, 8 | ) : Logger { 9 | 10 | private val file = if (logFile.isDirectory) { 11 | File(logFile, "recomposition-compiler-logs.txt") 12 | } else { 13 | logFile 14 | } 15 | 16 | private val writer by lazy { 17 | FileOutputStream(file, true) 18 | } 19 | 20 | init { 21 | file.delete() 22 | file.createNewFile() 23 | } 24 | 25 | 26 | override fun logMsg(msg: String) { 27 | writer.write(msg.toByteArray()) 28 | writer.write("\n".toByteArray()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/debug_logger/Logger.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.debug_logger 2 | 3 | interface Logger { 4 | fun logMsg(msg: String) 5 | } -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/generations/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.generations 2 | 3 | import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext 4 | import org.jetbrains.kotlin.ir.declarations.IrFunction 5 | import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction 6 | import org.jetbrains.kotlin.ir.util.hasAnnotation 7 | import org.jetbrains.kotlin.name.FqName 8 | 9 | val composableAnnotationName get() = FqName("androidx.compose.runtime.Composable") 10 | val disableLogAnnotationName get() = FqName("com.welltech.recomposition_logger_annotations.DisableLogs") 11 | val logArgumentAnnotationName = FqName("com.welltech.recomposition_logger_annotations.LogArgument") 12 | 13 | fun IrFunction.shouldAddLogsAndHighlight(pluginContext: IrPluginContext): Boolean { 14 | val isAnonymousFunction = this is IrSimpleFunction && (name.toString() == "") 15 | 16 | return body != null 17 | && hasAnnotation(composableAnnotationName) 18 | && !isAnonymousFunction 19 | && !hasAnnotation(disableLogAnnotationName) 20 | && returnType == pluginContext.irBuiltIns.unitType 21 | } 22 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/generations/HighlightGenerationExtension.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.generations 2 | 3 | import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext 4 | import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 5 | import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext 6 | import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder 7 | import org.jetbrains.kotlin.ir.IrStatement 8 | import org.jetbrains.kotlin.ir.builders.* 9 | import org.jetbrains.kotlin.ir.declarations.IrFunction 10 | import org.jetbrains.kotlin.ir.declarations.IrModuleFragment 11 | import org.jetbrains.kotlin.ir.declarations.IrValueParameter 12 | import org.jetbrains.kotlin.ir.expressions.IrCall 13 | import org.jetbrains.kotlin.ir.expressions.IrStatementContainer 14 | import org.jetbrains.kotlin.ir.types.isClassType 15 | import org.jetbrains.kotlin.ir.util.dump 16 | import org.jetbrains.kotlin.ir.util.hasAnnotation 17 | import org.jetbrains.kotlin.ir.util.statements 18 | import org.jetbrains.kotlin.name.FqName 19 | import org.jetbrains.kotlin.name.FqNameUnsafe 20 | import com.welltech.compiler_plugin.debug_logger.Logger 21 | import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI 22 | 23 | class HighlightGenerationExtension( 24 | private val logger: Logger, 25 | ) : IrGenerationExtension { 26 | override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { 27 | moduleFragment.transform(HighlightTransformer(pluginContext, logger), null) 28 | } 29 | } 30 | 31 | @OptIn(FirIncompatiblePluginAPI::class) 32 | private class HighlightTransformer( 33 | private val pluginContext: IrPluginContext, 34 | private val logger: Logger, 35 | ) : IrElementTransformerVoidWithContext() { 36 | 37 | private val funHighlight = 38 | pluginContext.referenceFunctions(FqName("com.welltech.recomposition_logger_runtime.highlight.highlightRecomposition")) 39 | .single { 40 | val parameters = it.owner.valueParameters 41 | parameters.size == 1 42 | && parameters[0].type == pluginContext.irBuiltIns.stringType 43 | } 44 | 45 | private val initialModifier = pluginContext.referenceClass(FqName("androidx.compose.ui.Modifier.Companion"))!! 46 | 47 | override fun visitFunctionNew(declaration: IrFunction): IrStatement { 48 | if (declaration.shouldAddLogsAndHighlight(pluginContext)) { 49 | logger.logMsg("visit new composable fun: ${declaration.name}") 50 | DeclarationIrBuilder(pluginContext, declaration.symbol) 51 | .findAndModifyComposeFunctions(declaration.name.toString(), declaration.body!!.statements) 52 | } 53 | 54 | return super.visitFunctionNew(declaration) 55 | } 56 | 57 | private fun IrBuilderWithScope.findAndModifyComposeFunctions(funName: String, statements: List) { 58 | for (statement in statements) { 59 | logger.logMsg("---statement: ${statement.dump()}") 60 | if (statement.isComposableCallWithModifier()) { 61 | logger.logMsg("-----is Composable call, add highlight modifier") 62 | addHighlightModifier(funName, statement as IrCall) 63 | } else if (statement is IrStatementContainer) { 64 | findAndModifyComposeFunctions(funName, statement.statements) 65 | } 66 | } 67 | } 68 | 69 | private fun IrStatement.isComposableCallWithModifier(): Boolean { 70 | return this is IrCall 71 | && symbol.owner.hasAnnotation(composableAnnotationName) 72 | && getModifierArgument() != null 73 | } 74 | 75 | private fun IrBuilderWithScope.addHighlightModifier(funName: String, irCallStatement: IrCall) { 76 | val modifierArgument = irCallStatement.getModifierArgument() 77 | ?: throw IllegalArgumentException("missing modifier argument in function ${irCallStatement.symbol.owner.name} ") 78 | 79 | val currentModifierValue = irCallStatement.getValueArgument(modifierArgument.index) 80 | logger.logMsg("-----current modifier: ${currentModifierValue?.dump()}") 81 | val newModifierValue = irCall(funHighlight).apply { 82 | extensionReceiver = currentModifierValue ?: irGetObject(initialModifier) 83 | putValueArgument(0, irString(funName)) 84 | } 85 | logger.logMsg("-----new modifier: ${newModifierValue.dump()}") 86 | 87 | irCallStatement.putValueArgument(modifierArgument.index, newModifierValue) 88 | } 89 | 90 | private fun IrCall.getModifierArgument(): IrValueParameter? { 91 | return symbol.owner.valueParameters.firstOrNull { 92 | it.type.isClassType(FqNameUnsafe("androidx.compose.ui.Modifier"), false) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/generations/LogsGenerationExtension.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.generations 2 | 3 | import com.welltech.compiler_plugin.generations.logging.DefaultLogProvider 4 | import com.welltech.compiler_plugin.generations.logging.LogProvider 5 | import com.welltech.compiler_plugin.generations.logging.RebuggerLogProvider 6 | import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext 7 | import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 8 | import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext 9 | import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder 10 | import org.jetbrains.kotlin.ir.IrStatement 11 | import org.jetbrains.kotlin.ir.builders.irBlockBody 12 | import org.jetbrains.kotlin.ir.declarations.IrFunction 13 | import org.jetbrains.kotlin.ir.declarations.IrModuleFragment 14 | import org.jetbrains.kotlin.ir.expressions.IrBlockBody 15 | import org.jetbrains.kotlin.ir.util.statements 16 | 17 | class LogsGenerationExtension( 18 | private val tag: String, 19 | private val useRebugger: Boolean, 20 | ) : IrGenerationExtension { 21 | override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { 22 | val logProvider = if (useRebugger) { 23 | RebuggerLogProvider(pluginContext) 24 | } else { 25 | DefaultLogProvider(tag, pluginContext) 26 | } 27 | 28 | val transformer = ComposeLogTransformer( 29 | pluginContext = pluginContext, 30 | logProvider = logProvider, 31 | ) 32 | moduleFragment.transform(transformer, null) 33 | } 34 | } 35 | 36 | private class ComposeLogTransformer( 37 | private val pluginContext: IrPluginContext, 38 | private val logProvider: LogProvider, 39 | ) : IrElementTransformerVoidWithContext() { 40 | override fun visitFunctionNew(declaration: IrFunction): IrStatement { 41 | if (declaration.shouldAddLogsAndHighlight(pluginContext)) { 42 | declaration.body = withLogComposition(declaration) 43 | } 44 | return super.visitFunctionNew(declaration) 45 | } 46 | 47 | private fun withLogComposition( 48 | function: IrFunction, 49 | ): IrBlockBody { 50 | return DeclarationIrBuilder(pluginContext, function.symbol).irBlockBody { 51 | with(logProvider) { 52 | +logCompositionCall(function) 53 | } 54 | function.body?.statements?.forEach { statement -> 55 | +statement 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/generations/logging/DefaultLogProvider.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.generations.logging 2 | 3 | import com.welltech.compiler_plugin.generations.logArgumentAnnotationName 4 | import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI 5 | import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext 6 | import org.jetbrains.kotlin.ir.builders.* 7 | import org.jetbrains.kotlin.ir.declarations.IrFunction 8 | import org.jetbrains.kotlin.ir.expressions.IrCall 9 | import org.jetbrains.kotlin.ir.expressions.addArgument 10 | import org.jetbrains.kotlin.ir.util.hasAnnotation 11 | import org.jetbrains.kotlin.name.FqName 12 | 13 | class DefaultLogProvider( 14 | private val tag: String, 15 | pluginContext: IrPluginContext, 16 | ) : LogProvider { 17 | private val typeString = pluginContext.irBuiltIns.stringType 18 | 19 | @OptIn(FirIncompatiblePluginAPI::class) 20 | private val funLog = pluginContext.referenceFunctions(FqName("com.welltech.recomposition_logger_runtime.LogComposition")) 21 | .single { 22 | val parameters = it.owner.valueParameters 23 | parameters.size == 2 24 | && parameters[0].type == typeString 25 | && parameters[1].type == typeString 26 | } 27 | 28 | override fun IrBuilderWithScope.logCompositionCall(function: IrFunction): IrCall { 29 | return irCall(funLog).also { call -> 30 | val concat = irConcat() 31 | concat.addArgument(irString("${function.name}")) 32 | 33 | val logParameters = function.valueParameters 34 | .filter { it.hasAnnotation(logArgumentAnnotationName) } 35 | logParameters.forEachIndexed { index, valueParameter -> 36 | if (index == 0) { 37 | concat.addArgument(irString("(")) 38 | } 39 | concat.addArgument(irString("${valueParameter.name}=")) 40 | concat.addArgument(irGet(valueParameter)) 41 | if (index == logParameters.lastIndex) { 42 | concat.addArgument(irString(")")) 43 | } else { 44 | concat.addArgument(irString(", ")) 45 | } 46 | } 47 | call.putValueArgument(0, concat) 48 | call.putValueArgument(1, irString(tag)) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/generations/logging/LogProvider.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.generations.logging 2 | 3 | import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope 4 | import org.jetbrains.kotlin.ir.declarations.IrFunction 5 | import org.jetbrains.kotlin.ir.expressions.IrCall 6 | 7 | interface LogProvider { 8 | fun IrBuilderWithScope.logCompositionCall( 9 | function: IrFunction, 10 | ): IrCall 11 | } 12 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/compiler-plugin/src/main/kotlin/com/welltech/compiler_plugin/generations/logging/RebuggerLogProvider.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.compiler_plugin.generations.logging 2 | 3 | import com.welltech.compiler_plugin.generations.logArgumentAnnotationName 4 | import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI 5 | import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext 6 | import org.jetbrains.kotlin.ir.builders.* 7 | import org.jetbrains.kotlin.ir.declarations.IrFunction 8 | import org.jetbrains.kotlin.ir.expressions.IrCall 9 | import org.jetbrains.kotlin.ir.expressions.IrExpression 10 | import org.jetbrains.kotlin.ir.expressions.addArgument 11 | import org.jetbrains.kotlin.ir.types.createType 12 | import org.jetbrains.kotlin.ir.types.impl.IrTypeBase 13 | import org.jetbrains.kotlin.ir.util.constructors 14 | import org.jetbrains.kotlin.ir.util.hasAnnotation 15 | import org.jetbrains.kotlin.name.ClassId 16 | import org.jetbrains.kotlin.name.FqName 17 | import org.jetbrains.kotlin.name.Name 18 | 19 | class RebuggerLogProvider( 20 | private val pluginContext: IrPluginContext, 21 | ) : LogProvider { 22 | 23 | @OptIn(FirIncompatiblePluginAPI::class) 24 | private val funLog = pluginContext.referenceFunctions(FqName("com.theapache64.rebugger.Rebugger")) 25 | .single { 26 | val parameters = it.owner.valueParameters 27 | parameters.size == 2 28 | } 29 | 30 | @OptIn(FirIncompatiblePluginAPI::class) 31 | private val createMapFun = pluginContext.referenceFunctions(FqName("kotlin.collections.mapOf")) 32 | .single { 33 | val parameters = it.owner.valueParameters 34 | parameters.size == 1 35 | && parameters[0].varargElementType != null 36 | } 37 | 38 | override fun IrBuilderWithScope.logCompositionCall(function: IrFunction): IrCall { 39 | return irCall(funLog).also { call -> 40 | call.putValueArgument(0, createArgumentsMap(function)) 41 | val functionName = irConcat() 42 | functionName.addArgument(irString(function.name.toString())) 43 | function.valueParameters 44 | .filter { it.hasAnnotation(logArgumentAnnotationName) } 45 | .forEach { valueParameter -> 46 | functionName.addArgument(irString(" ${valueParameter.name}=")) 47 | functionName.addArgument(irGet(valueParameter)) 48 | } 49 | call.putValueArgument(1, functionName) 50 | } 51 | } 52 | 53 | private fun IrBuilderWithScope.createArgumentsMap(function: IrFunction): IrExpression { 54 | return irCall(createMapFun).also { call -> 55 | val pairClass = pluginContext.referenceClass(ClassId(FqName("kotlin"), Name.identifier("Pair")))!! 56 | 57 | val values = function.valueParameters.map { inputParameter -> 58 | irCall(pairClass.constructors.first()).also { pairConstructor -> 59 | pairConstructor.putValueArgument(0, irString(inputParameter.name.toString())) 60 | pairConstructor.putValueArgument(1, irGet(inputParameter)) 61 | } 62 | } 63 | 64 | val varargType = pairClass.createType( 65 | hasQuestionMark = false, 66 | arguments = listOf(pluginContext.irBuiltIns.stringType, pluginContext.irBuiltIns.anyType).map { 67 | it as IrTypeBase 68 | }, 69 | ) 70 | 71 | call.putValueArgument(0, irVararg(varargType, values)) 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.internal.KaptTask 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 3 | 4 | plugins { 5 | id("java-gradle-plugin") 6 | kotlin("jvm") 7 | id("com.github.gmazzo.buildconfig") 8 | id("plugin-options-config") 9 | `maven-publish` 10 | `signing` 11 | id("org.jetbrains.dokka") version "1.8.20" 12 | } 13 | 14 | version = pluginConfig.version 15 | group = pluginConfig.group 16 | 17 | dependencies { 18 | implementation(kotlin("stdlib")) 19 | implementation(kotlin("gradle-plugin-api")) 20 | implementation(kotlin("gradle-plugin")) 21 | } 22 | 23 | buildConfig { 24 | packageName(pluginConfig.group + ".gradle_plugin") 25 | buildConfigField("String", "KOTLIN_VERSION", "\"${pluginConfig.kotlinVersion}\"") 26 | buildConfigField("String", "KOTLIN_PLUGIN_ID", "\"${pluginConfig.compilerPluginId}\"") 27 | buildConfigField("String", "KOTLIN_PLUGIN_GROUP", "\"${pluginConfig.group}\"") 28 | buildConfigField("String", "KOTLIN_PLUGIN_NAME", "\"${pluginConfig.compilerPluginName}\"") 29 | buildConfigField("String", "KOTLIN_PLUGIN_VERSION", "\"${pluginConfig.version}\"") 30 | buildConfigField("String", "RUNTIME_LIB", "\"${pluginConfig.runtimeLib}\"") 31 | buildConfigField("String", "ANNOTATIONS_LIB", "\"${pluginConfig.annotationsLib}\"") 32 | buildConfigField("String", "REBUGGER_LIB", "\"${pluginConfig.rebuggerLib}\"") 33 | } 34 | 35 | tasks.withType { 36 | kotlinOptions.jvmTarget = "17" 37 | } 38 | 39 | val dokkaHtml by tasks.getting(org.jetbrains.dokka.gradle.DokkaTask::class) { 40 | dependsOn(tasks.withType()) 41 | } 42 | val javadocJar: TaskProvider by tasks.registering(Jar::class) { 43 | dependsOn(dokkaHtml) 44 | archiveClassifier.set("javadoc") 45 | from(dokkaHtml.outputDirectory) 46 | } 47 | 48 | java { 49 | sourceCompatibility = JavaVersion.VERSION_17 50 | targetCompatibility = JavaVersion.VERSION_17 51 | } 52 | 53 | gradlePlugin { 54 | plugins { 55 | create(pluginConfig.gradlePluginName) { 56 | id = pluginConfig.gradlePluginId 57 | displayName = "Recomposition logger gradle plugin" 58 | implementationClass = "com.welltech.gradle_plugin.RecompositionLoggerGradlePlugin" 59 | } 60 | } 61 | } 62 | 63 | extensions.configure { 64 | configureGpg() 65 | sign(publishing.publications) 66 | } 67 | 68 | publishing { 69 | setupPublishingRepositories(project) 70 | 71 | publications { 72 | create("default") { 73 | artifactId = pluginConfig.gradlePluginName 74 | from(components["java"]) 75 | artifact(tasks.kotlinSourcesJar) 76 | artifact(javadocJar) 77 | 78 | pom { 79 | setupDefaultPom() 80 | name.set("Recomposition Logger gradle plugin") 81 | description.set( 82 | "This plugin makes it easy to debug composable functions by adding logs to each function.\n" + 83 | "The plugin also highlights composable functions during recomposition" 84 | ) 85 | url.set("https://github.com/welltech-com/recompose-logger") 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle-plugin/src/main/kotlin/com/welltech/gradle_plugin/Dsl.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.gradle_plugin 2 | 3 | import org.gradle.api.Project 4 | import com.welltech.gradle_plugin.extension.RecompositionLoggerGradleExtension 5 | 6 | fun Project.recompositionLogger(configuration: RecompositionLoggerGradleExtension.() -> Unit) { 7 | extensions.configure("recompositionLogger", configuration) 8 | } -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle-plugin/src/main/kotlin/com/welltech/gradle_plugin/RecompositionLoggerGradlePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.gradle_plugin 2 | 3 | import com.welltech.gradle_plugin.extension.RecompositionLoggerGradleExtension 4 | import org.gradle.api.Project 5 | import org.gradle.api.provider.Provider 6 | import org.jetbrains.kotlin.gradle.plugin.* 7 | 8 | class RecompositionLoggerGradlePlugin : KotlinCompilerPluginSupportPlugin { 9 | override fun apply(target: Project): Unit = with(target) { 10 | extensions.create("recompositionLogger", RecompositionLoggerGradleExtension::class.java) 11 | afterEvaluate { 12 | val extension = project.extensions.getByType(RecompositionLoggerGradleExtension::class.java) 13 | val supportLibDependency = extension.supportLibConfigurationName ?: "debugImplementation" 14 | val useRebugger = extension.useRebugger ?: BuildConfig.DEFAULT_USE_REBUGGER 15 | 16 | val runtimeLibs = if (useRebugger) { 17 | listOf(BuildConfig.RUNTIME_LIB, BuildConfig.REBUGGER_LIB) 18 | } else { 19 | listOf(BuildConfig.RUNTIME_LIB) 20 | } 21 | 22 | runtimeLibs.forEach { dependencies.add(supportLibDependency, it) } 23 | 24 | dependencies.add("implementation", BuildConfig.ANNOTATIONS_LIB) 25 | } 26 | } 27 | 28 | override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean { 29 | return true 30 | } 31 | 32 | override fun getCompilerPluginId(): String = BuildConfig.KOTLIN_PLUGIN_ID 33 | 34 | override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact( 35 | groupId = BuildConfig.KOTLIN_PLUGIN_GROUP, 36 | artifactId = BuildConfig.KOTLIN_PLUGIN_NAME, 37 | version = BuildConfig.KOTLIN_PLUGIN_VERSION, 38 | ) 39 | 40 | override fun applyToCompilation( 41 | kotlinCompilation: KotlinCompilation<*>, 42 | ): Provider> { 43 | val project = kotlinCompilation.target.project 44 | val extension = project.extensions.getByType(RecompositionLoggerGradleExtension::class.java) 45 | 46 | val logFile = extension.logFile 47 | 48 | val pluginEnabled = extension.enabled ?: kotlin.run { 49 | project.gradle.startParameter.taskRequests.any { 50 | it.args.any { it.endsWith("Debug") } 51 | } 52 | } 53 | if (pluginEnabled) { 54 | val kotlinVersion = project.getKotlinPluginVersion() 55 | if (kotlinVersion != BuildConfig.KOTLIN_VERSION) { 56 | error("Require kotlin version ${BuildConfig.KOTLIN_VERSION} but current: $kotlinVersion") 57 | } 58 | } 59 | val logsTag = extension.tag ?: BuildConfig.DEFAULT_RECOMPOSITION_LOGS_TAG 60 | val useRebugger = extension.useRebugger ?: BuildConfig.DEFAULT_USE_REBUGGER 61 | 62 | if (useRebugger) { 63 | project.repositories.maven { 64 | it.setUrl("https://jitpack.io") 65 | } 66 | } 67 | 68 | return project.provider { 69 | listOfNotNull( 70 | SubpluginOption( 71 | key = BuildConfig.KEY_RECOMPOSITION_LOGS_ENABLED, 72 | value = pluginEnabled.toString(), 73 | ), 74 | SubpluginOption( 75 | key = BuildConfig.KEY_RECOMPOSITION_LOGS_TAG, 76 | value = logsTag, 77 | ), 78 | logFile?.let { 79 | SubpluginOption( 80 | key = BuildConfig.KEY_LOG_FILE, 81 | value = it.absolutePath, 82 | ) 83 | }, 84 | SubpluginOption( 85 | key = BuildConfig.KEY_USE_REBUGGER, 86 | value = useRebugger.toString(), 87 | ), 88 | ) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle-plugin/src/main/kotlin/com/welltech/gradle_plugin/extension/RecompositionLoggerGradleExtension.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.gradle_plugin.extension 2 | 3 | import java.io.File 4 | 5 | abstract class RecompositionLoggerGradleExtension { 6 | 7 | abstract var logFile: File? 8 | 9 | /** 10 | * available: none, implementation, api, compileOnly. Default - implementation 11 | */ 12 | @Deprecated("use supportLibConfigurationName instead", level = DeprecationLevel.ERROR) 13 | abstract var supportLibDependency: String? 14 | 15 | /** 16 | * configuration name for dependencies. By default: debugImplementation 17 | */ 18 | abstract var supportLibConfigurationName: String? 19 | 20 | abstract var enabled: Boolean? 21 | 22 | abstract var tag: String? 23 | 24 | abstract var useRebugger: Boolean? 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/recomposition-logger-plugin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Mon Jan 24 15:52:19 EET 2022 8 | sdk.dir=/Users/evgeniy/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /recomposition-logger-plugin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "recomposition-logger-plugin" 2 | 3 | include(":gradle-plugin") 4 | include(":compiler-plugin") 5 | includeBuild("../common-build-scripts") 6 | -------------------------------------------------------------------------------- /recomposition-logger-support/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /recomposition-logger-support/build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath("com.android.tools.build:gradle:8.0.2") 8 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${config.kotlinVersion}") 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | mavenCentral() 16 | mavenLocal() 17 | } 18 | 19 | project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class).all { 20 | kotlinOptions { 21 | jvmTarget = JavaVersion.VERSION_17.toString() 22 | freeCompilerArgs = freeCompilerArgs + listOf( 23 | "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi", 24 | "-Xopt-in=kotlin.ExperimentalStdlibApi", 25 | "-Xopt-in=kotlin.contracts.ExperimentalContracts", 26 | "-Xopt-in=kotlin.RequiresOptIn", 27 | "-Xjvm-default=all", 28 | ) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /recomposition-logger-support/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | `kotlin-dsl-precompiled-script-plugins` 4 | } 5 | 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | implementation(":common-build-scripts") 13 | } -------------------------------------------------------------------------------- /recomposition-logger-support/buildSrc/src/main/kotlin/ProjectConfig.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Project 2 | import java.io.FileInputStream 3 | import java.util.* 4 | 5 | val Project.config: ProjectConfig 6 | get() = ProjectConfig(this) 7 | 8 | class ProjectConfig internal constructor(private val project: Project) { 9 | private val commonProperties = project.loadProperties("../common.properties") 10 | 11 | val kotlinVersion = commonProperties.getProperty("kotlin_version") 12 | 13 | val group = commonProperties.getProperty("group_id") 14 | val version = commonProperties.getProperty("version") 15 | 16 | val runtimeLibArtifact = commonProperties.getProperty("runtime_artifact") 17 | val annotationsLibArtifact = commonProperties.getProperty("annotations_artifact") 18 | } -------------------------------------------------------------------------------- /recomposition-logger-support/buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | object Versions { 2 | val composeToolingVersion = "1.5.0" 3 | val composeCompilerVersion = "1.5.14" 4 | } 5 | -------------------------------------------------------------------------------- /recomposition-logger-support/buildSrc/src/main/kotlin/kotlin-publish.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `maven-publish` 3 | `signing` 4 | } 5 | 6 | extensions.configure { 7 | configureGpg() 8 | sign(publishing.publications) 9 | } 10 | 11 | 12 | project.afterEvaluate { 13 | tasks.withType { 14 | dependsOn(tasks.named("bundleReleaseAar")) 15 | } 16 | 17 | tasks.withType { 18 | dependsOn(tasks.named("bundleReleaseAar")) 19 | } 20 | } 21 | 22 | publishing { 23 | setupPublishingRepositories(project) 24 | 25 | task("sourceJar", Jar::class) { 26 | from("$projectDir/src/main/java") 27 | archiveClassifier.set("sources") 28 | } 29 | 30 | publications { 31 | create("AndroidLibrary", MavenPublication::class).apply { 32 | groupId = config.group 33 | version = config.version 34 | 35 | artifact("$buildDir/outputs/aar/${project.name}-release.aar") 36 | artifact(tasks.getByName("sourceJar")) 37 | 38 | pom { 39 | setupDefaultPom() 40 | 41 | withXml { 42 | asNode().apply { 43 | // dependencies 44 | appendNode("dependencies").apply { 45 | configurations.getByName("releaseCompileClasspath") 46 | .resolvedConfiguration 47 | .firstLevelModuleDependencies 48 | .forEach { 49 | if (it.moduleVersion != "unspecified") { 50 | val dependency = appendNode("dependency") 51 | dependency.appendNode("groupId", it.moduleGroup) 52 | dependency.appendNode("artifactId", it.moduleName) 53 | dependency.appendNode("version", it.moduleVersion) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | tasks.register("buildAndPublishToMavenLocal") { 64 | dependsOn(tasks.named("assemble"), tasks.named("publishAndroidLibraryPublicationToMavenLocal")) 65 | } 66 | 67 | tasks.register("buildAndPublishToMavenRepository") { 68 | dependsOn(tasks.named("assemble"), tasks.named("publishAndroidLibraryPublicationToStagingRepository")) 69 | } 70 | 71 | tasks.register("buildAndPublishToSnapshotRepository") { 72 | dependsOn(tasks.named("assemble"), tasks.named("publishAndroidLibraryPublicationToSnapshotRepository")) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /recomposition-logger-support/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /recomposition-logger-support/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/recomposition-logger-support/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /recomposition-logger-support/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Dec 24 12:25:34 EET 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /recomposition-logger-support/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /recomposition-logger-support/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Fri May 20 13:49:59 EEST 2022 8 | sdk.dir=/Users/evgeniy/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("kotlin-android") 4 | id("kotlin-publish") 5 | } 6 | 7 | android { 8 | namespace = "com.welltech.recomposition_logger_annotations" 9 | compileSdk = 33 10 | 11 | defaultConfig { 12 | minSdk = 21 13 | targetSdk = 33 14 | 15 | testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" 16 | consumerProguardFile("consumer-rules.pro") 17 | } 18 | 19 | buildTypes { 20 | release { 21 | isMinifyEnabled = false 22 | setProguardFiles( 23 | listOf( 24 | getDefaultProguardFile("proguard-android-optimize.txt"), 25 | file("proguard-rules.pro") 26 | ) 27 | ) 28 | } 29 | } 30 | 31 | buildFeatures { 32 | buildConfig = false 33 | } 34 | 35 | compileOptions { 36 | sourceCompatibility = JavaVersion.VERSION_17 37 | targetCompatibility = JavaVersion.VERSION_17 38 | } 39 | } 40 | 41 | publishing { 42 | publications { 43 | getByName("AndroidLibrary").apply { 44 | artifactId = config.annotationsLibArtifact 45 | pom { 46 | name.set("Recomposition Logger annotations") 47 | description.set("Support library for Recomposition Logger plugin that contains annotations") 48 | url.set("https://github.com/welltech-com/recompose-logger") 49 | } 50 | } 51 | } 52 | } 53 | 54 | dependencies { 55 | compileOnly("androidx.compose.ui:ui-tooling:${Versions.composeToolingVersion}") 56 | } 57 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/recomposition-logger-support/recomposition-logger-annotations/consumer-rules.pro -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/src/main/java/com/welltech/recomposition_logger_annotations/DisableLogs.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_annotations 2 | 3 | /** 4 | * disable logs and highlighting for composable function 5 | */ 6 | @Target(AnnotationTarget.FUNCTION) 7 | @Retention(AnnotationRetention.SOURCE) 8 | annotation class DisableLogs -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/src/main/java/com/welltech/recomposition_logger_annotations/LogArgument.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_annotations 2 | 3 | @Target(AnnotationTarget.VALUE_PARAMETER) 4 | @Retention(AnnotationRetention.SOURCE) 5 | annotation class LogArgument 6 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-annotations/src/main/java/com/welltech/recomposition_logger_annotations/highlight/HighlightOptions.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_annotations.highlight 2 | 3 | import androidx.compose.runtime.getValue 4 | import androidx.compose.runtime.mutableStateOf 5 | import androidx.compose.runtime.setValue 6 | import androidx.compose.ui.graphics.Color 7 | 8 | var debugHighlightOptions by mutableStateOf(HighlightOptions()) 9 | 10 | data class HighlightOptions( 11 | val enabled: Boolean = false, 12 | val durationMillis: Long = 100, 13 | val normalColor: Color = Color.Green, 14 | val recompositionColor: Color = Color.Red, 15 | ) 16 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("kotlin-android") 4 | id("kotlin-publish") 5 | } 6 | 7 | android { 8 | namespace = "com.welltech.recomposition_logger_support" 9 | compileSdk = 33 10 | 11 | defaultConfig { 12 | minSdk = 21 13 | targetSdk = 33 14 | 15 | testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" 16 | consumerProguardFile("consumer-rules.pro") 17 | } 18 | 19 | buildTypes { 20 | debug { 21 | isMinifyEnabled = false 22 | } 23 | release { 24 | isMinifyEnabled = false 25 | setProguardFiles( 26 | listOf( 27 | getDefaultProguardFile("proguard-android-optimize.txt"), 28 | file("proguard-rules.pro") 29 | ) 30 | ) 31 | } 32 | } 33 | 34 | buildFeatures { 35 | compose = true 36 | buildConfig = false 37 | } 38 | 39 | composeOptions { 40 | kotlinCompilerExtensionVersion = Versions.composeCompilerVersion 41 | } 42 | 43 | compileOptions { 44 | sourceCompatibility = JavaVersion.VERSION_17 45 | targetCompatibility = JavaVersion.VERSION_17 46 | } 47 | } 48 | 49 | publishing { 50 | publications { 51 | getByName("AndroidLibrary").apply { 52 | artifactId = config.runtimeLibArtifact 53 | pom { 54 | name.set("Recomposition Logger runtime") 55 | description.set("Support library for Recomposition Logger plugin that contains runtime methods") 56 | url.set("https://github.com/welltech-com/recompose-logger") 57 | } 58 | } 59 | } 60 | } 61 | 62 | dependencies { 63 | compileOnly("androidx.compose.ui:ui-tooling:${Versions.composeToolingVersion}") 64 | compileOnly(project(":recomposition-logger-annotations")) 65 | } 66 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/recomposition-logger-support/recomposition-logger-runtime/consumer-rules.pro -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/src/main/java/com/welltech/recomposition_logger_runtime/CompositionCounter.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_runtime 2 | 3 | /** 4 | * Wrapper over the Int value to prevent recomposition after changing [value] 5 | * 6 | * @property value current amount of recomposition 7 | */ 8 | @PublishedApi 9 | internal class CompositionCounter(var value: Int) -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/src/main/java/com/welltech/recomposition_logger_runtime/LogComposition.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_runtime 2 | 3 | import android.util.Log 4 | import androidx.compose.runtime.* 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.launch 7 | 8 | /** 9 | * An effect which logs the number compositions at the invoked point of the slot table. 10 | * Reference source [chrisbanes/tivi](https://github.com/chrisbanes/tivi/blob/main/common-ui-compose/src/main/java/app/tivi/common/compose/Debug.kt) 11 | * 12 | * This is an inline function to act as like a C-style #include to the host composable function. 13 | * That way we track it's compositions, not this function's compositions. 14 | * 15 | * Log example: 16 | * 17 | * RecompositionLogs: #"WorkoutItem id: 15" recomposed 1 times 18 | * 19 | * RecompositionLogs: #"WorkoutItem id: 15" recomposed 2 times 20 | * 21 | * RecompositionLogs: #"WorkoutItem id: 15" recomposed 3 times 22 | * 23 | * @param name name of the composition to identify it in the logcat. 24 | * @param tag Log tag used for [Log.i] 25 | */ 26 | @Composable 27 | @NonRestartableComposable 28 | fun LogComposition( 29 | name: String, 30 | tag: String, 31 | ) { 32 | val scope = rememberCoroutineScope(getContext = { Dispatchers.Default }) 33 | val counter = remember { CompositionCounter(0) } 34 | SideEffect { 35 | val amount = ++counter.value 36 | if (amount > 1) { 37 | scope.launch { 38 | Log.i(tag, "#\"$name\" recomposed $amount times") 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /recomposition-logger-support/recomposition-logger-runtime/src/main/java/com/welltech/recomposition_logger_runtime/highlight/HighlightComposition.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_runtime.highlight 2 | 3 | import androidx.compose.runtime.* 4 | import androidx.compose.ui.Modifier 5 | import androidx.compose.ui.composed 6 | import androidx.compose.ui.draw.drawWithCache 7 | import androidx.compose.ui.geometry.Offset 8 | import androidx.compose.ui.geometry.Size 9 | import androidx.compose.ui.graphics.Color 10 | import androidx.compose.ui.graphics.Paint 11 | import androidx.compose.ui.graphics.drawscope.Stroke 12 | import androidx.compose.ui.graphics.nativeCanvas 13 | import androidx.compose.ui.platform.debugInspectorInfo 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.unit.sp 16 | import com.welltech.recomposition_logger_annotations.highlight.debugHighlightOptions 17 | import com.welltech.recomposition_logger_runtime.CompositionCounter 18 | import kotlinx.coroutines.delay 19 | 20 | /** 21 | * Draw rect around composable to which this modifier is applied. 22 | * Rect color depended on count of recomposition: [debugHighlightOptions.normalColor] - recomposition count is 0, else - [debugHighlightOptions.recompositionColor]. 23 | * Count of recomposition are dropped after [debugHighlightOptions.durationMillis] ms if during this time no recomposition has occurred 24 | * 25 | * This method designed for using with recomposition-plugin and does not involve manual use 26 | */ 27 | fun Modifier.highlightRecomposition(funName: String) = composed( 28 | inspectorInfo = debugInspectorInfo { 29 | name = "recomposeHighlighter" 30 | properties["funName"] = funName 31 | }, 32 | ) { 33 | if (debugHighlightOptions.enabled.not()) { 34 | return@composed this 35 | } 36 | val duration = debugHighlightOptions.durationMillis 37 | 38 | val totalCompositions = remember { CompositionCounter(-1) } 39 | totalCompositions.value++ 40 | 41 | val totalCompositionsAtLastTimeout = remember { mutableStateOf(0) } 42 | 43 | LaunchedEffect(totalCompositions.value) { 44 | delay(duration) 45 | totalCompositionsAtLastTimeout.value = totalCompositions.value 46 | } 47 | 48 | drawWithCache { 49 | val paint = Paint().apply { 50 | this.color = Color.White 51 | this.isAntiAlias = true 52 | }.asFrameworkPaint() 53 | paint.textSize = 8.sp.toPx() 54 | 55 | val textWidth = paint.measureText(funName) 56 | 57 | onDrawWithContent { 58 | drawContent() 59 | 60 | val numCompositionsSinceTimeout = 61 | totalCompositions.value - totalCompositionsAtLastTimeout.value 62 | 63 | val hasValidBorderParams = size.minDimension > 0f 64 | if (!hasValidBorderParams) { 65 | return@onDrawWithContent 66 | } 67 | 68 | val color = if (numCompositionsSinceTimeout > 0) { 69 | debugHighlightOptions.recompositionColor 70 | } else { 71 | debugHighlightOptions.normalColor 72 | } 73 | 74 | drawRect(color = color, style = Stroke(1.dp.toPx())) 75 | drawRect( 76 | color = Color.Black, 77 | topLeft = Offset(size.width - textWidth, 0f), 78 | size = Size(textWidth, 10.sp.toPx()), 79 | alpha = 0.5f, 80 | ) 81 | drawContext.canvas.nativeCanvas.drawText(funName, size.width - textWidth, 20f, paint) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /recomposition-logger-support/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "recomposition-logger-support" 2 | 3 | include(":recomposition-logger-runtime") 4 | include(":recomposition-logger-annotations") 5 | includeBuild("../common-build-scripts") -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /local.properties -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext{ 3 | kotlin_version = '1.9.24' 4 | compose_bom_version = '2024.05.00' 5 | compose_compiler_version = '1.5.14' 6 | } 7 | 8 | repositories { 9 | google() 10 | mavenCentral() 11 | mavenLocal() 12 | } 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:8.0.2' 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 16 | classpath 'com.welltech:recomposition-logger-plugin:1.7.3' 17 | } 18 | } 19 | 20 | apply plugin: 'com.android.application' 21 | apply plugin: 'kotlin-android' 22 | apply plugin: 'com.welltech.recomposition-logger-plugin' 23 | 24 | repositories { 25 | google() 26 | mavenCentral() 27 | mavenLocal() 28 | } 29 | 30 | android { 31 | compileSdk 34 32 | 33 | defaultConfig { 34 | applicationId "com.welltech.recomposition_logger_sample" 35 | minSdk 26 36 | targetSdk 34 37 | versionCode 1 38 | versionName "1.0" 39 | 40 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 41 | } 42 | 43 | buildTypes { 44 | debug { 45 | applicationIdSuffix = ".debug" 46 | } 47 | release { 48 | minifyEnabled false 49 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 50 | } 51 | } 52 | compileOptions { 53 | sourceCompatibility JavaVersion.VERSION_17 54 | targetCompatibility JavaVersion.VERSION_17 55 | } 56 | kotlinOptions { 57 | jvmTarget = '17' 58 | } 59 | 60 | buildFeatures{ 61 | viewBinding = true 62 | compose true 63 | } 64 | 65 | composeOptions { 66 | kotlinCompilerExtensionVersion compose_compiler_version 67 | } 68 | namespace 'com.welltech.recomposition_logger_sample' 69 | } 70 | 71 | recompositionLogger { 72 | enabled = true 73 | tag = "SampleRecomposition" // tag for recomposition logs 74 | logFile = projectDir 75 | useRebugger = true 76 | } 77 | 78 | dependencies { 79 | 80 | implementation 'androidx.core:core-ktx:1.9.0' 81 | implementation 'androidx.appcompat:appcompat:1.6.0' 82 | implementation 'com.google.android.material:material:1.8.0' 83 | 84 | implementation platform("androidx.compose:compose-bom:$compose_bom_version") 85 | implementation "androidx.compose.ui:ui" 86 | implementation "androidx.compose.ui:ui-tooling" 87 | implementation "androidx.compose.foundation:foundation" 88 | implementation "androidx.compose.material:material" 89 | 90 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 91 | } 92 | repositories { 93 | mavenCentral() 94 | } -------------------------------------------------------------------------------- /sample/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | android.defaults.buildfeatures.buildconfig=true 23 | android.nonTransitiveRClass=false 24 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /sample/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/sample/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sample/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /sample/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /sample/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "sample" 2 | 3 | // replace maven dependencies with project dependencies 4 | includeBuild("../recomposition-logger-support") { 5 | dependencySubstitution { 6 | substitute(module("com.welltech:recomposition-logger-annotations")).using(project(":recomposition-logger-annotations")) 7 | substitute(module("com.welltech:recomposition-logger-runtime")).using(project(":recomposition-logger-runtime")) 8 | } 9 | } 10 | includeBuild("../recomposition-logger-plugin") { 11 | dependencySubstitution { 12 | substitute(module("com.welltech:recomposition-logger-plugin")).using(project(":gradle-plugin")) 13 | substitute(module("com.welltech:recomposition-logger-compiler-plugin")).using(project(":compiler-plugin")) 14 | } 15 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sample/src/main/java/com/welltech/recomposition_logger_sample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.welltech.recomposition_logger_sample 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.material.* 7 | import androidx.compose.runtime.* 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.unit.dp 11 | import com.welltech.recomposition_logger_annotations.DisableLogs 12 | import com.welltech.recomposition_logger_annotations.LogArgument 13 | import com.welltech.recomposition_logger_annotations.highlight.debugHighlightOptions 14 | import com.welltech.recomposition_logger_sample.databinding.ActivityMainBinding 15 | import kotlin.random.Random 16 | 17 | class MainActivity : AppCompatActivity() { 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | val binding = ActivityMainBinding.inflate(layoutInflater) 22 | setContentView(binding.root) 23 | debugHighlightOptions = debugHighlightOptions.copy(enabled = true) 24 | binding.root.setContent { Content() } 25 | } 26 | 27 | @Composable 28 | @DisableLogs 29 | private fun Content() { 30 | val colors = /*if (isSystemInDarkTheme()) darkColors() else*/ lightColors() 31 | MaterialTheme(colors = colors) { 32 | Box(modifier = Modifier.padding(2.dp)) { 33 | var itemCount by remember { 34 | mutableStateOf(5) 35 | } 36 | Column( 37 | modifier = Modifier.fillMaxSize(), 38 | ) { 39 | (0..itemCount).forEach { 40 | // set random string for force the Item to recompose 41 | Item( 42 | id = it, 43 | string = randomString(), 44 | ) 45 | } 46 | } 47 | Row( 48 | modifier = Modifier.align(Alignment.BottomCenter), 49 | horizontalArrangement = Arrangement.spacedBy(16.dp), 50 | ) { 51 | Button( 52 | onClick = { itemCount += 1 }, 53 | content = { Text(text = "Add") }, 54 | ) 55 | 56 | Button( 57 | onClick = { debugHighlightOptions = debugHighlightOptions.copy(enabled = !debugHighlightOptions.enabled) }, 58 | content = { Text(text = "Change highlighting") }, 59 | ) 60 | } 61 | } 62 | } 63 | } 64 | 65 | private fun randomString(): String { 66 | return if (Random.Default.nextBoolean()) { 67 | "lorem" 68 | } else { 69 | "lorem ipsum" 70 | } 71 | } 72 | 73 | @Composable 74 | private fun Item( 75 | @LogArgument id: Int, 76 | string: String, 77 | modifier: Modifier = Modifier, 78 | ) { 79 | Row( 80 | modifier = modifier 81 | .fillMaxWidth() 82 | .padding(10.dp), 83 | ) { 84 | Text( 85 | modifier = Modifier 86 | .padding(3.dp), 87 | text = string, 88 | color = MaterialTheme.colors.onSurface, 89 | ) 90 | Spacer(modifier = Modifier.padding(10.dp)) 91 | Text( 92 | modifier = Modifier 93 | .padding(3.dp), 94 | text = "ipsum", 95 | color = MaterialTheme.colors.onSurface, 96 | ) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RecompositionLoggerSample 3 | -------------------------------------------------------------------------------- /sample_record.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/welltech-com/recompose-logger/f320bf098555892e98301fc88e8f6a1dba7ec461/sample_record.mov -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "recomposition-logger" 2 | 3 | includeBuild("common-build-scripts") 4 | includeBuild("recomposition-logger-support") 5 | includeBuild("recomposition-logger-plugin") 6 | --------------------------------------------------------------------------------