├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── RELEASING.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── hugo-annotations ├── build.gradle └── src │ └── main │ └── java │ └── hugo │ └── weaving │ └── DebugLog.java ├── hugo-example ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── example │ └── hugo │ └── HugoActivity.java ├── hugo-plugin ├── build.gradle └── src │ └── main │ ├── groovy │ └── hugo │ │ └── weaving │ │ └── plugin │ │ ├── HugoExtension.groovy │ │ └── HugoPlugin.groovy │ └── resources │ └── META-INF │ └── gradle-plugins │ ├── com.jakewharton.hugo.properties │ └── hugo.properties ├── hugo-runtime ├── build.gradle ├── gradle.properties └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── hugo │ │ └── weaving │ │ └── internal │ │ ├── Hugo.java │ │ └── Strings.java │ └── test │ └── java │ └── hugo │ └── weaving │ └── internal │ └── StringsTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | local.properties 3 | .DS_Store 4 | .idea 5 | build 6 | *.iml 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: 4 | - oraclejdk7 5 | - oraclejdk8 6 | 7 | android: 8 | components: 9 | - build-tools-23.0.1 10 | - android-23 11 | 12 | # Update Android SDK Tools 13 | - tools 14 | 15 | branches: 16 | except: 17 | - gh-pages 18 | 19 | notifications: 20 | email: false 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 1.2.1 *(2015-02-17)* 5 | ---------------------------- 6 | 7 | * Fix: Enforce Java 1.7 compatibility for the plugin. 8 | 9 | 10 | Version 1.2.0 *(2015-02-12)* 11 | ---------------------------- 12 | 13 | * Plugin ID is now 'com.jakewharton.hugo' (instead of just 'hugo'). 14 | * Support annotating entire classes with `@DebugLog`. 15 | * Include thread names for non-main threads in the log. 16 | * Lower the minimum supported runtime to API 7. Woooo Eclair! 17 | * Log using verbose level. 18 | * Fix: Escape unprintable characters. 19 | * Fix: Eliminate intermittent VerifyError occurrences. 20 | 21 | 22 | Version 1.1.0 *(2014-04-05)* 23 | ---------------------------- 24 | 25 | * Support the 0.10.x Android plugin. 26 | 27 | 28 | Version 1.0.1 *(2013-12-08)* 29 | ---------------------------- 30 | 31 | * Support for printing primitive arrays. 32 | * Invocation time is now displayed before return value for convenience. 33 | * Logging is now enabled for all builds marked as debuggable. 34 | 35 | 36 | Version 1.0.0 *(2013-12-02)* 37 | ---------------------------- 38 | 39 | Initial release. 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hugo 2 | ==== 3 | 4 | Annotation-triggered method call logging for your debug builds. 5 | 6 | As a programmer, you often add log statements to print method calls, their arguments, their return 7 | values, and the time it took to execute. This is not a question. Every one of you does this. 8 | Shouldn't it be easier? 9 | 10 | Simply add `@DebugLog` to your methods and you will automatically get all of the things listed above 11 | logged for free. 12 | 13 | ```java 14 | @DebugLog 15 | public String getName(String first, String last) { 16 | SystemClock.sleep(15); // Don't ever really do this! 17 | return first + " " + last; 18 | } 19 | ``` 20 | ``` 21 | V/Example: ⇢ getName(first="Jake", last="Wharton") 22 | V/Example: ⇠ getName [16ms] = "Jake Wharton" 23 | ``` 24 | 25 | The logging will only happen in debug builds and the annotation itself is never present in the 26 | compiled class file for any build type. This means you can keep the annotation and check it into 27 | source control. It has zero effect on non-debug builds. 28 | 29 | Add it to your project today! 30 | 31 | ```groovy 32 | buildscript { 33 | repositories { 34 | mavenCentral() 35 | } 36 | 37 | dependencies { 38 | classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1' 39 | } 40 | } 41 | 42 | apply plugin: 'com.android.application' 43 | apply plugin: 'com.jakewharton.hugo' 44 | ``` 45 | 46 | Disable logging temporarily by adding the following: 47 | 48 | ```groovy 49 | hugo { 50 | enabled false 51 | } 52 | ``` 53 | 54 | If you want to toggle logging at runtime, use `Hugo.setEnabled(true|false)` 55 | 56 | 57 | Local Development 58 | ----------------- 59 | 60 | Working on this project? Here's some helpful Gradle tasks: 61 | 62 | * `install` - Install plugin, runtime, and annotations into local repo. 63 | * `cleanExample` - Clean the example project build. 64 | * `assembleExample` - Build the example project. Must run `install` first. 65 | * `installExample` - Build and install the example project debug APK onto a device. 66 | 67 | 68 | License 69 | -------- 70 | 71 | Copyright 2013 Jake Wharton 72 | 73 | Licensed under the Apache License, Version 2.0 (the "License"); 74 | you may not use this file except in compliance with the License. 75 | You may obtain a copy of the License at 76 | 77 | http://www.apache.org/licenses/LICENSE-2.0 78 | 79 | Unless required by applicable law or agreed to in writing, software 80 | distributed under the License is distributed on an "AS IS" BASIS, 81 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 82 | See the License for the specific language governing permissions and 83 | limitations under the License. 84 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Release Process 2 | =============== 3 | 4 | 1. Update the `CHANGELOG.md` file with relevant info and date. 5 | 2. Update version number in `gradle.properties` file. 6 | 3. Update version number in `hugo-example/build.gradle` file. 7 | 4. Update version numbers in `HugoPlugin.groovy` file. 8 | 5. Update version number in `README.md` file. 9 | 6. Commit: `git commit -am "Prepare version X.Y.Z."` 10 | 7. Tag: `git tag -a X.Y.Z -m "Version X.Y.Z"` 11 | 8. Release: `./gradlew clean assemble uploadArchives` 12 | 9. Update version number in `gradle.properties` file to next "SNAPSHOT" version. 13 | 10. Update version number in `hugo-example/build.gradle` file to next "SNAPSHOT" version. 14 | 11. Update version numbers in `HugoPlugin.groovy` file to next "SNAPSHOT" version. 15 | 12. Commit: `git commit -am "Prepare next development version."` 16 | 13. Push: `git push && git push --tags` 17 | 14. Write a script for steps 2 - 13. *(Optional)* 18 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'org.gradle.api.plugins:gradle-nexus-plugin:0.7' 9 | classpath 'com.android.tools.build:gradle:1.3.1' 10 | classpath 'org.aspectj:aspectjtools:1.8.6' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | group = GROUP 21 | version = VERSION_NAME 22 | 23 | apply plugin: 'maven' 24 | } 25 | 26 | ext { 27 | compileSdkVersion = 23 28 | buildToolsVersion = '23.0.1' 29 | } 30 | 31 | task cleanExample(type: Exec) { 32 | executable = '../gradlew' 33 | workingDir = project.file('hugo-example') 34 | args = [ 'clean' ] 35 | } 36 | 37 | task assembleExample(type: Exec) { 38 | executable = '../gradlew' 39 | workingDir = project.file('hugo-example') 40 | args = [ 'assemble' ] 41 | } 42 | 43 | task installExample(type: Exec) { 44 | executable = '../gradlew' 45 | workingDir = project.file('hugo-example') 46 | args = [ 'installDebug' ] 47 | } 48 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=1.2.2-SNAPSHOT 2 | GROUP=com.jakewharton.hugo 3 | 4 | POM_DESCRIPTION=Annotation-triggered method call logging for your debug builds. 5 | POM_URL=https://github.com/JakeWharton/hugo/ 6 | POM_SCM_URL=https://github.com/JakeWharton/hugo/ 7 | POM_SCM_CONNECTION=scm:git@github.com/JakeWharton/hugo.git 8 | POM_SCM_DEV_CONNECTION=scm:git@github.com:JakeWharton/hugo.git 9 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 10 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 11 | POM_LICENCE_DIST=repo 12 | POM_DEVELOPER_ID=jakewharton 13 | POM_DEVELOPER_NAME=Jake Wharton 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeWharton/hugo/50ee96dc07cda4277cd5ee59add847251c34604e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 08 15:44:09 PST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /hugo-annotations/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'nexus' 3 | 4 | targetCompatibility = JavaVersion.VERSION_1_6 5 | sourceCompatibility = JavaVersion.VERSION_1_6 6 | 7 | modifyPom { 8 | project { 9 | name "Hugo Annotations" 10 | description POM_DESCRIPTION 11 | url POM_URL 12 | 13 | scm { 14 | url POM_SCM_URL 15 | connection POM_SCM_CONNECTION 16 | developerConnection POM_SCM_DEV_CONNECTION 17 | } 18 | 19 | licenses { 20 | license { 21 | name POM_LICENCE_NAME 22 | url POM_LICENCE_URL 23 | distribution POM_LICENCE_DIST 24 | } 25 | } 26 | 27 | developers { 28 | developer { 29 | id POM_DEVELOPER_ID 30 | name POM_DEVELOPER_NAME 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /hugo-annotations/src/main/java/hugo/weaving/DebugLog.java: -------------------------------------------------------------------------------- 1 | package hugo.weaving; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.CONSTRUCTOR; 7 | import static java.lang.annotation.ElementType.METHOD; 8 | import static java.lang.annotation.ElementType.TYPE; 9 | import static java.lang.annotation.RetentionPolicy.CLASS; 10 | 11 | @Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(CLASS) 12 | public @interface DebugLog { 13 | } 14 | -------------------------------------------------------------------------------- /hugo-example/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | repositories { 4 | mavenCentral() 5 | 6 | // NOTE: This is only needed when developing the plugin! 7 | mavenLocal() 8 | } 9 | 10 | classpath 'com.android.tools.build:gradle:1.3.1' 11 | classpath 'com.jakewharton.hugo:hugo-plugin:1.2.2-SNAPSHOT' 12 | } 13 | } 14 | 15 | apply plugin: 'com.android.application' 16 | apply plugin: 'hugo' 17 | 18 | dependencies { 19 | repositories { 20 | mavenCentral() 21 | 22 | // NOTE: This is only needed when developing the plugin! 23 | mavenLocal() 24 | } 25 | } 26 | 27 | android { 28 | compileSdkVersion 23 29 | buildToolsVersion '23.0.1' 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_7 33 | targetCompatibility JavaVersion.VERSION_1_7 34 | } 35 | 36 | buildTypes { 37 | dev { 38 | applicationIdSuffix '.dev' 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /hugo-example/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /hugo-example/src/main/java/com/example/hugo/HugoActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.hugo; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.os.SystemClock; 6 | import android.util.Log; 7 | import android.widget.TextView; 8 | import hugo.weaving.DebugLog; 9 | 10 | public class HugoActivity extends Activity { 11 | @Override protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | TextView tv = new TextView(this); 14 | tv.setText("Check logcat!"); 15 | setContentView(tv); 16 | 17 | printArgs("The", "Quick", "Brown", "Fox"); 18 | 19 | Log.i("Fibonacci", "fibonacci's 4th number is " + fibonacci(4)); 20 | 21 | Greeter greeter = new Greeter("Jake"); 22 | Log.d("Greeting", greeter.sayHello()); 23 | 24 | Charmer charmer = new Charmer("Jake"); 25 | Log.d("Charming", charmer.askHowAreYou()); 26 | 27 | startSleepyThread(); 28 | } 29 | 30 | @DebugLog 31 | private void printArgs(String... args) { 32 | for (String arg : args) { 33 | Log.i("Args", arg); 34 | } 35 | } 36 | 37 | @DebugLog 38 | private int fibonacci(int number) { 39 | if (number <= 0) { 40 | throw new IllegalArgumentException("Number must be greater than zero."); 41 | } 42 | if (number == 1 || number == 2) { 43 | return 1; 44 | } 45 | // NOTE: Don't ever do this. Use the iterative approach! 46 | return fibonacci(number - 1) + fibonacci(number - 2); 47 | } 48 | 49 | private void startSleepyThread() { 50 | new Thread(new Runnable() { 51 | private static final long SOME_POINTLESS_AMOUNT_OF_TIME = 50; 52 | 53 | @Override public void run() { 54 | sleepyMethod(SOME_POINTLESS_AMOUNT_OF_TIME); 55 | } 56 | 57 | @DebugLog 58 | private void sleepyMethod(long milliseconds) { 59 | SystemClock.sleep(milliseconds); 60 | } 61 | }, "I'm a lazy thr.. bah! whatever!").start(); 62 | } 63 | 64 | @DebugLog 65 | static class Greeter { 66 | private final String name; 67 | 68 | Greeter(String name) { 69 | this.name = name; 70 | } 71 | 72 | private String sayHello() { 73 | return "Hello, " + name; 74 | } 75 | } 76 | 77 | @DebugLog 78 | static class Charmer { 79 | private final String name; 80 | 81 | private Charmer(String name) { 82 | this.name = name; 83 | } 84 | 85 | public String askHowAreYou() { 86 | return "How are you " + name + "?"; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /hugo-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | apply plugin: 'nexus' 3 | 4 | 5 | targetCompatibility = JavaVersion.VERSION_1_7 6 | sourceCompatibility = JavaVersion.VERSION_1_7 7 | 8 | dependencies { 9 | compile gradleApi() 10 | compile localGroovy() 11 | compile 'com.android.tools.build:gradle:1.3.1' 12 | compile 'org.aspectj:aspectjtools:1.8.6' 13 | compile 'org.aspectj:aspectjrt:1.8.6' 14 | } 15 | 16 | modifyPom { 17 | project { 18 | name "Hugo Plugin" 19 | description POM_DESCRIPTION 20 | url POM_URL 21 | 22 | scm { 23 | url POM_SCM_URL 24 | connection POM_SCM_CONNECTION 25 | developerConnection POM_SCM_DEV_CONNECTION 26 | } 27 | 28 | licenses { 29 | license { 30 | name POM_LICENCE_NAME 31 | url POM_LICENCE_URL 32 | distribution POM_LICENCE_DIST 33 | } 34 | } 35 | 36 | developers { 37 | developer { 38 | id POM_DEVELOPER_ID 39 | name POM_DEVELOPER_NAME 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /hugo-plugin/src/main/groovy/hugo/weaving/plugin/HugoExtension.groovy: -------------------------------------------------------------------------------- 1 | package hugo.weaving.plugin 2 | 3 | class HugoExtension { 4 | def enabled = true 5 | 6 | def setEnabled(boolean enabled) { 7 | this.enabled = enabled 8 | } 9 | 10 | def getEnabled() { 11 | return enabled; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hugo-plugin/src/main/groovy/hugo/weaving/plugin/HugoPlugin.groovy: -------------------------------------------------------------------------------- 1 | package hugo.weaving.plugin 2 | 3 | import com.android.build.gradle.AppPlugin 4 | import com.android.build.gradle.LibraryPlugin 5 | import org.aspectj.bridge.IMessage 6 | import org.aspectj.bridge.MessageHandler 7 | import org.aspectj.tools.ajc.Main 8 | import org.gradle.api.Plugin 9 | import org.gradle.api.Project 10 | import org.gradle.api.tasks.compile.JavaCompile 11 | 12 | class HugoPlugin implements Plugin { 13 | @Override void apply(Project project) { 14 | def hasApp = project.plugins.withType(AppPlugin) 15 | def hasLib = project.plugins.withType(LibraryPlugin) 16 | if (!hasApp && !hasLib) { 17 | throw new IllegalStateException("'android' or 'android-library' plugin required.") 18 | } 19 | 20 | final def log = project.logger 21 | final def variants 22 | if (hasApp) { 23 | variants = project.android.applicationVariants 24 | } else { 25 | variants = project.android.libraryVariants 26 | } 27 | 28 | project.dependencies { 29 | debugCompile 'com.jakewharton.hugo:hugo-runtime:1.2.2-SNAPSHOT' 30 | // TODO this should come transitively 31 | debugCompile 'org.aspectj:aspectjrt:1.8.6' 32 | compile 'com.jakewharton.hugo:hugo-annotations:1.2.2-SNAPSHOT' 33 | } 34 | 35 | project.extensions.create('hugo', HugoExtension) 36 | 37 | variants.all { variant -> 38 | if (!variant.buildType.isDebuggable()) { 39 | log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.") 40 | return; 41 | } else if (!project.hugo.enabled) { 42 | log.debug("Hugo is not disabled.") 43 | return; 44 | } 45 | 46 | JavaCompile javaCompile = variant.javaCompile 47 | javaCompile.doLast { 48 | String[] args = [ 49 | "-showWeaveInfo", 50 | "-1.5", 51 | "-inpath", javaCompile.destinationDir.toString(), 52 | "-aspectpath", javaCompile.classpath.asPath, 53 | "-d", javaCompile.destinationDir.toString(), 54 | "-classpath", javaCompile.classpath.asPath, 55 | "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator) 56 | ] 57 | log.debug "ajc args: " + Arrays.toString(args) 58 | 59 | MessageHandler handler = new MessageHandler(true); 60 | new Main().run(args, handler); 61 | for (IMessage message : handler.getMessages(null, true)) { 62 | switch (message.getKind()) { 63 | case IMessage.ABORT: 64 | case IMessage.ERROR: 65 | case IMessage.FAIL: 66 | log.error message.message, message.thrown 67 | break; 68 | case IMessage.WARNING: 69 | log.warn message.message, message.thrown 70 | break; 71 | case IMessage.INFO: 72 | log.info message.message, message.thrown 73 | break; 74 | case IMessage.DEBUG: 75 | log.debug message.message, message.thrown 76 | break; 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hugo-plugin/src/main/resources/META-INF/gradle-plugins/com.jakewharton.hugo.properties: -------------------------------------------------------------------------------- 1 | implementation-class=hugo.weaving.plugin.HugoPlugin 2 | -------------------------------------------------------------------------------- /hugo-plugin/src/main/resources/META-INF/gradle-plugins/hugo.properties: -------------------------------------------------------------------------------- 1 | implementation-class=hugo.weaving.plugin.HugoPlugin 2 | -------------------------------------------------------------------------------- /hugo-runtime/build.gradle: -------------------------------------------------------------------------------- 1 | import org.aspectj.bridge.IMessage 2 | import org.aspectj.bridge.MessageHandler 3 | import org.aspectj.tools.ajc.Main 4 | 5 | apply plugin: 'com.android.library' 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | 8 | dependencies { 9 | compile 'org.aspectj:aspectjrt:1.8.6' 10 | compile project(':hugo-annotations') 11 | 12 | testCompile 'junit:junit:4.12' 13 | } 14 | 15 | android { 16 | compileSdkVersion rootProject.ext.compileSdkVersion 17 | buildToolsVersion rootProject.ext.buildToolsVersion 18 | 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_7 21 | targetCompatibility JavaVersion.VERSION_1_7 22 | } 23 | } 24 | 25 | android.libraryVariants.all { variant -> 26 | JavaCompile javaCompile = variant.javaCompile 27 | javaCompile.doLast { 28 | String[] args = [ 29 | "-showWeaveInfo", 30 | "-1.5", 31 | "-inpath", javaCompile.destinationDir.toString(), 32 | "-aspectpath", javaCompile.classpath.asPath, 33 | "-d", javaCompile.destinationDir.toString(), 34 | "-classpath", javaCompile.classpath.asPath, 35 | "-bootclasspath", android.bootClasspath.join(File.pathSeparator) 36 | ] 37 | 38 | MessageHandler handler = new MessageHandler(true); 39 | new Main().run(args, handler) 40 | 41 | def log = project.logger 42 | for (IMessage message : handler.getMessages(null, true)) { 43 | switch (message.getKind()) { 44 | case IMessage.ABORT: 45 | case IMessage.ERROR: 46 | case IMessage.FAIL: 47 | log.error message.message, message.thrown 48 | break; 49 | case IMessage.WARNING: 50 | case IMessage.INFO: 51 | log.info message.message, message.thrown 52 | break; 53 | case IMessage.DEBUG: 54 | log.debug message.message, message.thrown 55 | break; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /hugo-runtime/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Hugo Runtime 2 | POM_ARTIFACT_ID=hugo-runtime 3 | POM_PACKAGING=aar 4 | -------------------------------------------------------------------------------- /hugo-runtime/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /hugo-runtime/src/main/java/hugo/weaving/internal/Hugo.java: -------------------------------------------------------------------------------- 1 | package hugo.weaving.internal; 2 | 3 | import android.os.Build; 4 | import android.os.Looper; 5 | import android.os.Trace; 6 | import android.util.Log; 7 | 8 | import org.aspectj.lang.JoinPoint; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.Signature; 11 | import org.aspectj.lang.annotation.Around; 12 | import org.aspectj.lang.annotation.Aspect; 13 | import org.aspectj.lang.annotation.Pointcut; 14 | import org.aspectj.lang.reflect.CodeSignature; 15 | import org.aspectj.lang.reflect.MethodSignature; 16 | 17 | import java.util.concurrent.TimeUnit; 18 | 19 | @Aspect 20 | public class Hugo { 21 | private static volatile boolean enabled = true; 22 | 23 | @Pointcut("within(@hugo.weaving.DebugLog *)") 24 | public void withinAnnotatedClass() {} 25 | 26 | @Pointcut("execution(!synthetic * *(..)) && withinAnnotatedClass()") 27 | public void methodInsideAnnotatedType() {} 28 | 29 | @Pointcut("execution(!synthetic *.new(..)) && withinAnnotatedClass()") 30 | public void constructorInsideAnnotatedType() {} 31 | 32 | @Pointcut("execution(@hugo.weaving.DebugLog * *(..)) || methodInsideAnnotatedType()") 33 | public void method() {} 34 | 35 | @Pointcut("execution(@hugo.weaving.DebugLog *.new(..)) || constructorInsideAnnotatedType()") 36 | public void constructor() {} 37 | 38 | public static void setEnabled(boolean enabled) { 39 | Hugo.enabled = enabled; 40 | } 41 | 42 | @Around("method() || constructor()") 43 | public Object logAndExecute(ProceedingJoinPoint joinPoint) throws Throwable { 44 | enterMethod(joinPoint); 45 | 46 | long startNanos = System.nanoTime(); 47 | Object result = joinPoint.proceed(); 48 | long stopNanos = System.nanoTime(); 49 | long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos); 50 | 51 | exitMethod(joinPoint, result, lengthMillis); 52 | 53 | return result; 54 | } 55 | 56 | private static void enterMethod(JoinPoint joinPoint) { 57 | if (!enabled) return; 58 | 59 | CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature(); 60 | 61 | Class cls = codeSignature.getDeclaringType(); 62 | String methodName = codeSignature.getName(); 63 | String[] parameterNames = codeSignature.getParameterNames(); 64 | Object[] parameterValues = joinPoint.getArgs(); 65 | 66 | StringBuilder builder = new StringBuilder("\u21E2 "); 67 | builder.append(methodName).append('('); 68 | for (int i = 0; i < parameterValues.length; i++) { 69 | if (i > 0) { 70 | builder.append(", "); 71 | } 72 | builder.append(parameterNames[i]).append('='); 73 | builder.append(Strings.toString(parameterValues[i])); 74 | } 75 | builder.append(')'); 76 | 77 | if (Looper.myLooper() != Looper.getMainLooper()) { 78 | builder.append(" [Thread:\"").append(Thread.currentThread().getName()).append("\"]"); 79 | } 80 | 81 | Log.v(asTag(cls), builder.toString()); 82 | 83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 84 | final String section = builder.toString().substring(2); 85 | Trace.beginSection(section); 86 | } 87 | } 88 | 89 | private static void exitMethod(JoinPoint joinPoint, Object result, long lengthMillis) { 90 | if (!enabled) return; 91 | 92 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 93 | Trace.endSection(); 94 | } 95 | 96 | Signature signature = joinPoint.getSignature(); 97 | 98 | Class cls = signature.getDeclaringType(); 99 | String methodName = signature.getName(); 100 | boolean hasReturnType = signature instanceof MethodSignature 101 | && ((MethodSignature) signature).getReturnType() != void.class; 102 | 103 | StringBuilder builder = new StringBuilder("\u21E0 ") 104 | .append(methodName) 105 | .append(" [") 106 | .append(lengthMillis) 107 | .append("ms]"); 108 | 109 | if (hasReturnType) { 110 | builder.append(" = "); 111 | builder.append(Strings.toString(result)); 112 | } 113 | 114 | Log.v(asTag(cls), builder.toString()); 115 | } 116 | 117 | private static String asTag(Class cls) { 118 | if (cls.isAnonymousClass()) { 119 | return asTag(cls.getEnclosingClass()); 120 | } 121 | return cls.getSimpleName(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /hugo-runtime/src/main/java/hugo/weaving/internal/Strings.java: -------------------------------------------------------------------------------- 1 | package hugo.weaving.internal; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Locale; 6 | import java.util.Set; 7 | 8 | final class Strings { 9 | static String toString(Object obj) { 10 | if (obj == null) { 11 | return "null"; 12 | } 13 | if (obj instanceof CharSequence) { 14 | return '"' + printableToString(obj.toString()) + '"'; 15 | } 16 | 17 | Class cls = obj.getClass(); 18 | if (Byte.class == cls) { 19 | return byteToString((Byte) obj); 20 | } 21 | 22 | if (cls.isArray()) { 23 | return arrayToString(cls.getComponentType(), obj); 24 | } 25 | return obj.toString(); 26 | } 27 | 28 | private static String printableToString(String string) { 29 | int length = string.length(); 30 | StringBuilder builder = new StringBuilder(length); 31 | for (int i = 0; i < length;) { 32 | int codePoint = string.codePointAt(i); 33 | switch (Character.getType(codePoint)) { 34 | case Character.CONTROL: 35 | case Character.FORMAT: 36 | case Character.PRIVATE_USE: 37 | case Character.SURROGATE: 38 | case Character.UNASSIGNED: 39 | switch (codePoint) { 40 | case '\n': 41 | builder.append("\\n"); 42 | break; 43 | case '\r': 44 | builder.append("\\r"); 45 | break; 46 | case '\t': 47 | builder.append("\\t"); 48 | break; 49 | case '\f': 50 | builder.append("\\f"); 51 | break; 52 | case '\b': 53 | builder.append("\\b"); 54 | break; 55 | default: 56 | builder.append("\\u").append(String.format("%04x", codePoint).toUpperCase(Locale.US)); 57 | break; 58 | } 59 | break; 60 | default: 61 | builder.append(Character.toChars(codePoint)); 62 | break; 63 | } 64 | i += Character.charCount(codePoint); 65 | } 66 | return builder.toString(); 67 | } 68 | 69 | private static String arrayToString(Class cls, Object obj) { 70 | if (byte.class == cls) { 71 | return byteArrayToString((byte[]) obj); 72 | } 73 | if (short.class == cls) { 74 | return Arrays.toString((short[]) obj); 75 | } 76 | if (char.class == cls) { 77 | return Arrays.toString((char[]) obj); 78 | } 79 | if (int.class == cls) { 80 | return Arrays.toString((int[]) obj); 81 | } 82 | if (long.class == cls) { 83 | return Arrays.toString((long[]) obj); 84 | } 85 | if (float.class == cls) { 86 | return Arrays.toString((float[]) obj); 87 | } 88 | if (double.class == cls) { 89 | return Arrays.toString((double[]) obj); 90 | } 91 | if (boolean.class == cls) { 92 | return Arrays.toString((boolean[]) obj); 93 | } 94 | return arrayToString((Object[]) obj); 95 | } 96 | 97 | /** A more human-friendly version of Arrays#toString(byte[]) that uses hex representation. */ 98 | private static String byteArrayToString(byte[] bytes) { 99 | StringBuilder builder = new StringBuilder("["); 100 | for (int i = 0; i < bytes.length; i++) { 101 | if (i > 0) { 102 | builder.append(", "); 103 | } 104 | builder.append(byteToString(bytes[i])); 105 | } 106 | return builder.append(']').toString(); 107 | } 108 | 109 | private static String byteToString(Byte b) { 110 | if (b == null) { 111 | return "null"; 112 | } 113 | return "0x" + String.format("%02x", b).toUpperCase(Locale.US); 114 | } 115 | 116 | private static String arrayToString(Object[] array) { 117 | StringBuilder buf = new StringBuilder(); 118 | arrayToString(array, buf, new HashSet()); 119 | return buf.toString(); 120 | } 121 | 122 | private static void arrayToString(Object[] array, StringBuilder builder, Set seen) { 123 | if (array == null) { 124 | builder.append("null"); 125 | return; 126 | } 127 | 128 | seen.add(array); 129 | builder.append('['); 130 | for (int i = 0; i < array.length; i++) { 131 | if (i > 0) { 132 | builder.append(", "); 133 | } 134 | 135 | Object element = array[i]; 136 | if (element == null) { 137 | builder.append("null"); 138 | } else { 139 | Class elementClass = element.getClass(); 140 | if (elementClass.isArray() && elementClass.getComponentType() == Object.class) { 141 | Object[] arrayElement = (Object[]) element; 142 | if (seen.contains(arrayElement)) { 143 | builder.append("[...]"); 144 | } else { 145 | arrayToString(arrayElement, builder, seen); 146 | } 147 | } else { 148 | builder.append(toString(element)); 149 | } 150 | } 151 | } 152 | builder.append(']'); 153 | seen.remove(array); 154 | } 155 | 156 | private Strings() { 157 | throw new AssertionError("No instances."); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /hugo-runtime/src/test/java/hugo/weaving/internal/StringsTest.java: -------------------------------------------------------------------------------- 1 | package hugo.weaving.internal; 2 | 3 | import java.math.BigInteger; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public final class StringsTest { 9 | @Test public void nullValue() { 10 | assertEquals("null", Strings.toString(null)); 11 | } 12 | 13 | @Test public void string() { 14 | assertEquals("\"String\"", Strings.toString("String")); 15 | } 16 | 17 | @Test public void unprintableCharacters() { 18 | assertEquals("\"Str\\ning\"", Strings.toString("Str\ning")); 19 | assertEquals("\"\\n\\r\\t\\f\\b\\u202C\"", Strings.toString("\n\r\t\f\b\u202C")); 20 | } 21 | 22 | @Test public void objects() { 23 | assertEquals("1", Strings.toString(new BigInteger("1"))); 24 | } 25 | 26 | @Test public void byteValue() { 27 | byte primitive = (byte) 0xAB; 28 | assertEquals("0xAB", Strings.toString(primitive)); 29 | Byte boxed = primitive; 30 | assertEquals("0xAB", Strings.toString(boxed)); 31 | } 32 | 33 | @Test public void byteArrays() { 34 | byte[] primitive = { (byte) 0xAB, (byte) 0xBC, (byte) 0xCD, (byte) 0xDE, (byte) 0xEF }; 35 | assertEquals("[0xAB, 0xBC, 0xCD, 0xDE, 0xEF]", Strings.toString(primitive)); 36 | Byte[] boxed = { (byte) 0xAB, (byte) 0xBC, null, (byte) 0xDE, (byte) 0xEF }; 37 | assertEquals("[0xAB, 0xBC, null, 0xDE, 0xEF]", Strings.toString(boxed)); 38 | } 39 | 40 | @Test public void shortArrays() { 41 | short[] primitive = { 1, 2, 3, 4, 5 }; 42 | assertEquals("[1, 2, 3, 4, 5]", Strings.toString(primitive)); 43 | Short[] boxed = { 1, 2, null, 4, 5 }; 44 | assertEquals("[1, 2, null, 4, 5]", Strings.toString(boxed)); 45 | } 46 | 47 | @Test public void charArrays() { 48 | char[] primitive = { 'a', 'b', 'c', 'd', 'e' }; 49 | assertEquals("[a, b, c, d, e]", Strings.toString(primitive)); 50 | Character[] boxed = { 'a', 'b', null, 'd', 'e' }; 51 | assertEquals("[a, b, null, d, e]", Strings.toString(boxed)); 52 | } 53 | 54 | @Test public void intArrays() { 55 | int[] primitive = { 1, 2, 3, 4, 5 }; 56 | assertEquals("[1, 2, 3, 4, 5]", Strings.toString(primitive)); 57 | Integer[] boxed = { 1, 2, null, 4, 5 }; 58 | assertEquals("[1, 2, null, 4, 5]", Strings.toString(boxed)); 59 | } 60 | 61 | @Test public void longArrays() { 62 | long[] primitive = { 1, 2, 3, 4, 5 }; 63 | assertEquals("[1, 2, 3, 4, 5]", Strings.toString(primitive)); 64 | Long[] boxed = { 1L, 2L, null, 4L, 5L }; 65 | assertEquals("[1, 2, null, 4, 5]", Strings.toString(boxed)); 66 | } 67 | 68 | @Test public void floatArrays() { 69 | float[] primitive = { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f }; 70 | assertEquals("[1.1, 2.2, 3.3, 4.4, 5.5]", Strings.toString(primitive)); 71 | Float[] boxed = { 1.1f, 2.2f, null, 4.4f, 5.5f }; 72 | assertEquals("[1.1, 2.2, null, 4.4, 5.5]", Strings.toString(boxed)); 73 | } 74 | 75 | @Test public void doubleArrays() { 76 | double[] primitive = { 1.1d, 2.2d, 3.3d, 4.4d, 5.5d }; 77 | assertEquals("[1.1, 2.2, 3.3, 4.4, 5.5]", Strings.toString(primitive)); 78 | Double[] boxed = { 1.1d, 2.2d, null, 4.4d, 5.5d }; 79 | assertEquals("[1.1, 2.2, null, 4.4, 5.5]", Strings.toString(boxed)); 80 | } 81 | 82 | @Test public void booleanArrays() { 83 | boolean[] primitive = { true, false, true, false, true }; 84 | assertEquals("[true, false, true, false, true]", Strings.toString(primitive)); 85 | Boolean[] boxed = { true, false, null, false, true }; 86 | assertEquals("[true, false, null, false, true]", Strings.toString(boxed)); 87 | } 88 | 89 | @Test public void objectArray() { 90 | Object[] array = { 1, true, "String", 1.1f, null, new BigInteger("1") }; 91 | assertEquals("[1, true, \"String\", 1.1, null, 1]", Strings.toString(array)); 92 | } 93 | 94 | @Test public void deepObjectArray() { 95 | Object[] array = { 1, true, "String", new Object[] { 1.1f, "Nested" } }; 96 | assertEquals("[1, true, \"String\", [1.1, \"Nested\"]]", Strings.toString(array)); 97 | } 98 | 99 | @Test public void recursiveObjectArray() { 100 | Object[] array = { 1, 2, 3, null }; 101 | array[3] = array; 102 | assertEquals("[1, 2, 3, [...]]", Strings.toString(array)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':hugo-annotations' 2 | include ':hugo-plugin' 3 | include ':hugo-runtime' --------------------------------------------------------------------------------