├── .gitignore ├── .idea ├── codeStyleSettings.xml ├── compiler.xml ├── copyright │ ├── Apache.xml │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ └── gradle-wrapper.jar ├── gradlew ├── gradlew.bat └── src └── main ├── groovy └── cursive │ └── ApiFacade.groovy ├── kotlin └── cursive │ ├── ClojurePlugin.kt │ └── LineProcessingOutputStream.kt └── resources ├── META-INF └── gradle-plugins │ └── com.cursive-ide.clojure.properties └── cursive └── test_runner.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/workspace.xml 2 | /.idea/replstate.xml 3 | /.idea/inspectionProfiles/Project_Default.xml 4 | /.idea/dictionaries 5 | /.idea/shelf 6 | /.idea/libraries 7 | /.idea/modules 8 | 9 | out 10 | dist 11 | target 12 | classes 13 | 14 | .gradle 15 | build/ 16 | 17 | # Ignore Gradle GUI config 18 | gradle-app.setting 19 | 20 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 21 | !gradle-wrapper.jar 22 | 23 | # Cache of project 24 | .gradletasknamecache 25 | 26 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 27 | gradle/wrapper/gradle-wrapper.properties 28 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | -------------------------------------------------------------------------------- /.idea/copyright/Apache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | # Deprecated # 2 | 3 | This plugin has been deprecated, and the development has been taken over by the fine folks over at the [gradle-clojure](https://github.com/gradle-clojure) org. You should use their plugin rather than this one. 4 | 5 | # gradle-clojure # 6 | 7 | A deliberately minimal Gradle plugin for Clojure compilation and test running. 8 | 9 | ## Quick Start ## 10 | 11 | ```groovy 12 | plugins { 13 | id "com.cursive-ide.clojure" version "1.1.0" 14 | } 15 | 16 | compileClojure { 17 | aotCompile = true // Defaults to false 18 | copySourceToOutput = false // Defaults to !aotCompile 19 | 20 | reflectionWarnings { 21 | enabled = true // Defaults to false 22 | projectOnly = true // Only show warnings from your project, not dependencies - default false 23 | asErrors = true // Treat reflection warnings as errors and fail the build 24 | // If projectOnly is true, only warnings from your project are errors. 25 | } 26 | 27 | // Compiler options for AOT 28 | disableLocalsClearing = true // Defaults to false 29 | elideMeta = ['doc', 'file', 'line', 'added'] // Defaults to [] 30 | directLinking = true // Defaults to false 31 | 32 | // compileClojure implements the standard JavaForkOptions interface, and thus supports the 33 | // standard Gradle mechanisms for configuring a Java process: 34 | // systemProperty systemProperties minHeapSize maxHeapSize 35 | // jvmArgs bootstrapClasspath classpath enableAssertions debug environment 36 | 37 | systemProperty 'java.awt.headless', true 38 | maxHeapSize '2048m' 39 | jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005' 40 | } 41 | 42 | compileTestClojure { 43 | // compileTestClojure accepts the same options as compileClojure, but you're unlikely to AOT 44 | // compile your tests 45 | 46 | // Select the files for testing using the standard Gradle include/exclude mechanisms 47 | exclude 'cursive/**/*generative*' 48 | } 49 | 50 | testClojure { 51 | // Standard JVM execution options here for test process 52 | systemProperty 'java.awt.headless', true 53 | 54 | // Specifying junitReport will trigger JUnit XML report generation 55 | // in addition to standard console output (turned off by default) 56 | junitReport = file("$buildDir/reports/junit-report.xml") 57 | } 58 | ``` 59 | 60 | This plugin assumes you're using a sane layout for your Clojure code - namespaces corresponding 61 | to your source code layout, and one namespace per file. The plugin uses the filenames to 62 | calculate the namespaces involved, it does not parse the files looking for `ns` forms. 63 | 64 | This plugin currently only implements compilation and test running. More features may be added, 65 | but features provided by Gradle itself will not be (uberjarring, project publishing). I don't 66 | use those features myself, examples of build script snippets to perform them for the doc would 67 | be very welcome. 68 | 69 | There is currently no functionality for running a REPL - you'll need to run an application which 70 | starts an nREPL server, or something similar. 71 | 72 | ## Planned Features ## 73 | 74 | 1. Android support - the Kotlin plugin has an example of this. I'm planning to add this soon so 75 | that Cursive can be used with Android Studio. 76 | 2. Code execution support with project classpath. 77 | 78 | ## Contributors ## 79 | 80 | * Colin Fleming ([@cmf](https://github.com/cmf), original author) 81 | * Piotrek Bzdyl ([@pbzdyl](https://github.com/pbzdyl)) 82 | 83 | ## Licence ## 84 | 85 | Apache License, Version 2.0 86 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Colin Fleming 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | buildscript { 18 | ext.kotlin_version = '1.0.3' 19 | 20 | repositories { 21 | jcenter() 22 | maven { 23 | url "https://plugins.gradle.org/m2/" 24 | } 25 | } 26 | 27 | dependencies { 28 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 29 | classpath "com.gradle.publish:plugin-publish-plugin:0.9.5" 30 | } 31 | } 32 | 33 | group 'com.cursive-ide' 34 | version '1.1.0' 35 | 36 | apply plugin: 'kotlin' 37 | apply plugin: 'groovy' 38 | apply plugin: 'maven' 39 | apply plugin: 'com.gradle.plugin-publish' 40 | 41 | project.afterEvaluate { 42 | // Ugh, see https://discuss.gradle.org/t/kotlin-groovy-and-java-compilation/14903/9 43 | compileGroovy.dependsOn = compileGroovy.taskDependencies.values - 'compileJava' 44 | compileKotlin.dependsOn compileGroovy 45 | compileKotlin.classpath += files(compileGroovy.destinationDir) 46 | classes.dependsOn compileKotlin 47 | } 48 | 49 | repositories { 50 | jcenter() 51 | mavenLocal() 52 | } 53 | 54 | dependencies { 55 | compile gradleApi() 56 | compile localGroovy() 57 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 58 | } 59 | 60 | pluginBundle { 61 | website = 'https://plugins.gradle.org/plugin/com.cursive-ide.clojure' 62 | vcsUrl = 'https://github.com/cursive-ide/gradle-clojure' 63 | description = 'Compiles Clojure code and runs tests' 64 | tags = ['clojure'] 65 | 66 | plugins { 67 | clojurePlugin { 68 | id = 'com.cursive-ide.clojure' 69 | displayName = 'Gradle Clojure plugin' 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cursive-ghost/gradle-clojure/22326a6a7850daa5f7da70dc017d5fe7dd1984e6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows 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 | -------------------------------------------------------------------------------- /src/main/groovy/cursive/ApiFacade.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Colin Fleming 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cursive 18 | 19 | import org.gradle.api.file.SourceDirectorySet 20 | import org.gradle.api.internal.file.FileResolver 21 | import org.gradle.api.internal.file.DefaultSourceDirectorySet 22 | import org.gradle.util.GradleVersion 23 | import org.gradle.util.ConfigureUtil 24 | import org.jetbrains.annotations.NotNull 25 | 26 | /** 27 | * This class is written in Groovy to allow multiple API versions to be handled 28 | * API examples from https://github.com/eriwen/gradle-js-plugin/blob/master/src/main/groovy/com/eriwen/gradle/js/source/internal/DefaultJavaScriptSourceSet.groovy 29 | * @author Colin Fleming 30 | */ 31 | class ApiFacade { 32 | 33 | public static @NotNull SourceDirectorySet sourceDirectorySet(String name, FileResolver resolver) { 34 | if (GradleVersion.current() >= GradleVersion.version("2.12")) { 35 | def fileTreeFactory = Class.forName("org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory") 36 | def directoryFileTreeFactory = fileTreeFactory.newInstance() 37 | return new DefaultSourceDirectorySet(name, resolver, directoryFileTreeFactory) 38 | } else { 39 | return new DefaultSourceDirectorySet(name, resolver) 40 | } 41 | } 42 | 43 | public static void configureByClosure(Object object, Closure closure) { 44 | if (GradleVersion.current() >= GradleVersion.version("2.14")) { 45 | ConfigureUtil.configureSelf(closure, object) 46 | } else { 47 | ConfigureUtil.configure(closure, object, false) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/cursive/ClojurePlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Colin Fleming 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cursive 18 | 19 | import groovy.lang.Closure 20 | import org.gradle.api.Plugin 21 | import org.gradle.api.Project 22 | import org.gradle.api.file.FileCollection 23 | import org.gradle.api.file.SourceDirectorySet 24 | import org.gradle.api.internal.ConventionTask 25 | import org.gradle.api.internal.HasConvention 26 | import org.gradle.api.internal.file.FileResolver 27 | import org.gradle.api.internal.file.collections.SimpleFileCollection 28 | import org.gradle.api.internal.plugins.DslObject 29 | import org.gradle.api.internal.project.ProjectInternal 30 | import org.gradle.api.logging.Logging 31 | import org.gradle.api.plugins.JavaBasePlugin 32 | import org.gradle.api.plugins.JavaPlugin 33 | import org.gradle.api.plugins.JavaPluginConvention 34 | import org.gradle.api.tasks.SourceSet 35 | import org.gradle.api.tasks.TaskAction 36 | import org.gradle.api.tasks.compile.AbstractCompile 37 | import org.gradle.process.JavaForkOptions 38 | import org.gradle.process.internal.DefaultJavaForkOptions 39 | import org.gradle.process.internal.ExecException 40 | import org.gradle.process.internal.JavaExecHandleBuilder 41 | import org.gradle.util.ConfigureUtil 42 | import java.io.File 43 | import java.io.OutputStream 44 | import java.util.* 45 | import java.util.regex.Pattern 46 | import javax.inject.Inject 47 | 48 | /** 49 | * @author Colin Fleming 50 | */ 51 | class ClojurePlugin : Plugin { 52 | val logger = Logging.getLogger(this.javaClass) 53 | 54 | override fun apply(project: Project) { 55 | logger.info("Applying ClojurePlugin") 56 | val javaBasePlugin = project.plugins.apply(JavaBasePlugin::class.java) 57 | val javaPluginConvention = project.convention.getPlugin(JavaPluginConvention::class.java) 58 | 59 | project.plugins.apply(JavaPlugin::class.java) 60 | 61 | val mainSourceSet = javaPluginConvention.sourceSets?.getByName(SourceSet.MAIN_SOURCE_SET_NAME) 62 | if (mainSourceSet is HasConvention) { 63 | val mainCompileTask = createCompileTask(project, mainSourceSet) 64 | val conventionMapping = mainCompileTask.conventionMapping 65 | conventionMapping.map("classpath", { 66 | mainSourceSet.compileClasspath 67 | .plus(SimpleFileCollection(mainSourceSet.allSource.srcDirs)) 68 | .plus(SimpleFileCollection(mainSourceSet.output.classesDir)) 69 | }) 70 | conventionMapping.map("namespaces", { 71 | mainCompileTask.findNamespaces() 72 | }) 73 | } 74 | 75 | val testSourceSet = javaPluginConvention.sourceSets?.getByName(SourceSet.TEST_SOURCE_SET_NAME) 76 | if (testSourceSet is HasConvention) { 77 | val testCompileTask = createCompileTask(project, testSourceSet) 78 | 79 | val mainSrcDirs = mainSourceSet?.allSource?.srcDirs ?: emptyList() 80 | val testSrcDirs = testSourceSet.allSource.srcDirs 81 | val outputDirs = if (mainSourceSet != null) 82 | listOf(mainSourceSet.output.classesDir, testSourceSet.output.classesDir) 83 | else 84 | listOf(testSourceSet.output.classesDir) 85 | 86 | val compileMapping = testCompileTask.conventionMapping 87 | compileMapping.map("classpath", { 88 | testSourceSet.compileClasspath 89 | .plus(SimpleFileCollection(mainSrcDirs + testSrcDirs + outputDirs)) 90 | }) 91 | compileMapping.map("namespaces", { 92 | testCompileTask.findNamespaces() 93 | }) 94 | 95 | val testTask = createTestTask(project) 96 | 97 | val testRunnerMapping = testTask.conventionMapping 98 | testRunnerMapping.map("classpath", { 99 | testSourceSet.runtimeClasspath.plus(SimpleFileCollection(mainSrcDirs + testSrcDirs + outputDirs)) 100 | }) 101 | testRunnerMapping.map("namespaces", { 102 | testCompileTask.findNamespaces() 103 | }) 104 | 105 | testTask.dependsOn(testCompileTask.name) 106 | } 107 | } 108 | 109 | fun createCompileTask(project: Project, sourceSet: SourceSet): ClojureCompiler { 110 | val projectInternal = project as ProjectInternal 111 | val sourceRootDir: String = "src/${sourceSet.name}/clojure" 112 | 113 | logger.info("Creating DefaultSourceDirectorySet for source set $sourceSet") 114 | val clojureSrcSet = ClojureSourceSetImpl(sourceSet.name, projectInternal.fileResolver) 115 | val clojureDirSet = clojureSrcSet.getClojure() 116 | DslObject(sourceSet).convention.plugins.put("clojure", clojureSrcSet) 117 | 118 | val srcDir = project.file(sourceRootDir) 119 | logger.info("Creating Clojure SourceDirectorySet for source set $sourceSet with src dir $srcDir") 120 | clojureDirSet.srcDir(srcDir) 121 | 122 | logger.info("Adding Clojure SourceDirectorySet $clojureDirSet to source set $sourceSet") 123 | sourceSet.allSource?.source(clojureDirSet) 124 | sourceSet.resources?.filter?.exclude { clojureDirSet.contains(it.file) } 125 | 126 | val name = sourceSet.getCompileTaskName("clojure") 127 | val compilerClass = ClojureCompiler::class.java 128 | logger.info("Creating Clojure compile task $name with class $compilerClass") 129 | val compile = project.tasks.create(name, compilerClass) 130 | compile.description = "Compiles the $sourceSet Clojure code" 131 | 132 | val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName) 133 | if (javaTask != null) { 134 | compile.dependsOn(javaTask.name) 135 | } 136 | 137 | project.tasks.findByName(sourceSet.classesTaskName)?.dependsOn(compile.name) 138 | 139 | val conventionMapping = compile.conventionMapping 140 | conventionMapping.map("destinationDir", { 141 | sourceSet.output.classesDir 142 | }) 143 | 144 | compile.source(clojureDirSet) 145 | 146 | return compile 147 | } 148 | } 149 | 150 | fun createTestTask(project: Project): ClojureTestRunner { 151 | val name = "testClojure" 152 | val testRunnerClass = ClojureTestRunner::class.java 153 | 154 | val testRunner = project.tasks.create(name, testRunnerClass) 155 | project.tasks.getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(testRunner) 156 | testRunner.description = "Runs the Clojure tests" 157 | testRunner.group = JavaBasePlugin.VERIFICATION_GROUP 158 | 159 | testRunner.outputs.upToDateWhen { false } 160 | 161 | return testRunner 162 | } 163 | 164 | 165 | interface ClojureSourceSet { 166 | fun getClojure(): SourceDirectorySet 167 | fun clojure(configureClosure: Closure?): ClojureSourceSet 168 | } 169 | 170 | 171 | open class ClojureSourceSetImpl(displayName: String?, resolver: FileResolver?) : ClojureSourceSet { 172 | private val clojure = ApiFacade.sourceDirectorySet(displayName + " Clojure source", resolver) 173 | 174 | init { 175 | clojure.filter.include("**/*.clj", "**/*.cljc") 176 | } 177 | 178 | override fun getClojure(): SourceDirectorySet = clojure 179 | 180 | override fun clojure(configureClosure: Closure?): ClojureSourceSet { 181 | ApiFacade.configureByClosure(this, configureClosure) 182 | return this 183 | } 184 | } 185 | 186 | class ReflectionWarnings(var enabled: Boolean, var projectOnly: Boolean, var asErrors: Boolean) 187 | 188 | open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : 189 | AbstractCompile(), 190 | JavaForkOptions by DefaultJavaForkOptions(fileResolver) { 191 | 192 | var aotCompile: Boolean = false 193 | var copySourceToOutput: Boolean? = null 194 | var reflectionWarnings = ReflectionWarnings(false, false, false) 195 | 196 | var disableLocalsClearing: Boolean = false 197 | var elideMeta: Collection = emptyList() 198 | var directLinking: Boolean = false 199 | 200 | var namespaces: Collection = emptyList() 201 | 202 | fun reflectionWarnings(configureClosure: Closure?): ReflectionWarnings { 203 | ConfigureUtil.configure(configureClosure, reflectionWarnings) 204 | return reflectionWarnings 205 | } 206 | 207 | @TaskAction 208 | override fun compile() { 209 | logger.info("Starting ClojureCompiler task") 210 | 211 | if (copySourceToOutput ?: !aotCompile) { 212 | project.copy { 213 | it.from(getSource()).into(destinationDir) 214 | } 215 | return 216 | } 217 | 218 | if (aotCompile) { 219 | logger.info("Destination: $destinationDir") 220 | 221 | // Required (I think) because Kotlin accesses the field directly (not the getter) from the same class 222 | val namespaces = conventionMapping.getConventionValue>(emptyList(), "namespaces", false) 223 | if (namespaces.isEmpty()) { 224 | logger.info("No Clojure namespaces defined, skipping $name") 225 | return 226 | } 227 | 228 | logger.info("Compiling " + namespaces.joinToString(", ")) 229 | 230 | val script = listOf("(try", 231 | " (binding [*compile-path* \"${destinationDir.canonicalPath}\"", 232 | " *warn-on-reflection* ${reflectionWarnings.enabled}", 233 | " *compiler-options* {:disable-locals-clearing $disableLocalsClearing", 234 | " :elide-meta [${elideMeta.map { ":$it" }.joinToString(" ")}]", 235 | " :direct-linking $directLinking}]", 236 | " " + namespaces.map { "(compile '$it)" }.joinToString("\n ") + ")", 237 | " (catch Throwable e", 238 | " (.printStackTrace e)", 239 | " (System/exit 1)))", 240 | "(System/exit 0)") 241 | .joinToString("\n") 242 | 243 | // println(script) 244 | 245 | val stdout = object : LineProcessingOutputStream() { 246 | override fun processLine(line: String) { 247 | System.out.print(line) 248 | } 249 | } 250 | 251 | val sourceRoots = getSourceRoots() 252 | 253 | var reflectionWarningCount = 0 254 | var libraryReflectionWarningCount = 0 255 | 256 | val stderr = object : LineProcessingOutputStream() { 257 | override fun processLine(line: String) { 258 | if (line.startsWith(REFLECTION_WARNING_PREFIX)) { 259 | if (reflectionWarnings.projectOnly) { 260 | val colon = line.indexOf(':') 261 | val file = line.substring(REFLECTION_WARNING_PREFIX.length, colon) 262 | val found = sourceRoots.find { File(it, file).exists() } != null 263 | if (found) { 264 | reflectionWarningCount++ 265 | System.err.print(line) 266 | } else { 267 | libraryReflectionWarningCount++ 268 | } 269 | } else { 270 | reflectionWarningCount++ 271 | System.err.print(line) 272 | } 273 | } else { 274 | System.err.print(line) 275 | } 276 | } 277 | } 278 | 279 | executeScript(script, stdout, stderr) 280 | 281 | if (libraryReflectionWarningCount > 0) { 282 | System.err.println("$libraryReflectionWarningCount reflection warnings from dependencies") 283 | } 284 | if (reflectionWarnings.asErrors && reflectionWarningCount > 0) { 285 | throw ExecException("$reflectionWarningCount reflection warnings found") 286 | } 287 | } 288 | } 289 | 290 | private fun executeScript(script: String, stdout: OutputStream, stderr: OutputStream) { 291 | val file = createTempFile("clojure-compiler", ".clj", temporaryDir) 292 | file.bufferedWriter().use { out -> 293 | out.write("$script\n") 294 | } 295 | 296 | // println("Classpath: " + classpath.joinToString(", ")) 297 | 298 | val exec = JavaExecHandleBuilder(fileResolver) 299 | copyTo(exec) 300 | exec.main = "clojure.main" 301 | exec.classpath = classpath 302 | exec.setArgs(listOf("-i", file.canonicalPath)) 303 | exec.defaultCharacterEncoding = "UTF8" 304 | 305 | exec.standardOutput = stdout 306 | exec.errorOutput = stderr 307 | 308 | val result = exec.build().start().waitForFinish() 309 | 310 | stdout.close() 311 | stderr.close() 312 | 313 | result.assertNormalExitValue() 314 | } 315 | 316 | fun findNamespaces(): List { 317 | 318 | fun findNamespace(file: File, roots: Set): String { 319 | var current = file.parentFile 320 | var namespace = demunge(file.name.substring(0, file.name.lastIndexOf('.'))) 321 | while (current != null) { 322 | if (roots.contains(current.canonicalPath)) { 323 | return namespace 324 | } 325 | namespace = demunge(current.name) + '.' + namespace 326 | current = current.parentFile 327 | } 328 | throw RuntimeException("No source root found for ${file.canonicalPath}") 329 | } 330 | 331 | val sources = getSource() 332 | // println("Sources: " + sources.joinToString(", ")) 333 | val roots = getSourceRoots() 334 | // println("Roots: " + roots.joinToString(", ")) 335 | val namespaces = sources.map { findNamespace(it, roots) }.toList() 336 | return namespaces 337 | } 338 | 339 | private fun getSourceRoots(): HashSet { 340 | val roots = source 341 | .filter { it is SourceDirectorySet } 342 | .flatMap { (it as SourceDirectorySet).srcDirs } 343 | .map { it.canonicalPath } 344 | .toHashSet() 345 | return roots 346 | } 347 | 348 | companion object { 349 | val CHAR_MAP = mapOf('-' to "_", 350 | ':' to "_COLON_", 351 | '+' to "_PLUS_", 352 | '>' to "_GT_", 353 | '<' to "_LT_", 354 | '=' to "_EQ_", 355 | '~' to "_TILDE_", 356 | '!' to "_BANG_", 357 | '@' to "_CIRCA_", 358 | '#' to "_SHARP_", 359 | '\'' to "_SINGLEQUOTE_", 360 | '"' to "_DOUBLEQUOTE_", 361 | '%' to "_PERCENT_", 362 | '^' to "_CARET_", 363 | '&' to "_AMPERSAND_", 364 | '*' to "_STAR_", 365 | '|' to "_BAR_", 366 | '{' to "_LBRACE_", 367 | '}' to "_RBRACE_", 368 | '[' to "_LBRACK_", 369 | ']' to "_RBRACK_", 370 | '/' to "_SLASH_", 371 | '\\' to "_BSLASH_", 372 | '?' to "_QMARK_") 373 | 374 | val DEMUNGE_MAP = CHAR_MAP.map { it.value to it.key }.toMap() 375 | val DEMUNGE_PATTERN = Pattern.compile(DEMUNGE_MAP.keys 376 | .sortedByDescending { it.length } 377 | .map { "\\Q$it\\E" } 378 | .joinToString("|")) 379 | 380 | val REFLECTION_WARNING_PREFIX = "Reflection warning, " 381 | 382 | fun munge(name: String): String { 383 | val sb = StringBuilder() 384 | for (c in name) { 385 | if (CHAR_MAP.containsKey(c)) 386 | sb.append(CHAR_MAP[c]) 387 | else 388 | sb.append(c) 389 | } 390 | return sb.toString() 391 | } 392 | 393 | fun demunge(mungedName: String): String { 394 | val sb = StringBuilder() 395 | val m = DEMUNGE_PATTERN.matcher(mungedName) 396 | var lastMatchEnd = 0 397 | while (m.find()) { 398 | val start = m.start() 399 | val end = m.end() 400 | // Keep everything before the match 401 | sb.append(mungedName.substring(lastMatchEnd, start)) 402 | lastMatchEnd = end 403 | // Replace the match with DEMUNGE_MAP result 404 | val origCh = DEMUNGE_MAP[m.group()] 405 | sb.append(origCh) 406 | } 407 | // Keep everything after the last match 408 | sb.append(mungedName.substring(lastMatchEnd)) 409 | return sb.toString() 410 | } 411 | } 412 | } 413 | 414 | 415 | open class ClojureTestRunner @Inject constructor(val fileResolver: FileResolver) : 416 | ConventionTask(), 417 | JavaForkOptions by DefaultJavaForkOptions(fileResolver) { 418 | 419 | var classpath: FileCollection = SimpleFileCollection() 420 | var namespaces: Collection = emptyList() 421 | var junitReport: File? = null 422 | 423 | @TaskAction 424 | fun test() { 425 | logger.info("Starting ClojureTestRunner task") 426 | 427 | val namespaces = conventionMapping.getConventionValue>(emptyList(), "namespaces", false) 428 | if (namespaces.isEmpty()) { 429 | logger.info("No Clojure namespaces defined, skipping $name") 430 | return 431 | } 432 | 433 | logger.info("Testing " + namespaces.joinToString(", ")) 434 | 435 | val namespaceVec = namespaces.joinToString(" ", "'[", "]") 436 | val runnerInvocation = if (junitReport != null) 437 | """(run-tests $namespaceVec "${junitReport?.absolutePath}")""" 438 | else 439 | """(run-tests $namespaceVec)""" 440 | 441 | val script = "$testRunnerScript\n$runnerInvocation" 442 | 443 | // println(script) 444 | 445 | executeScript(script) 446 | } 447 | 448 | private fun executeScript(script: String) { 449 | val file = createTempFile("clojure-compiler", ".clj", temporaryDir) 450 | file.bufferedWriter().use { out -> 451 | out.write("$script\n") 452 | } 453 | 454 | val classpath = conventionMapping.getConventionValue(SimpleFileCollection(), "classpath", false) 455 | 456 | // println("Classpath: " + classpath.joinToString(", ")) 457 | 458 | val exec = JavaExecHandleBuilder(fileResolver) 459 | copyTo(exec) 460 | exec.main = "clojure.main" 461 | exec.classpath = classpath 462 | exec.setArgs(listOf("-i", file.canonicalPath)) 463 | 464 | exec.build().start().waitForFinish().assertNormalExitValue() 465 | } 466 | 467 | companion object { 468 | val testRunnerScript = 469 | ClojureTestRunner::class.java.getResourceAsStream("/cursive/test_runner.clj").bufferedReader().use { it.readText() } 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /src/main/kotlin/cursive/LineProcessingOutputStream.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Colin Fleming 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cursive 18 | 19 | import java.io.OutputStream 20 | import java.nio.ByteBuffer 21 | import java.nio.CharBuffer 22 | import java.nio.charset.Charset 23 | import java.nio.charset.CharsetDecoder 24 | import java.nio.charset.CoderResult 25 | 26 | abstract class LineProcessingOutputStream : OutputStream() { 27 | val line = StringBuilder() 28 | val bytes: ByteBuffer = ByteBuffer.allocate(8192) 29 | val chars: CharBuffer = CharBuffer.allocate(8192) 30 | val decoder: CharsetDecoder = Charset.forName("UTF8").newDecoder() 31 | 32 | override fun write(b: Int) { 33 | bytes.put(b.toByte()) 34 | process(false) 35 | } 36 | 37 | override fun write(b: ByteArray) { 38 | bytes.put(b) 39 | process(false) 40 | } 41 | 42 | override fun write(b: ByteArray, off: Int, len: Int) { 43 | bytes.put(b, off, len) 44 | process(false) 45 | } 46 | 47 | fun process(endOfInput: Boolean) { 48 | do { 49 | bytes.flip() 50 | val result = decoder.decode(bytes, chars, endOfInput) 51 | bytes.compact() 52 | 53 | chars.flip() 54 | while (chars.remaining() > 0) { 55 | val ch = chars.get() 56 | line.append(ch) 57 | if (ch == '\n') { 58 | processLine(line.toString()) 59 | line.setLength(0) 60 | } 61 | } 62 | chars.compact() 63 | } while (result == CoderResult.OVERFLOW) 64 | } 65 | 66 | override fun close() { 67 | process(true) 68 | if (line.length > 0) { 69 | processLine(line.toString() + "\n") 70 | } 71 | } 72 | 73 | abstract fun processLine(line: String) 74 | } 75 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/com.cursive-ide.clojure.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2016 Colin Fleming 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | implementation-class=cursive.ClojurePlugin 18 | -------------------------------------------------------------------------------- /src/main/resources/cursive/test_runner.clj: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright 2016 Colin Fleming 3 | ; 4 | ; Licensed under the Apache License, Version 2.0 (the "License") 5 | ; you may not use this file except in compliance with the License. 6 | ; You may obtain a copy of the License at 7 | ; 8 | ; http://www.apache.org/licenses/LICENSE-2.0 9 | ; 10 | ; Unless required by applicable law or agreed to in writing, software 11 | ; distributed under the License is distributed on an "AS IS" BASIS, 12 | ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | ; See the License for the specific language governing permissions and 14 | ; limitations under the License. 15 | ; 16 | 17 | (ns cursive.test-runner 18 | (:require 19 | [clojure.java.io] 20 | [clojure.test] 21 | [clojure.test.junit]) 22 | (:import (java.io File))) 23 | 24 | (defn- exit-code 25 | [summary] 26 | (if (clojure.test/successful? summary) 27 | 0 28 | 1)) 29 | 30 | (let [plain-report clojure.test/report 31 | junit-report clojure.test.junit/junit-report] 32 | (defn- junit-logging-report-fn [plain-test-out] 33 | (fn [m] 34 | (junit-report m) 35 | (binding [clojure.test/*test-out* plain-test-out] 36 | (plain-report m))))) 37 | 38 | (defn- run-tests-with-junit-report 39 | [^String junit-report-filename & ns-syms] 40 | 41 | (when-let [report-dir (.getParentFile (File. junit-report-filename))] 42 | (.mkdirs report-dir)) 43 | 44 | (let [plain-test-out clojure.test/*test-out*] 45 | (with-open [xml-test-out (clojure.java.io/writer junit-report-filename)] 46 | (binding [clojure.test/*test-out* xml-test-out 47 | clojure.test.junit/junit-report (junit-logging-report-fn plain-test-out)] 48 | (clojure.test.junit/with-junit-output 49 | (apply clojure.test/run-tests ns-syms)))))) 50 | 51 | (defn run-tests* 52 | [ns-syms runner-fn] 53 | (apply require ns-syms) 54 | (->> ns-syms 55 | (apply runner-fn) 56 | (exit-code) 57 | (System/exit))) 58 | 59 | (defn run-tests 60 | ([ns-syms] 61 | (run-tests* ns-syms clojure.test/run-tests)) 62 | 63 | ([ns-syms ^String junit-report-filename] 64 | (run-tests* ns-syms (partial run-tests-with-junit-report junit-report-filename)))) 65 | --------------------------------------------------------------------------------