From 42b74236be56cb97f87b453f1c49a6fc59902283 Mon Sep 17 00:00:00 2001 From: Piotr Bzdyl Date: Thu, 24 Nov 2016 11:30:27 +0100 Subject: [PATCH 1/5] Added support for multiple source sets (issue #11) and basic support for Gradle incremental tasks (issue #10). When Gradle detects there were no changes to the input files, it won't call Clojure compile task at all. --- src/main/kotlin/cursive/ClojurePlugin.kt | 136 ++++++++++------------- 1 file changed, 60 insertions(+), 76 deletions(-) diff --git a/src/main/kotlin/cursive/ClojurePlugin.kt b/src/main/kotlin/cursive/ClojurePlugin.kt index d1e8d54..3efa516 100644 --- a/src/main/kotlin/cursive/ClojurePlugin.kt +++ b/src/main/kotlin/cursive/ClojurePlugin.kt @@ -22,7 +22,6 @@ import org.gradle.api.Project import org.gradle.api.file.FileCollection import org.gradle.api.file.SourceDirectorySet import org.gradle.api.internal.ConventionTask -import org.gradle.api.internal.HasConvention import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.collections.SimpleFileCollection import org.gradle.api.internal.plugins.DslObject @@ -34,6 +33,7 @@ import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.compile.AbstractCompile +import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.process.JavaForkOptions import org.gradle.process.internal.DefaultJavaForkOptions import org.gradle.process.internal.ExecException @@ -53,60 +53,32 @@ class ClojurePlugin : Plugin { override fun apply(project: Project) { logger.info("Applying ClojurePlugin") - val javaBasePlugin = project.plugins.apply(JavaBasePlugin::class.java) + project.plugins.apply(JavaBasePlugin::class.java) + project.plugins.apply(JavaPlugin::class.java) + val javaPluginConvention = project.convention.getPlugin(JavaPluginConvention::class.java) - project.plugins.apply(JavaPlugin::class.java) + javaPluginConvention.sourceSets?.all { sourceSet -> + val compileTask = createClojureCompileTask(project, sourceSet) - val mainSourceSet = javaPluginConvention.sourceSets?.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - if (mainSourceSet is HasConvention) { - val mainCompileTask = createCompileTask(project, mainSourceSet) - val conventionMapping = mainCompileTask.conventionMapping - conventionMapping.map("classpath", { - mainSourceSet.compileClasspath - .plus(SimpleFileCollection(mainSourceSet.allSource.srcDirs)) - .plus(SimpleFileCollection(mainSourceSet.output.classesDir)) - }) - conventionMapping.map("namespaces", { - mainCompileTask.findNamespaces() - }) - } - val testSourceSet = javaPluginConvention.sourceSets?.getByName(SourceSet.TEST_SOURCE_SET_NAME) - if (testSourceSet is HasConvention) { - val testCompileTask = createCompileTask(project, testSourceSet) - - val mainSrcDirs = mainSourceSet?.allSource?.srcDirs ?: emptyList() - val testSrcDirs = testSourceSet.allSource.srcDirs - val outputDirs = if (mainSourceSet != null) - listOf(mainSourceSet.output.classesDir, testSourceSet.output.classesDir) - else - listOf(testSourceSet.output.classesDir) - - val compileMapping = testCompileTask.conventionMapping - compileMapping.map("classpath", { - testSourceSet.compileClasspath - .plus(SimpleFileCollection(mainSrcDirs + testSrcDirs + outputDirs)) - }) - compileMapping.map("namespaces", { - testCompileTask.findNamespaces() - }) - - val testTask = createTestTask(project) - - val testRunnerMapping = testTask.conventionMapping - testRunnerMapping.map("classpath", { - testSourceSet.runtimeClasspath.plus(SimpleFileCollection(mainSrcDirs + testSrcDirs + outputDirs)) - }) - testRunnerMapping.map("namespaces", { - testCompileTask.findNamespaces() - }) - - testTask.dependsOn(testCompileTask.name) + if (sourceSet.name == SourceSet.TEST_SOURCE_SET_NAME) { + val testTask = createTestTask(project) + + val testTaskMapping = testTask.conventionMapping + testTaskMapping.map("classpath", { + sourceSet.runtimeClasspath + }) + testTaskMapping.map("namespaces", { + compileTask.findNamespaces() + }) + + testTask.dependsOn(compileTask.name) + } } } - fun createCompileTask(project: Project, sourceSet: SourceSet): ClojureCompiler { + fun createClojureCompileTask(project: Project, sourceSet: SourceSet): ClojureCompile { val projectInternal = project as ProjectInternal val sourceRootDir: String = "src/${sourceSet.name}/clojure" @@ -124,26 +96,32 @@ class ClojurePlugin : Plugin { sourceSet.resources?.filter?.exclude { clojureDirSet.contains(it.file) } val name = sourceSet.getCompileTaskName("clojure") - val compilerClass = ClojureCompiler::class.java - logger.info("Creating Clojure compile task $name with class $compilerClass") - val compile = project.tasks.create(name, compilerClass) - compile.description = "Compiles the $sourceSet Clojure code" - - val javaTask = project.tasks.findByName(sourceSet.compileJavaTaskName) - if (javaTask != null) { - compile.dependsOn(javaTask.name) + val clojureCompileClass = ClojureCompile::class.java + logger.info("Creating Clojure compile task $name with class $clojureCompileClass") + val clojureCompileTask = project.tasks.create(name, clojureCompileClass) + clojureCompileTask.description = "Compiles the $sourceSet Clojure code" + + val javaCompileTask = project.tasks.findByName(sourceSet.compileJavaTaskName) + if (javaCompileTask != null) { + clojureCompileTask.dependsOn(javaCompileTask.name) } - project.tasks.findByName(sourceSet.classesTaskName)?.dependsOn(compile.name) + project.tasks.findByName(sourceSet.classesTaskName)?.dependsOn(clojureCompileTask.name) - val conventionMapping = compile.conventionMapping + val conventionMapping = clojureCompileTask.conventionMapping + conventionMapping.map("classpath", { + sourceSet.compileClasspath + }) + conventionMapping.map("namespaces", { + clojureCompileTask.findNamespaces() + }) conventionMapping.map("destinationDir", { sourceSet.output.classesDir }) - compile.source(clojureDirSet) + clojureCompileTask.source(clojureDirSet) - return compile + return clojureCompileTask } } @@ -161,7 +139,6 @@ fun createTestTask(project: Project): ClojureTestRunner { return testRunner } - interface ClojureSourceSet { fun getClojure(): SourceDirectorySet fun clojure(configureClosure: Closure?): ClojureSourceSet @@ -178,14 +155,14 @@ open class ClojureSourceSetImpl(displayName: String?, resolver: FileResolver?) : override fun getClojure(): SourceDirectorySet = clojure override fun clojure(configureClosure: Closure?): ClojureSourceSet { - ApiFacade.configureByClosure(this, configureClosure) + ApiFacade.configureByClosure(clojure, configureClosure) return this } } class ReflectionWarnings(var enabled: Boolean, var projectOnly: Boolean, var asErrors: Boolean) -open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : +open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : AbstractCompile(), JavaForkOptions by DefaultJavaForkOptions(fileResolver) { @@ -205,8 +182,15 @@ open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : } @TaskAction + fun compile(inputs: IncrementalTaskInputs) { + compile() + } + override fun compile() { - logger.info("Starting ClojureCompiler task") + logger.info("Starting ClojureCompile task") + + destinationDir.deleteRecursively() + destinationDir.mkdirs() if (copySourceToOutput ?: !aotCompile) { project.copy { @@ -240,8 +224,6 @@ open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : "(System/exit 0)") .joinToString("\n") - // println(script) - val stdout = object : LineProcessingOutputStream() { override fun processLine(line: String) { System.out.print(line) @@ -293,12 +275,16 @@ open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : out.write("$script\n") } - // println("Classpath: " + classpath.joinToString(", ")) - val exec = JavaExecHandleBuilder(fileResolver) copyTo(exec) exec.main = "clojure.main" - exec.classpath = classpath + + // clojure.core/compile requires following on its classpath: + // - libs (this.classpath) + // - compiled namespaces sources (getSourceRootsFiles) + // - *compile-path* directory (this.destinationDir) + exec.classpath = classpath + SimpleFileCollection(getSourceRootsFiles()) + SimpleFileCollection(destinationDir) + exec.setArgs(listOf("-i", file.canonicalPath)) exec.defaultCharacterEncoding = "UTF8" @@ -329,10 +315,8 @@ open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : } val sources = getSource() - // println("Sources: " + sources.joinToString(", ")) val roots = getSourceRoots() - // println("Roots: " + roots.joinToString(", ")) - val namespaces = sources.map { findNamespace(it, roots) }.toList() + val namespaces = sources.map { findNamespace(it, roots) } return namespaces } @@ -345,6 +329,10 @@ open class ClojureCompiler @Inject constructor(val fileResolver: FileResolver) : return roots } + private fun getSourceRootsFiles(): List { + return getSourceRoots().map(::File) + } + companion object { val CHAR_MAP = mapOf('-' to "_", ':' to "_COLON_", @@ -440,8 +428,6 @@ open class ClojureTestRunner @Inject constructor(val fileResolver: FileResolver) val script = "$testRunnerScript\n$runnerInvocation" - // println(script) - executeScript(script) } @@ -453,8 +439,6 @@ open class ClojureTestRunner @Inject constructor(val fileResolver: FileResolver) val classpath = conventionMapping.getConventionValue(SimpleFileCollection(), "classpath", false) - // println("Classpath: " + classpath.joinToString(", ")) - val exec = JavaExecHandleBuilder(fileResolver) copyTo(exec) exec.main = "clojure.main" From 2c5e8f359f959ca1102753b06fc34f14e7e6a09f Mon Sep 17 00:00:00 2001 From: Piotr Bzdyl Date: Wed, 11 Jan 2017 14:54:04 +0100 Subject: [PATCH 2/5] Added cleaning outdated output files created by Clojure compile task. --- .gitignore | 1 + build.gradle | 17 +- gradle/wrapper/gradle-wrapper.jar | Bin 53324 -> 53319 bytes gradlew.bat | 180 +++++++++--------- src/main/kotlin/cursive/ClojurePlugin.kt | 49 +++-- .../com.cursive-ide.clojure.properties | 17 -- .../BasicClojureProjectTest/build.gradle | 27 +++ .../src/main/clojure/basic_project/core.clj | 21 ++ .../test/clojure/basic_project/core_test.clj | 22 +++ .../IncrementalCompilationTest/build.gradle | 27 +++ .../src/main/clojure/basic_project/core.clj | 20 ++ .../TestFailureFailsBuildTest/build.gradle | 27 +++ .../src/main/clojure/basic_project/core.clj | 21 ++ .../test/clojure/basic_project/core_test.clj | 22 +++ .../kotlin/cursive/BasicClojureProjectTest.kt | 55 ++++++ .../cursive/IncrementalCompilationTest.kt | 121 ++++++++++++ .../kotlin/cursive/IntegrationTestBase.kt | 93 +++++++++ .../cursive/TestFailureFailsBuildTest.kt | 36 ++++ 18 files changed, 637 insertions(+), 119 deletions(-) delete mode 100644 src/main/resources/META-INF/gradle-plugins/com.cursive-ide.clojure.properties create mode 100644 src/test/int-tests-projects/cursive/BasicClojureProjectTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj create mode 100644 src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/test/clojure/basic_project/core_test.clj create mode 100644 src/test/int-tests-projects/cursive/IncrementalCompilationTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj create mode 100644 src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/main/clojure/basic_project/core.clj create mode 100644 src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/test/clojure/basic_project/core_test.clj create mode 100644 src/test/kotlin/cursive/BasicClojureProjectTest.kt create mode 100644 src/test/kotlin/cursive/IncrementalCompilationTest.kt create mode 100644 src/test/kotlin/cursive/IntegrationTestBase.kt create mode 100644 src/test/kotlin/cursive/TestFailureFailsBuildTest.kt diff --git a/.gitignore b/.gitignore index a59b1cb..49bacff 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /.idea/shelf /.idea/libraries /.idea/modules +/.idea/atlassian-ide-plugin.xml out dist diff --git a/build.gradle b/build.gradle index 2a4f07e..1f9ebfc 100644 --- a/build.gradle +++ b/build.gradle @@ -32,11 +32,14 @@ buildscript { group 'com.cursive-ide' version '1.1.0' +ext.pluginId = 'com.cursive-ide.clojure' +ext.pluginImplementationClass = 'cursive.ClojurePlugin' apply plugin: 'kotlin' apply plugin: 'groovy' apply plugin: 'maven' apply plugin: 'com.gradle.plugin-publish' +apply plugin: "java-gradle-plugin" project.afterEvaluate { // Ugh, see https://discuss.gradle.org/t/kotlin-groovy-and-java-compilation/14903/9 @@ -55,6 +58,18 @@ dependencies { compile gradleApi() compile localGroovy() compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + testCompile gradleTestKit() + testCompile "junit:junit:4.12" + testCompile 'net.wuerl.kotlin:assertj-core-kotlin:0.1.3' +} + +gradlePlugin { + plugins { + clojurePlugin { + id = pluginId + implementationClass = pluginImplementationClass + } + } } pluginBundle { @@ -65,7 +80,7 @@ pluginBundle { plugins { clojurePlugin { - id = 'com.cursive-ide.clojure' + id = pluginId displayName = 'Gradle Clojure plugin' } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3baa851b28c65f87dd36a6748e1a85cf360c1301..d3b83982b9b1bccad955349d702be9b884c6e049 100644 GIT binary patch delta 2138 zcmZ9M4Nz276vywr-DMZuT^14c~ z6N;x&M;f)+^mdeHuqEwRj6nCY1qM8oLx@i4ZiG+~!%EWZP;li7R67pC#h3XClONGH5 zio@TL!T@2C%fi^8<%&22@5_Eg@TPn(f(D(^&kISo<}4%Lz87RjhWxiNvO#ZEAUR;p z@WY8DS|!JcHa1`^POP);j?vIm$!FcZ>+D{HG-{wyW(2iU(~vReAj=_5S1?EQf^?-2 z2FtTD)m9pn8ZsED5jxYb@aN3T+lq?ceP2$=EBWz{ zft`PK?x?7DdFH(scP8AJsL+J3TT?xwb)e;&pH8nX`sIZkVVi@iNgS80$qtFG^lN(e zT32G(2VZXeblTg8f}+DGwJ$0TUwLAC%2Zq9!z1RHlMiZq9v9P^B5}i8*uM3GsFlfmLRHZhg_AV+d*<2@!GEq*pvEO85sQMC{xb`>6aA`}8lsT8z4zs0q>J~Gfi}guNGvRPQ zBX^F>XPVSwfKhOzIZaBt3St~n#oYzPju^a{S_iM&O)q41p)De9wuBomWi2UO+9d}s zaln0t5tW7(Uh*6Grt2WJ1$zNrZ{azU1ionVhct;6s#;Vitf5%KjbLv`;6g#u%9c#6 zJhxVcOLJR!RIj9HsT|Rs)`=FOwF0j!KJSzha*u8$n(pn<(^#rhqA*Tr0b?7pw6^hF zkOuLR4h1aK_)UJ|9Lc#QuEFgxLK3_QiF{;6$HvloU~ij&IrpuPJet8#Nb{OSnsfeW zt`1OvrJd!BXy>_=dc=0UA1GZa^oYIKbbY^Ahk0j zQ_u)dsG#upuz~(-$-hZi*{s2Ag-de_mPh0i*H#R!Ycp zk*28(2Fl_gv6tnU+sktwm1CZYUe*&^_BHBk;|S@b%_#Pdg~49tpXKxX>1uj%E8U0q zRlaIRGq=YJGF=AT%uA=Ts#F%>Uf>oed#k~5qmA^$N)myQ%yOlJAT zF%6f|+5AGMOZxb=R>kBp2z)N>M_`+*41ro7xt9wfp{iQ~;ySTcib%iD5dq25gt#J7 zu3P7Y!C1ILhJh;5*ByhYOGe!=n7pc2FqEuy73A)wwQ_!orY0vTA1xg*+V?xkcS1FX zs3TdQZ~sNrtoL5rchVHLwISuH|N1*gNsncZ{*0XxaysPK{`!624=1PsHuae*D?QG> zpQ!%2?rvGvs-D96UNzktY$w_0Wn|9t6*;!3AoUNDhXG+x9opd&O@-G_g>HMY24G(rb`-A%jYg_)VrxjzL4Nj>X{ij%Xp!-z z?TU!yt1bcgwy+BQYL^vNsBvqkWLQA%m);eE*7A!|+}c~k3eI0t4+&elYc>i#w=Tp8 zoPhdk0y5OFM&QID~kd1Hr}kCaszS>)(|pHls6kmW16)zrDYOoKW*U{ z?Gn_UYYE0OZgouDRRSf{sb24-FCA_PLr#W1<4B!^kaa8}ks|*mG{zc-)`eD%Attn* zWl~YQRVd)Lm6NQePpsj3{=IU348R0(b^huK>XT_#@+7LYDfmlIvu&ItjUKWE;=gJe z$Iuiy?C_`eZ3cvttsE1WN++iU)0M4WXudjjLO=x!WE85I@3iJGnOhnV7t+R7!Mq)` zigeWK_qxy(>Av)wU4b40!OD?1s5Ak5!bBebYz2XqhGzIt|2BcV$ZyM7t1BS~;My+` zk&80X{Ys{m=8u!LnZ7mc;MxP(%@vW)=Q>jv%Jp=na{r_5seK^W1}7{M39j!zfnsMM z@2=w;8Bvl${)|bk2l2r7PWEA9|iPq zRS<1&Q{x$hF%emxfZiSS^F(J{qOaQCL0P<=V-6Rh+d+F6LX7||C;}*mzO<_`lVp#D z*Gr+a5GYOjo(+_scC{lAiE&+<2af{r1e7Kw-YFeV*@bG$6runBZt?xtWw^H{1z!0{ z?u0~(ay8Q-M?5CBpq*oORY8ewIVXCt>3r^YaNZdhl6azfs-5RGR{J`qI#p^^_(M<9 z`D#SxWY>T|OJ~?QJnX#=j_JC99`3c=8~IWaeT?tz3-E~&`S{S^JB(;Cy^~`G8qgx6 mGXSB304=(NP+O+~r{pnaF?oZ78XldNUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/kotlin/cursive/ClojurePlugin.kt b/src/main/kotlin/cursive/ClojurePlugin.kt index 3efa516..b419f13 100644 --- a/src/main/kotlin/cursive/ClojurePlugin.kt +++ b/src/main/kotlin/cursive/ClojurePlugin.kt @@ -34,6 +34,7 @@ import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.incremental.IncrementalTaskInputs +import org.gradle.api.tasks.incremental.InputFileDetails import org.gradle.process.JavaForkOptions import org.gradle.process.internal.DefaultJavaForkOptions import org.gradle.process.internal.ExecException @@ -181,17 +182,19 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : return reflectionWarnings } - @TaskAction - fun compile(inputs: IncrementalTaskInputs) { - compile() + override fun compile() { + throw UnsupportedOperationException() } - override fun compile() { + @TaskAction + fun compile(inputs: IncrementalTaskInputs) { logger.info("Starting ClojureCompile task") - destinationDir.deleteRecursively() destinationDir.mkdirs() + inputs.outOfDate { removeOutputFilesDerivedFromInputFile(it, destinationDir) } + inputs.removed { removeOutputFilesDerivedFromInputFile(it, destinationDir) } + if (copySourceToOutput ?: !aotCompile) { project.copy { it.from(getSource()).into(destinationDir) @@ -269,6 +272,30 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : } } + private fun removeOutputFilesDerivedFromInputFile(inputFileDetails: InputFileDetails, destinationDir: File) { + val sourceAbsoluteFile = inputFileDetails.file + if (isClojureSource(sourceAbsoluteFile)) { + logger.debug("Removing class files for {}", inputFileDetails.file) + val sourceCanonicalFileName = sourceAbsoluteFile.canonicalPath + val sourceFileRoot = getSourceRootsFiles() + .find { sourceCanonicalFileName.startsWith(it.canonicalPath) } + ?: throw IllegalStateException("No source root found for source file ${sourceAbsoluteFile}") + val sourceRelativeFile = sourceAbsoluteFile.relativeTo(sourceFileRoot) + val sourceRelativeDirectory = sourceRelativeFile.parentFile + val sourceFileName = sourceAbsoluteFile.nameWithoutExtension + destinationDir.resolve(sourceRelativeDirectory) + .listFiles { file -> file.name.startsWith(sourceFileName) } + ?.forEach { + logger.debug("Deleting derived file {}", it) + it.delete() + } + } + } + + private fun isClojureSource(file: File): Boolean { + return CLJ_EXTENSION_REGEX.matches(file.extension) && getSourceRoots().any { file.canonicalPath.startsWith(it) } + } + private fun executeScript(script: String, stdout: OutputStream, stderr: OutputStream) { val file = createTempFile("clojure-compiler", ".clj", temporaryDir) file.bufferedWriter().use { out -> @@ -281,7 +308,7 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : // clojure.core/compile requires following on its classpath: // - libs (this.classpath) - // - compiled namespaces sources (getSourceRootsFiles) + // - namespaces sources to be compiled (getSourceRootsFiles) // - *compile-path* directory (this.destinationDir) exec.classpath = classpath + SimpleFileCollection(getSourceRootsFiles()) + SimpleFileCollection(destinationDir) @@ -321,16 +348,15 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : } private fun getSourceRoots(): HashSet { - val roots = source - .filter { it is SourceDirectorySet } - .flatMap { (it as SourceDirectorySet).srcDirs } + return getSourceRootsFiles() .map { it.canonicalPath } .toHashSet() - return roots } private fun getSourceRootsFiles(): List { - return getSourceRoots().map(::File) + return source + .filter { it is SourceDirectorySet } + .flatMap { (it as SourceDirectorySet).srcDirs } } companion object { @@ -359,6 +385,7 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : '\\' to "_BSLASH_", '?' to "_QMARK_") + val CLJ_EXTENSION_REGEX = "cljc?".toRegex() val DEMUNGE_MAP = CHAR_MAP.map { it.value to it.key }.toMap() val DEMUNGE_PATTERN = Pattern.compile(DEMUNGE_MAP.keys .sortedByDescending { it.length } diff --git a/src/main/resources/META-INF/gradle-plugins/com.cursive-ide.clojure.properties b/src/main/resources/META-INF/gradle-plugins/com.cursive-ide.clojure.properties deleted file mode 100644 index e615d51..0000000 --- a/src/main/resources/META-INF/gradle-plugins/com.cursive-ide.clojure.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2016 Colin Fleming -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -implementation-class=cursive.ClojurePlugin diff --git a/src/test/int-tests-projects/cursive/BasicClojureProjectTest/build.gradle b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/build.gradle new file mode 100644 index 0000000..2e3ff66 --- /dev/null +++ b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/build.gradle @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} diff --git a/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj new file mode 100644 index 0000000..f8b3b7c --- /dev/null +++ b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj @@ -0,0 +1,21 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns basic-project.core) + +(defn hello [name] + (println "Generating message for" name) + (str "Hello " name)) diff --git a/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/test/clojure/basic_project/core_test.clj b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/test/clojure/basic_project/core_test.clj new file mode 100644 index 0000000..7940020 --- /dev/null +++ b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/test/clojure/basic_project/core_test.clj @@ -0,0 +1,22 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns basic-project.core-test + (:require [basic-project.core :refer [hello]] + [clojure.test :refer :all])) + +(deftest test-hello + (is (= "Hello World" (hello "World")))) diff --git a/src/test/int-tests-projects/cursive/IncrementalCompilationTest/build.gradle b/src/test/int-tests-projects/cursive/IncrementalCompilationTest/build.gradle new file mode 100644 index 0000000..2e3ff66 --- /dev/null +++ b/src/test/int-tests-projects/cursive/IncrementalCompilationTest/build.gradle @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} diff --git a/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj b/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj new file mode 100644 index 0000000..a661c3c --- /dev/null +++ b/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj @@ -0,0 +1,20 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns basic-project.core) + +(defn hello [name] + (str "Hello " name)) diff --git a/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/build.gradle b/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/build.gradle new file mode 100644 index 0000000..2e3ff66 --- /dev/null +++ b/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/build.gradle @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} diff --git a/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/main/clojure/basic_project/core.clj b/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/main/clojure/basic_project/core.clj new file mode 100644 index 0000000..f8b3b7c --- /dev/null +++ b/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/main/clojure/basic_project/core.clj @@ -0,0 +1,21 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns basic-project.core) + +(defn hello [name] + (println "Generating message for" name) + (str "Hello " name)) diff --git a/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/test/clojure/basic_project/core_test.clj b/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/test/clojure/basic_project/core_test.clj new file mode 100644 index 0000000..34ebd19 --- /dev/null +++ b/src/test/int-tests-projects/cursive/TestFailureFailsBuildTest/src/test/clojure/basic_project/core_test.clj @@ -0,0 +1,22 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns basic-project.core-test + (:require [basic-project.core :refer [hello]] + [clojure.test :refer :all])) + +(deftest test-hello + (is (= "Hello World!" (hello "World")))) diff --git a/src/test/kotlin/cursive/BasicClojureProjectTest.kt b/src/test/kotlin/cursive/BasicClojureProjectTest.kt new file mode 100644 index 0000000..2c59e2e --- /dev/null +++ b/src/test/kotlin/cursive/BasicClojureProjectTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class BasicClojureProjectTest : IntegrationTestBase() { + val coreNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/core.clj") + + @Test + fun basicClojureProjectBuildNoAot() { + // when + val result = projectBuildRunner().withArguments("check").build() + + // then + assertThat(result.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.output).contains("Generating message for World") + buildDirFiles().forEach { println(it) } + assertSourceFileCopiedToOutputDir(coreNsSourceFile) + } + + @Test + fun basicClojureProjectBuildWithAot() { + // given + projectGradleFile().appendText("compileClojure.aotCompile = true") + + // when + val result = projectBuildRunner().withArguments("check").build() + + // then + assertThat(result.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.output).contains("Generating message for World") + assertSourceFileCompiledToOutputDir(coreNsSourceFile) + } +} \ No newline at end of file diff --git a/src/test/kotlin/cursive/IncrementalCompilationTest.kt b/src/test/kotlin/cursive/IncrementalCompilationTest.kt new file mode 100644 index 0000000..003ad30 --- /dev/null +++ b/src/test/kotlin/cursive/IncrementalCompilationTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class IncrementalCompilationTest : IntegrationTestBase() { + val coreNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/core.clj") + + @Test + fun incrementalCompileTaskUpToDateWhenNoChangesNoAot() { + // when + val firstRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + println(firstRunResult.output) + assertSourceFileCopiedToOutputDir(coreNsSourceFile) + + // when + val secondRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) + println(secondRunResult.output) + assertSourceFileCopiedToOutputDir(coreNsSourceFile) + } + + @Test + fun incrementalCompileTaskUpToDateWhenNoChangesWithAot() { + // given + projectGradleFile().appendText("compileClojure.aotCompile = true") + + // when + val firstRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + println(firstRunResult.output) + assertSourceFileCompiledToOutputDir(coreNsSourceFile) + + // when + val secondRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) + println(secondRunResult.output) + assertSourceFileCompiledToOutputDir(coreNsSourceFile) + } + + @Test + fun incrementalCompileTaskExecutedWhenSourceFileChangedNoAot() { + // given + val utilsNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/utils.clj") + + // when + val firstRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + println(firstRunResult.output) + assertSourceFileCopiedToOutputDir(coreNsSourceFile) + assertThat(destinationFileForSourceFile(utilsNsSourceFile).exists()).isFalse() + + coreNsSourceFile.delete() + utilsNsSourceFile.writeText("""(ns basic-project.utils) (defn ping [] "pong")""") + + // when + val secondRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + println(secondRunResult.output) + assertThat(destinationFileForSourceFile(coreNsSourceFile).exists()).isFalse() + assertSourceFileCopiedToOutputDir(utilsNsSourceFile) + } + + @Test + fun incrementalCompileTaskExecutedWhenNewCljFileAddedWithAot() { + // given + projectGradleFile().appendText("compileClojure.aotCompile = true") + val utilsNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/utils.clj") + + // when + val firstRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + println(firstRunResult.output) + assertSourceFileCompiledToOutputDir(coreNsSourceFile) + assertThat(compiledClassFilesForSourceFile(utilsNsSourceFile)).isEmpty() + + coreNsSourceFile.delete() + utilsNsSourceFile.writeText("""(ns basic-project.utils) (defn ping [] "pong")""") + + // when + val secondRunResult = projectBuildRunner().withArguments("check").build() + + // then + assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + println(secondRunResult.output) + assertThat(compiledClassFilesForSourceFile(coreNsSourceFile)).isEmpty() + assertSourceFileCompiledToOutputDir(utilsNsSourceFile) + } +} \ No newline at end of file diff --git a/src/test/kotlin/cursive/IntegrationTestBase.kt b/src/test/kotlin/cursive/IntegrationTestBase.kt new file mode 100644 index 0000000..36fedb7 --- /dev/null +++ b/src/test/kotlin/cursive/IntegrationTestBase.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert.assertEquals +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.io.File +import java.util.* + +open class IntegrationTestBase { + @Rule + @JvmField + val tempDir: TemporaryFolder = initTempDir() + val testProjectDir: File = tempDir.root + + @Before + fun setup() { + val sourceDirectory = File("src/test/int-tests-projects", this.javaClass.name.replace('.', '/')) + + if (sourceDirectory.exists()) { + sourceDirectory.copyRecursively(target = testProjectDir, overwrite = true) + } else { + println("Source directory ${sourceDirectory.absolutePath} doesn't exist") + } + } + + fun projectBuildRunner(): GradleRunner { + return GradleRunner.create().withPluginClasspath().withProjectDir(testProjectDir) + } + + fun projectGradleFile(): File { + return testProjectDir.resolve("build.gradle") + } + + fun buildDirFiles(): List { + return testProjectDir.resolve("build").walkTopDown().toList() + } + + fun assertSourceFileCopiedToOutputDir(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")) { + val expectedDestinationFile = destinationFileForSourceFile(sourceFile, sourceBaseDir, destinationDir) + if (!expectedDestinationFile.exists()) { + fail("Expected destination file doesn't exist for source file ${sourceFile}") + } + val sourceFileContents = sourceFile.readText() + val destinationFileContents = expectedDestinationFile.readText() + assertEquals("Source and destination files content", sourceFileContents, destinationFileContents) + } + + fun assertSourceFileCompiledToOutputDir(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")) { + val classFiles = compiledClassFilesForSourceFile(sourceFile, sourceBaseDir, destinationDir) + if (classFiles.isEmpty()) { + fail("No compiled files have been found for source file ${sourceFile}") + } + } + + fun destinationFileForSourceFile(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")): File { + val relativeSourceFile = sourceFile.relativeTo(sourceBaseDir) + val expectedDestinationFile = destinationDir.resolve(relativeSourceFile) + return expectedDestinationFile + } + + fun compiledClassFilesForSourceFile(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")): List { + val relativeSourceFile = sourceFile.relativeTo(sourceBaseDir) + val relativeSourceFileDir = relativeSourceFile.parentFile + val sourceFileName = sourceFile.nameWithoutExtension + + return destinationDir.resolve(relativeSourceFileDir).listFiles { file -> file.nameWithoutExtension.startsWith(sourceFileName) }?.toList() ?: Collections.emptyList() + } + + private fun initTempDir(): TemporaryFolder { + val tmpDir = TemporaryFolder() + tmpDir.create() + return tmpDir + } +} diff --git a/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt b/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt new file mode 100644 index 0000000..1eaec4b --- /dev/null +++ b/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class TestFailureFailsBuildTest : IntegrationTestBase() { + @Test + fun testFailureFailsBuild() { + // when + val result = projectBuildRunner().withArguments("check").buildAndFail() + + // then + println(result.output) + KotlinAssertions.assertThat(result.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + KotlinAssertions.assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + KotlinAssertions.assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.FAILED) + } +} \ No newline at end of file From 8d04cc34c9585f394af139ce3b94ba1b4ab0c22c Mon Sep 17 00:00:00 2001 From: Piotr Bzdyl Date: Wed, 11 Jan 2017 22:42:39 +0100 Subject: [PATCH 3/5] Added more integration tests. --- .../cursive/MixedClojureJavaTest/build.gradle | 44 ++++++++++ .../src/cljSS/clojure/cljSS/Example.clj | 20 +++++ .../src/javaSS/java/javaSS/Test.java | 9 ++ .../cursive/MixedJavaClojureTest/build.gradle | 44 ++++++++++ .../src/cljSS/clojure/cljSS/core.clj | 21 +++++ .../src/javaSS/java/javaSS/Example.java | 22 +++++ .../MultipleSourceSetsTest/build.gradle | 50 +++++++++++ .../src/ss1/clojure/ss1/core.clj | 20 +++++ .../src/ss2/clojure/ss2/core.clj | 21 +++++ .../src/test/clojure/test/core_test.clj | 24 +++++ .../kotlin/cursive/BasicClojureProjectTest.kt | 11 ++- .../cursive/IncrementalCompilationTest.kt | 36 ++++---- .../kotlin/cursive/IntegrationTestBase.kt | 87 +++++++++++++++---- .../kotlin/cursive/MixedClojureJavaTest.kt | 44 ++++++++++ .../kotlin/cursive/MixedJavaClojureTest.kt | 44 ++++++++++ .../kotlin/cursive/MultipleSourceSetsTest.kt | 49 +++++++++++ .../cursive/TestFailureFailsBuildTest.kt | 3 +- 17 files changed, 508 insertions(+), 41 deletions(-) create mode 100644 src/test/int-tests-projects/cursive/MixedClojureJavaTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/cljSS/clojure/cljSS/Example.clj create mode 100644 src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/javaSS/java/javaSS/Test.java create mode 100644 src/test/int-tests-projects/cursive/MixedJavaClojureTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj create mode 100644 src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java create mode 100644 src/test/int-tests-projects/cursive/MultipleSourceSetsTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss1/clojure/ss1/core.clj create mode 100644 src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss2/clojure/ss2/core.clj create mode 100644 src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/test/clojure/test/core_test.clj create mode 100644 src/test/kotlin/cursive/MixedClojureJavaTest.kt create mode 100644 src/test/kotlin/cursive/MixedJavaClojureTest.kt create mode 100644 src/test/kotlin/cursive/MultipleSourceSetsTest.kt diff --git a/src/test/int-tests-projects/cursive/MixedClojureJavaTest/build.gradle b/src/test/int-tests-projects/cursive/MixedClojureJavaTest/build.gradle new file mode 100644 index 0000000..81a11bd --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedClojureJavaTest/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} + +sourceSets { + cljSS { + compileClasspath += configurations.compile + runtimeClasspath += configurations.compile + } + javaSS { + compileClasspath += configurations.compile + runtimeClasspath += configurations.compile + compileClasspath += cljSS.output + runtimeClasspath += cljSS.output + } +} + +compileCljSSClojure { + aotCompile = true +} diff --git a/src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/cljSS/clojure/cljSS/Example.clj b/src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/cljSS/clojure/cljSS/Example.clj new file mode 100644 index 0000000..929886f --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/cljSS/clojure/cljSS/Example.clj @@ -0,0 +1,20 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns cljSS.Example + (:gen-class :methods [[test [] void]])) + +(defn -test [this]) diff --git a/src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/javaSS/java/javaSS/Test.java b/src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/javaSS/java/javaSS/Test.java new file mode 100644 index 0000000..dd13d18 --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedClojureJavaTest/src/javaSS/java/javaSS/Test.java @@ -0,0 +1,9 @@ +package javaSS; + +import cljSS.Example; + +public class Test { + public void test() { + new Example().test(); + } +} \ No newline at end of file diff --git a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/build.gradle b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/build.gradle new file mode 100644 index 0000000..9962cbc --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} + +sourceSets { + javaSS { + compileClasspath += configurations.compile + runtimeClasspath += configurations.compile + } + cljSS { + compileClasspath += configurations.compile + runtimeClasspath += configurations.compile + compileClasspath += javaSS.output + runtimeClasspath += javaSS.output + } +} + +compileCljSSClojure { + aotCompile = true +} diff --git a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj new file mode 100644 index 0000000..58c3b87 --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj @@ -0,0 +1,21 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns cljSS.core + (:import javaSS.Example)) + +(defn test [] + (.test (Example.))) diff --git a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java new file mode 100644 index 0000000..edde1c6 --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javaSS; + +public class Example { + public void test() { + } +} \ No newline at end of file diff --git a/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/build.gradle b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/build.gradle new file mode 100644 index 0000000..cdc9aee --- /dev/null +++ b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/build.gradle @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} + +sourceSets { + ss1 { + } + ss2 { + compileClasspath += configurations.compile + compileClasspath += ss1.output + runtimeClasspath += configurations.compile + runtimeClasspath += ss1.output + } + test { + compileClasspath += configurations.compile + runtimeClasspath += configurations.compile + compileClasspath += ss1.output + runtimeClasspath += ss1.output + compileClasspath += ss2.output + runtimeClasspath += ss2.output + } +} + +compileSs2Clojure { + aotCompile = true +} diff --git a/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss1/clojure/ss1/core.clj b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss1/clojure/ss1/core.clj new file mode 100644 index 0000000..6213fce --- /dev/null +++ b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss1/clojure/ss1/core.clj @@ -0,0 +1,20 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns ss1.core) + +(defn hello [] + "SourceSet1") diff --git a/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss2/clojure/ss2/core.clj b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss2/clojure/ss2/core.clj new file mode 100644 index 0000000..fe664e1 --- /dev/null +++ b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/ss2/clojure/ss2/core.clj @@ -0,0 +1,21 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns ss2.core + (:require [ss1.core])) + +(defn hello [] + (str "SourceSet2 " (ss1.core/hello))) diff --git a/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/test/clojure/test/core_test.clj b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/test/clojure/test/core_test.clj new file mode 100644 index 0000000..3cdb59a --- /dev/null +++ b/src/test/int-tests-projects/cursive/MultipleSourceSetsTest/src/test/clojure/test/core_test.clj @@ -0,0 +1,24 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns test.core-test + (:require [ss1.core] + [ss2.core] + [clojure.test :refer :all])) + +(deftest hello-test + (println "Test1" (ss1.core/hello)) + (println "Test2" (ss2.core/hello))) diff --git a/src/test/kotlin/cursive/BasicClojureProjectTest.kt b/src/test/kotlin/cursive/BasicClojureProjectTest.kt index 2c59e2e..5007703 100644 --- a/src/test/kotlin/cursive/BasicClojureProjectTest.kt +++ b/src/test/kotlin/cursive/BasicClojureProjectTest.kt @@ -32,9 +32,10 @@ class BasicClojureProjectTest : IntegrationTestBase() { assertThat(result.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertSourceFileIsOnlyCopiedToOutputDir(coreNsSourceFile) + assertThat(result.output).contains("Generating message for World") - buildDirFiles().forEach { println(it) } - assertSourceFileCopiedToOutputDir(coreNsSourceFile) } @Test @@ -49,7 +50,9 @@ class BasicClojureProjectTest : IntegrationTestBase() { assertThat(result.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertSourceFileIsOnlyCompiledToOutputDir(coreNsSourceFile) + assertThat(result.output).contains("Generating message for World") - assertSourceFileCompiledToOutputDir(coreNsSourceFile) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/cursive/IncrementalCompilationTest.kt b/src/test/kotlin/cursive/IncrementalCompilationTest.kt index 003ad30..192a4d0 100644 --- a/src/test/kotlin/cursive/IncrementalCompilationTest.kt +++ b/src/test/kotlin/cursive/IncrementalCompilationTest.kt @@ -30,16 +30,14 @@ class IncrementalCompilationTest : IntegrationTestBase() { // then assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - println(firstRunResult.output) - assertSourceFileCopiedToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCopiedToOutputDir(coreNsSourceFile) // when val secondRunResult = projectBuildRunner().withArguments("check").build() // then assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) - println(secondRunResult.output) - assertSourceFileCopiedToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCopiedToOutputDir(coreNsSourceFile) } @Test @@ -52,16 +50,14 @@ class IncrementalCompilationTest : IntegrationTestBase() { // then assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - println(firstRunResult.output) - assertSourceFileCompiledToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCompiledToOutputDir(coreNsSourceFile) // when val secondRunResult = projectBuildRunner().withArguments("check").build() // then assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) - println(secondRunResult.output) - assertSourceFileCompiledToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCompiledToOutputDir(coreNsSourceFile) } @Test @@ -74,9 +70,9 @@ class IncrementalCompilationTest : IntegrationTestBase() { // then assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - println(firstRunResult.output) - assertSourceFileCopiedToOutputDir(coreNsSourceFile) - assertThat(destinationFileForSourceFile(utilsNsSourceFile).exists()).isFalse() + assertSourceFileIsOnlyCopiedToOutputDir(coreNsSourceFile) + assertSourceFileNotCopiedToOutputDir(utilsNsSourceFile) + assertSourceFileNotCompiledToOutputDir(utilsNsSourceFile) coreNsSourceFile.delete() utilsNsSourceFile.writeText("""(ns basic-project.utils) (defn ping [] "pong")""") @@ -86,9 +82,9 @@ class IncrementalCompilationTest : IntegrationTestBase() { // then assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - println(secondRunResult.output) - assertThat(destinationFileForSourceFile(coreNsSourceFile).exists()).isFalse() - assertSourceFileCopiedToOutputDir(utilsNsSourceFile) + assertSourceFileNotCopiedToOutputDir(coreNsSourceFile) + assertSourceFileNotCompiledToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCopiedToOutputDir(utilsNsSourceFile) } @Test @@ -102,9 +98,9 @@ class IncrementalCompilationTest : IntegrationTestBase() { // then assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - println(firstRunResult.output) assertSourceFileCompiledToOutputDir(coreNsSourceFile) - assertThat(compiledClassFilesForSourceFile(utilsNsSourceFile)).isEmpty() + assertSourceFileNotCopiedToOutputDir(utilsNsSourceFile) + assertSourceFileNotCompiledToOutputDir(utilsNsSourceFile) coreNsSourceFile.delete() utilsNsSourceFile.writeText("""(ns basic-project.utils) (defn ping [] "pong")""") @@ -114,8 +110,8 @@ class IncrementalCompilationTest : IntegrationTestBase() { // then assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - println(secondRunResult.output) - assertThat(compiledClassFilesForSourceFile(coreNsSourceFile)).isEmpty() - assertSourceFileCompiledToOutputDir(utilsNsSourceFile) + assertSourceFileNotCopiedToOutputDir(coreNsSourceFile) + assertSourceFileNotCompiledToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCompiledToOutputDir(utilsNsSourceFile) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/cursive/IntegrationTestBase.kt b/src/test/kotlin/cursive/IntegrationTestBase.kt index 36fedb7..f20d04d 100644 --- a/src/test/kotlin/cursive/IntegrationTestBase.kt +++ b/src/test/kotlin/cursive/IntegrationTestBase.kt @@ -17,8 +17,7 @@ package cursive import org.gradle.testkit.runner.GradleRunner -import org.junit.Assert.assertEquals -import org.junit.Assert.fail +import org.junit.Assert.* import org.junit.Before import org.junit.Rule import org.junit.rules.TemporaryFolder @@ -54,35 +53,93 @@ open class IntegrationTestBase { return testProjectDir.resolve("build").walkTopDown().toList() } - fun assertSourceFileCopiedToOutputDir(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")) { - val expectedDestinationFile = destinationFileForSourceFile(sourceFile, sourceBaseDir, destinationDir) - if (!expectedDestinationFile.exists()) { - fail("Expected destination file doesn't exist for source file ${sourceFile}") + fun assertSourceFileCopiedToOutputDir( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")) { + + val expectedOutputFile = outputFileForSourceFile(sourceFile, sourceBaseDir, outputDir) + if (!expectedOutputFile.exists()) { + fail("Expected output file doesn't exist for source file ${sourceFile}") } val sourceFileContents = sourceFile.readText() - val destinationFileContents = expectedDestinationFile.readText() - assertEquals("Source and destination files content", sourceFileContents, destinationFileContents) + val outputFileContents = expectedOutputFile.readText() + assertEquals("Source and output files content", sourceFileContents, outputFileContents) } - fun assertSourceFileCompiledToOutputDir(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")) { - val classFiles = compiledClassFilesForSourceFile(sourceFile, sourceBaseDir, destinationDir) + fun assertSourceFileCompiledToOutputDir( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")) { + + val classFiles = compiledClassFilesForSourceFile(sourceFile, sourceBaseDir, outputDir) if (classFiles.isEmpty()) { fail("No compiled files have been found for source file ${sourceFile}") } } - fun destinationFileForSourceFile(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")): File { + fun assertSourceFileNotCopiedToOutputDir( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")) { + + assertFalse( + "Source file exists in output dir", + outputFileForSourceFile(sourceFile, sourceBaseDir, outputDir).exists()) + } + + fun assertSourceFileNotCompiledToOutputDir( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")) { + + assertTrue( + "Compiled files exists in output dir", + compiledClassFilesForSourceFile(sourceFile, sourceBaseDir, outputDir).isEmpty()) + } + + fun assertSourceFileIsOnlyCopiedToOutputDir( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")) { + + assertSourceFileCopiedToOutputDir(sourceFile, sourceBaseDir, outputDir) + assertSourceFileNotCompiledToOutputDir(sourceFile, sourceBaseDir, outputDir) + } + + fun assertSourceFileIsOnlyCompiledToOutputDir( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")) { + + assertSourceFileNotCopiedToOutputDir(sourceFile, sourceBaseDir, outputDir) + assertSourceFileCompiledToOutputDir(sourceFile, sourceBaseDir, outputDir) + } + + fun outputFileForSourceFile( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")): File { + val relativeSourceFile = sourceFile.relativeTo(sourceBaseDir) - val expectedDestinationFile = destinationDir.resolve(relativeSourceFile) - return expectedDestinationFile + return outputDir.resolve(relativeSourceFile) } - fun compiledClassFilesForSourceFile(sourceFile: File, sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), destinationDir: File = testProjectDir.resolve("build/classes/main")): List { + fun compiledClassFilesForSourceFile( + sourceFile: File, + sourceBaseDir: File = testProjectDir.resolve("src/main/clojure"), + outputDir: File = testProjectDir.resolve("build/classes/main")): List { + val relativeSourceFile = sourceFile.relativeTo(sourceBaseDir) val relativeSourceFileDir = relativeSourceFile.parentFile val sourceFileName = sourceFile.nameWithoutExtension - return destinationDir.resolve(relativeSourceFileDir).listFiles { file -> file.nameWithoutExtension.startsWith(sourceFileName) }?.toList() ?: Collections.emptyList() + return outputDir + .resolve(relativeSourceFileDir) + .listFiles { file -> + file.nameWithoutExtension.startsWith(sourceFileName) && file.extension == "class"} + ?.toList() + ?: Collections.emptyList() } private fun initTempDir(): TemporaryFolder { diff --git a/src/test/kotlin/cursive/MixedClojureJavaTest.kt b/src/test/kotlin/cursive/MixedClojureJavaTest.kt new file mode 100644 index 0000000..34ddf08 --- /dev/null +++ b/src/test/kotlin/cursive/MixedClojureJavaTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class MixedClojureJavaTest : IntegrationTestBase() { + @Test + fun compilationWithJavaCodeDependingOnClojureCode() { + // given + val cljSourceDir = testProjectDir.resolve("src/cljSS/clojure") + val cljExampleNsFile = cljSourceDir.resolve("cljSS/Example.clj") + val cljOutputDir = testProjectDir.resolve("build/classes/cljSS") + + val javaOutputDir = testProjectDir.resolve("build/classes/javaSS") + val javaClassFile = javaOutputDir.resolve("javaSS/Test.class") + + // when + val result = projectBuildRunner().withArguments("compileJavaSSJava").build() + + // then + assertThat(result.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertSourceFileIsOnlyCompiledToOutputDir(cljExampleNsFile, cljSourceDir, cljOutputDir) + assertThat(javaClassFile.exists()).isTrue() + } +} diff --git a/src/test/kotlin/cursive/MixedJavaClojureTest.kt b/src/test/kotlin/cursive/MixedJavaClojureTest.kt new file mode 100644 index 0000000..4b053fa --- /dev/null +++ b/src/test/kotlin/cursive/MixedJavaClojureTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class MixedJavaClojureTest : IntegrationTestBase() { + @Test + fun compilationWithClojureCodeDependingOnJavaCode() { + // given + val javaOutputDir = testProjectDir.resolve("build/classes/javaSS") + val javaClassFile = javaOutputDir.resolve("javaSS/Example.class") + + val cljSourceDir = testProjectDir.resolve("src/cljSS/clojure") + val cljCoreNsFile = cljSourceDir.resolve("cljSS/core.clj") + val cljOutputDir = testProjectDir.resolve("build/classes/cljSS") + + // when + val result = projectBuildRunner().withArguments("compileCljSSClojure").build() + + // then + assertThat(result.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertThat(javaClassFile.exists()).isTrue() + assertSourceFileIsOnlyCompiledToOutputDir(cljCoreNsFile, cljSourceDir, cljOutputDir) + } +} \ No newline at end of file diff --git a/src/test/kotlin/cursive/MultipleSourceSetsTest.kt b/src/test/kotlin/cursive/MultipleSourceSetsTest.kt new file mode 100644 index 0000000..89aece9 --- /dev/null +++ b/src/test/kotlin/cursive/MultipleSourceSetsTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class MultipleSourceSetsTest : IntegrationTestBase() { + @Test + fun compilationWithMultipleSourceSets() { + // given + val ss1SourceDir = testProjectDir.resolve("src/ss1/clojure") + val ss1CoreNsFile = ss1SourceDir.resolve("ss1/core.clj") + val ss1OutputDir = testProjectDir.resolve("build/classes/ss1") + + val ss2SourceDir = testProjectDir.resolve("src/ss2/clojure") + val ss2CoreNsFile = ss2SourceDir.resolve("ss2/core.clj") + val ss2OutputDir = testProjectDir.resolve("build/classes/ss2") + + // when + val result = projectBuildRunner().withArguments("check").build() + + // then + assertThat(result.task(":compileSs1Clojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":compileSs2Clojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertSourceFileIsOnlyCopiedToOutputDir(ss1CoreNsFile, ss1SourceDir, ss1OutputDir) + assertSourceFileIsOnlyCompiledToOutputDir(ss2CoreNsFile, ss2SourceDir, ss2OutputDir) + + assertThat(result.output).contains("Test1 SourceSet1") + assertThat(result.output).contains("Test2 SourceSet2 SourceSet1") + } +} diff --git a/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt b/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt index 1eaec4b..3498bde 100644 --- a/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt +++ b/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt @@ -28,9 +28,8 @@ class TestFailureFailsBuildTest : IntegrationTestBase() { val result = projectBuildRunner().withArguments("check").buildAndFail() // then - println(result.output) KotlinAssertions.assertThat(result.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) KotlinAssertions.assertThat(result.task(":compileTestClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) KotlinAssertions.assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.FAILED) } -} \ No newline at end of file +} From 683b70945fd18d2662e6479fc466784da3914150 Mon Sep 17 00:00:00 2001 From: Piotr Bzdyl Date: Thu, 12 Jan 2017 09:01:07 +0100 Subject: [PATCH 4/5] Added integration test for single source set with Java and Clojure sources. Renamed test cases with backticks for more readable test output. --- .../build.gradle | 31 +++++++++++ .../src/main/clojure/clj_example/core.clj | 19 +++++++ .../src/main/java/javaExample/Example.java | 22 ++++++++ .../kotlin/cursive/BasicClojureProjectTest.kt | 4 +- .../cursive/IncrementalCompilationTest.kt | 8 +-- .../JavaAndClojureInOneSourceSetTest.kt | 53 +++++++++++++++++++ .../kotlin/cursive/MixedClojureJavaTest.kt | 2 +- .../kotlin/cursive/MixedJavaClojureTest.kt | 2 +- .../kotlin/cursive/MultipleSourceSetsTest.kt | 2 +- .../cursive/TestFailureFailsBuildTest.kt | 2 +- 10 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/build.gradle create mode 100644 src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/clojure/clj_example/core.clj create mode 100644 src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/java/javaExample/Example.java create mode 100644 src/test/kotlin/cursive/JavaAndClojureInOneSourceSetTest.kt diff --git a/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/build.gradle b/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/build.gradle new file mode 100644 index 0000000..e013c7e --- /dev/null +++ b/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/build.gradle @@ -0,0 +1,31 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.cursive-ide.clojure' +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'org.clojure:clojure:1.8.0' +} + +compileClojure { + aotCompile = true +} diff --git a/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/clojure/clj_example/core.clj b/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/clojure/clj_example/core.clj new file mode 100644 index 0000000..0db8f9a --- /dev/null +++ b/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/clojure/clj_example/core.clj @@ -0,0 +1,19 @@ +; +; Copyright 2017 Colin Fleming +; +; Licensed under the Apache License, Version 2.0 (the "License") +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. +; + +(ns clj-example.core) + +(defn test []) diff --git a/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/java/javaExample/Example.java b/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/java/javaExample/Example.java new file mode 100644 index 0000000..969f60a --- /dev/null +++ b/src/test/int-tests-projects/cursive/JavaAndClojureInOneSourceSetTest/src/main/java/javaExample/Example.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javaExample; + +public class Example { + public void test() { + } +} \ No newline at end of file diff --git a/src/test/kotlin/cursive/BasicClojureProjectTest.kt b/src/test/kotlin/cursive/BasicClojureProjectTest.kt index 5007703..a22f301 100644 --- a/src/test/kotlin/cursive/BasicClojureProjectTest.kt +++ b/src/test/kotlin/cursive/BasicClojureProjectTest.kt @@ -24,7 +24,7 @@ class BasicClojureProjectTest : IntegrationTestBase() { val coreNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/core.clj") @Test - fun basicClojureProjectBuildNoAot() { + fun `Build without AOT only copies clj files to output directory`() { // when val result = projectBuildRunner().withArguments("check").build() @@ -39,7 +39,7 @@ class BasicClojureProjectTest : IntegrationTestBase() { } @Test - fun basicClojureProjectBuildWithAot() { + fun `Build with AOT compiles to class files without copying clj files to output directory`() { // given projectGradleFile().appendText("compileClojure.aotCompile = true") diff --git a/src/test/kotlin/cursive/IncrementalCompilationTest.kt b/src/test/kotlin/cursive/IncrementalCompilationTest.kt index 192a4d0..de05075 100644 --- a/src/test/kotlin/cursive/IncrementalCompilationTest.kt +++ b/src/test/kotlin/cursive/IncrementalCompilationTest.kt @@ -24,7 +24,7 @@ class IncrementalCompilationTest : IntegrationTestBase() { val coreNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/core.clj") @Test - fun incrementalCompileTaskUpToDateWhenNoChangesNoAot() { + fun `Incremental compile task without AOT is up-to-date when no input changes`() { // when val firstRunResult = projectBuildRunner().withArguments("check").build() @@ -41,7 +41,7 @@ class IncrementalCompilationTest : IntegrationTestBase() { } @Test - fun incrementalCompileTaskUpToDateWhenNoChangesWithAot() { + fun `Incremental compile task with AOT is up-to-date when no input changes`() { // given projectGradleFile().appendText("compileClojure.aotCompile = true") @@ -61,7 +61,7 @@ class IncrementalCompilationTest : IntegrationTestBase() { } @Test - fun incrementalCompileTaskExecutedWhenSourceFileChangedNoAot() { + fun `Incremental compile task without AOT processes outdated source files when input changes`() { // given val utilsNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/utils.clj") @@ -88,7 +88,7 @@ class IncrementalCompilationTest : IntegrationTestBase() { } @Test - fun incrementalCompileTaskExecutedWhenNewCljFileAddedWithAot() { + fun `Incremental compile task with AOT processes outdated source files when input changes`() { // given projectGradleFile().appendText("compileClojure.aotCompile = true") val utilsNsSourceFile = testProjectDir.resolve("src/main/clojure/basic_project/utils.clj") diff --git a/src/test/kotlin/cursive/JavaAndClojureInOneSourceSetTest.kt b/src/test/kotlin/cursive/JavaAndClojureInOneSourceSetTest.kt new file mode 100644 index 0000000..0e923fe --- /dev/null +++ b/src/test/kotlin/cursive/JavaAndClojureInOneSourceSetTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursive + +import org.assertj.core.api.KotlinAssertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test + +class JavaAndClojureInOneSourceSetTest : IntegrationTestBase() { + val coreNsSourceFile = testProjectDir.resolve("src/main/clojure/clj_example/core.clj") + val utilsNsSourceFile = testProjectDir.resolve("src/main/clojure/clj_example/utils.clj") + val javaClassFile = testProjectDir.resolve("build/classes/main/javaExample/Example.class") + + @Test + fun `Incremental compile task does not remove Java class files`() { + // when + val firstRunResult = projectBuildRunner().withArguments("build").build() + + // then + assertThat(firstRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(firstRunResult.task(":compileJava").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertSourceFileIsOnlyCompiledToOutputDir(coreNsSourceFile) + assertThat(javaClassFile.exists()).isTrue() + + coreNsSourceFile.delete() + utilsNsSourceFile.writeText("""(ns clj-example.utils) (defn ping [] "pong")""") + + // when + val secondRunResult = projectBuildRunner().withArguments("build").build() + + // then + assertThat(secondRunResult.task(":compileClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(secondRunResult.task(":compileJava").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) + assertSourceFileNotCopiedToOutputDir(coreNsSourceFile) + assertSourceFileNotCompiledToOutputDir(coreNsSourceFile) + assertSourceFileIsOnlyCompiledToOutputDir(utilsNsSourceFile) + assertThat(javaClassFile.exists()).isTrue() + } +} diff --git a/src/test/kotlin/cursive/MixedClojureJavaTest.kt b/src/test/kotlin/cursive/MixedClojureJavaTest.kt index 34ddf08..e87c929 100644 --- a/src/test/kotlin/cursive/MixedClojureJavaTest.kt +++ b/src/test/kotlin/cursive/MixedClojureJavaTest.kt @@ -22,7 +22,7 @@ import org.junit.Test class MixedClojureJavaTest : IntegrationTestBase() { @Test - fun compilationWithJavaCodeDependingOnClojureCode() { + fun `Compilation with Java code depending on Clojure code`() { // given val cljSourceDir = testProjectDir.resolve("src/cljSS/clojure") val cljExampleNsFile = cljSourceDir.resolve("cljSS/Example.clj") diff --git a/src/test/kotlin/cursive/MixedJavaClojureTest.kt b/src/test/kotlin/cursive/MixedJavaClojureTest.kt index 4b053fa..a04f83d 100644 --- a/src/test/kotlin/cursive/MixedJavaClojureTest.kt +++ b/src/test/kotlin/cursive/MixedJavaClojureTest.kt @@ -22,7 +22,7 @@ import org.junit.Test class MixedJavaClojureTest : IntegrationTestBase() { @Test - fun compilationWithClojureCodeDependingOnJavaCode() { + fun `Compilation with Clojure code depending on Java code`() { // given val javaOutputDir = testProjectDir.resolve("build/classes/javaSS") val javaClassFile = javaOutputDir.resolve("javaSS/Example.class") diff --git a/src/test/kotlin/cursive/MultipleSourceSetsTest.kt b/src/test/kotlin/cursive/MultipleSourceSetsTest.kt index 89aece9..6436e1e 100644 --- a/src/test/kotlin/cursive/MultipleSourceSetsTest.kt +++ b/src/test/kotlin/cursive/MultipleSourceSetsTest.kt @@ -22,7 +22,7 @@ import org.junit.Test class MultipleSourceSetsTest : IntegrationTestBase() { @Test - fun compilationWithMultipleSourceSets() { + fun `Compilation with multiple source sets`() { // given val ss1SourceDir = testProjectDir.resolve("src/ss1/clojure") val ss1CoreNsFile = ss1SourceDir.resolve("ss1/core.clj") diff --git a/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt b/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt index 3498bde..b074d0a 100644 --- a/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt +++ b/src/test/kotlin/cursive/TestFailureFailsBuildTest.kt @@ -23,7 +23,7 @@ import org.junit.Test class TestFailureFailsBuildTest : IntegrationTestBase() { @Test - fun testFailureFailsBuild() { + fun `testClojure task failure fails build`() { // when val result = projectBuildRunner().withArguments("check").buildAndFail() From e29a1cf1341e687398a57f54eb64a2a7cc25cde7 Mon Sep 17 00:00:00 2001 From: Piotr Bzdyl Date: Fri, 13 Jan 2017 09:34:16 +0100 Subject: [PATCH 5/5] Changed mechanism for detecting obsolete class files. Relying only on the source namespace name doesn't work for class files generated e.g. from defprotocol. Instead all files are compiled to a temporary directory and then copied to the destination directory. During next compilation first all files found in temporary destination directory are deleted from the proper destination directory. --- src/main/kotlin/cursive/ClojurePlugin.kt | 75 +++++++++++-------- .../src/main/clojure/basic_project/core.clj | 2 + .../src/main/clojure/basic_project/core.clj | 2 + .../src/cljSS/clojure/cljSS/core.clj | 5 +- .../javaSS/{Example.java => Example1.java} | 2 +- .../src/javaSS/java/javaSS/Example2.java | 22 ++++++ .../kotlin/cursive/BasicClojureProjectTest.kt | 2 + .../cursive/IncrementalCompilationTest.kt | 2 + .../kotlin/cursive/MixedJavaClojureTest.kt | 70 ++++++++++++++--- 9 files changed, 139 insertions(+), 43 deletions(-) rename src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/{Example.java => Example1.java} (96%) create mode 100644 src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example2.java diff --git a/src/main/kotlin/cursive/ClojurePlugin.kt b/src/main/kotlin/cursive/ClojurePlugin.kt index b419f13..87b9c36 100644 --- a/src/main/kotlin/cursive/ClojurePlugin.kt +++ b/src/main/kotlin/cursive/ClojurePlugin.kt @@ -34,13 +34,13 @@ import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.incremental.IncrementalTaskInputs -import org.gradle.api.tasks.incremental.InputFileDetails import org.gradle.process.JavaForkOptions import org.gradle.process.internal.DefaultJavaForkOptions import org.gradle.process.internal.ExecException import org.gradle.process.internal.JavaExecHandleBuilder import org.gradle.util.ConfigureUtil import java.io.File +import java.io.IOException import java.io.OutputStream import java.util.* import java.util.regex.Pattern @@ -49,6 +49,7 @@ import javax.inject.Inject /** * @author Colin Fleming */ +@Suppress("unused") class ClojurePlugin : Plugin { val logger = Logging.getLogger(this.javaClass) @@ -163,6 +164,12 @@ open class ClojureSourceSetImpl(displayName: String?, resolver: FileResolver?) : class ReflectionWarnings(var enabled: Boolean, var projectOnly: Boolean, var asErrors: Boolean) +object FileCopyErrorHandler : (File, IOException) -> OnErrorAction { + override fun invoke(file: File, exception: IOException): OnErrorAction { + throw ExecException("Could not copy ${file} to output directory", exception) + } +} + open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : AbstractCompile(), JavaForkOptions by DefaultJavaForkOptions(fileResolver) { @@ -175,30 +182,38 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : var elideMeta: Collection = emptyList() var directLinking: Boolean = false + @Suppress("unused") var namespaces: Collection = emptyList() + @Suppress("unused") fun reflectionWarnings(configureClosure: Closure?): ReflectionWarnings { ConfigureUtil.configure(configureClosure, reflectionWarnings) return reflectionWarnings } - override fun compile() { - throw UnsupportedOperationException() - } - + @Suppress("UNUSED_PARAMETER") @TaskAction fun compile(inputs: IncrementalTaskInputs) { + compile() + } + + override fun compile() { logger.info("Starting ClojureCompile task") - destinationDir.mkdirs() + val tmpDestinationDir = temporaryDir.resolve("classes") + removeObsoleteClassFiles(destinationDir, tmpDestinationDir) - inputs.outOfDate { removeOutputFilesDerivedFromInputFile(it, destinationDir) } - inputs.removed { removeOutputFilesDerivedFromInputFile(it, destinationDir) } + if (!tmpDestinationDir.deleteRecursively()) { + throw ExecException("Could not delete ${tmpDestinationDir}") + } + tmpDestinationDir.mkdirs() + destinationDir.mkdirs() if (copySourceToOutput ?: !aotCompile) { project.copy { - it.from(getSource()).into(destinationDir) + it.from(getSource()).into(tmpDestinationDir) } + copyToDestination(tmpDestinationDir) return } @@ -215,7 +230,7 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : logger.info("Compiling " + namespaces.joinToString(", ")) val script = listOf("(try", - " (binding [*compile-path* \"${destinationDir.canonicalPath}\"", + " (binding [*compile-path* \"${tmpDestinationDir.canonicalPath}\"", " *warn-on-reflection* ${reflectionWarnings.enabled}", " *compiler-options* {:disable-locals-clearing $disableLocalsClearing", " :elide-meta [${elideMeta.map { ":$it" }.joinToString(" ")}]", @@ -263,6 +278,8 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : executeScript(script, stdout, stderr) + copyToDestination(tmpDestinationDir) + if (libraryReflectionWarningCount > 0) { System.err.println("$libraryReflectionWarningCount reflection warnings from dependencies") } @@ -272,28 +289,22 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : } } - private fun removeOutputFilesDerivedFromInputFile(inputFileDetails: InputFileDetails, destinationDir: File) { - val sourceAbsoluteFile = inputFileDetails.file - if (isClojureSource(sourceAbsoluteFile)) { - logger.debug("Removing class files for {}", inputFileDetails.file) - val sourceCanonicalFileName = sourceAbsoluteFile.canonicalPath - val sourceFileRoot = getSourceRootsFiles() - .find { sourceCanonicalFileName.startsWith(it.canonicalPath) } - ?: throw IllegalStateException("No source root found for source file ${sourceAbsoluteFile}") - val sourceRelativeFile = sourceAbsoluteFile.relativeTo(sourceFileRoot) - val sourceRelativeDirectory = sourceRelativeFile.parentFile - val sourceFileName = sourceAbsoluteFile.nameWithoutExtension - destinationDir.resolve(sourceRelativeDirectory) - .listFiles { file -> file.name.startsWith(sourceFileName) } - ?.forEach { - logger.debug("Deleting derived file {}", it) - it.delete() - } - } + private fun copyToDestination(tmpDestinationDir: File) { + tmpDestinationDir.copyRecursively(target = destinationDir, overwrite = true, onError = FileCopyErrorHandler) } - private fun isClojureSource(file: File): Boolean { - return CLJ_EXTENSION_REGEX.matches(file.extension) && getSourceRoots().any { file.canonicalPath.startsWith(it) } + private fun removeObsoleteClassFiles(destinationDir: File, tmpDestinationDir: File) { + tmpDestinationDir.walkBottomUp().forEach { + val relativeFile = it.relativeTo(tmpDestinationDir) + val fileInDestination = destinationDir.resolve(relativeFile) + if (fileInDestination.exists()) { + if (fileInDestination.delete()) { + logger.debug("Deleted obsolete output file {}", fileInDestination) + } else { + logger.warn("Couldn't delete obsolete output file {}", fileInDestination) + } + } + } } private fun executeScript(script: String, stdout: OutputStream, stderr: OutputStream) { @@ -385,7 +396,6 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : '\\' to "_BSLASH_", '?' to "_QMARK_") - val CLJ_EXTENSION_REGEX = "cljc?".toRegex() val DEMUNGE_MAP = CHAR_MAP.map { it.value to it.key }.toMap() val DEMUNGE_PATTERN = Pattern.compile(DEMUNGE_MAP.keys .sortedByDescending { it.length } @@ -394,6 +404,7 @@ open class ClojureCompile @Inject constructor(val fileResolver: FileResolver) : val REFLECTION_WARNING_PREFIX = "Reflection warning, " + @Suppress("unused") fun munge(name: String): String { val sb = StringBuilder() for (c in name) { @@ -431,7 +442,9 @@ open class ClojureTestRunner @Inject constructor(val fileResolver: FileResolver) ConventionTask(), JavaForkOptions by DefaultJavaForkOptions(fileResolver) { + @Suppress("unused") var classpath: FileCollection = SimpleFileCollection() + @Suppress("unused") var namespaces: Collection = emptyList() var junitReport: File? = null diff --git a/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj index f8b3b7c..831e480 100644 --- a/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj +++ b/src/test/int-tests-projects/cursive/BasicClojureProjectTest/src/main/clojure/basic_project/core.clj @@ -16,6 +16,8 @@ (ns basic-project.core) +(defprotocol ITest) + (defn hello [name] (println "Generating message for" name) (str "Hello " name)) diff --git a/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj b/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj index a661c3c..d0ac77d 100644 --- a/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj +++ b/src/test/int-tests-projects/cursive/IncrementalCompilationTest/src/main/clojure/basic_project/core.clj @@ -16,5 +16,7 @@ (ns basic-project.core) +(defprotocol ITest) + (defn hello [name] (str "Hello " name)) diff --git a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj index 58c3b87..c4edd31 100644 --- a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj +++ b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/cljSS/clojure/cljSS/core.clj @@ -15,7 +15,8 @@ ; (ns cljSS.core - (:import javaSS.Example)) + (:import (javaSS Example1 Example2))) (defn test [] - (.test (Example.))) + (.test (Example1.)) + (.test (Example2.))) diff --git a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example1.java similarity index 96% rename from src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java rename to src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example1.java index edde1c6..a4eb91f 100644 --- a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example.java +++ b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example1.java @@ -16,7 +16,7 @@ package javaSS; -public class Example { +public class Example1 { public void test() { } } \ No newline at end of file diff --git a/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example2.java b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example2.java new file mode 100644 index 0000000..4141d5e --- /dev/null +++ b/src/test/int-tests-projects/cursive/MixedJavaClojureTest/src/javaSS/java/javaSS/Example2.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017 Colin Fleming + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javaSS; + +public class Example2 { + public void test() { + } +} \ No newline at end of file diff --git a/src/test/kotlin/cursive/BasicClojureProjectTest.kt b/src/test/kotlin/cursive/BasicClojureProjectTest.kt index a22f301..a8991af 100644 --- a/src/test/kotlin/cursive/BasicClojureProjectTest.kt +++ b/src/test/kotlin/cursive/BasicClojureProjectTest.kt @@ -18,6 +18,7 @@ package cursive import org.assertj.core.api.KotlinAssertions.assertThat import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert.assertTrue import org.junit.Test class BasicClojureProjectTest : IntegrationTestBase() { @@ -52,6 +53,7 @@ class BasicClojureProjectTest : IntegrationTestBase() { assertThat(result.task(":testClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) assertSourceFileIsOnlyCompiledToOutputDir(coreNsSourceFile) + assertTrue("Protocol class file exists", testProjectDir.resolve("build/classes/main/basic_project/core/ITest.class").exists()) assertThat(result.output).contains("Generating message for World") } diff --git a/src/test/kotlin/cursive/IncrementalCompilationTest.kt b/src/test/kotlin/cursive/IncrementalCompilationTest.kt index de05075..9fe3f46 100644 --- a/src/test/kotlin/cursive/IncrementalCompilationTest.kt +++ b/src/test/kotlin/cursive/IncrementalCompilationTest.kt @@ -18,6 +18,7 @@ package cursive import org.assertj.core.api.KotlinAssertions.assertThat import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert.assertFalse import org.junit.Test class IncrementalCompilationTest : IntegrationTestBase() { @@ -113,5 +114,6 @@ class IncrementalCompilationTest : IntegrationTestBase() { assertSourceFileNotCopiedToOutputDir(coreNsSourceFile) assertSourceFileNotCompiledToOutputDir(coreNsSourceFile) assertSourceFileIsOnlyCompiledToOutputDir(utilsNsSourceFile) + assertFalse("Protocol class file doesn't exists", testProjectDir.resolve("build/classes/main/basic_project/core/ITest.class").exists()) } } diff --git a/src/test/kotlin/cursive/MixedJavaClojureTest.kt b/src/test/kotlin/cursive/MixedJavaClojureTest.kt index a04f83d..0181ed5 100644 --- a/src/test/kotlin/cursive/MixedJavaClojureTest.kt +++ b/src/test/kotlin/cursive/MixedJavaClojureTest.kt @@ -21,16 +21,17 @@ import org.gradle.testkit.runner.TaskOutcome import org.junit.Test class MixedJavaClojureTest : IntegrationTestBase() { - @Test - fun `Compilation with Clojure code depending on Java code`() { - // given - val javaOutputDir = testProjectDir.resolve("build/classes/javaSS") - val javaClassFile = javaOutputDir.resolve("javaSS/Example.class") + val javaOutputDir = testProjectDir.resolve("build/classes/javaSS") + val javaExample1SourceFile = testProjectDir.resolve("src/javaSS/java/javaSS/Example1.java") + val javaExample1ClassFile = javaOutputDir.resolve("javaSS/Example1.class") + val javaExample2ClassFile = javaOutputDir.resolve("javaSS/Example2.class") - val cljSourceDir = testProjectDir.resolve("src/cljSS/clojure") - val cljCoreNsFile = cljSourceDir.resolve("cljSS/core.clj") - val cljOutputDir = testProjectDir.resolve("build/classes/cljSS") + val cljSourceDir = testProjectDir.resolve("src/cljSS/clojure") + val cljCoreNsFile = cljSourceDir.resolve("cljSS/core.clj") + val cljOutputDir = testProjectDir.resolve("build/classes/cljSS") + @Test + fun `Compilation with Clojure code depending on Java code`() { // when val result = projectBuildRunner().withArguments("compileCljSSClojure").build() @@ -38,7 +39,58 @@ class MixedJavaClojureTest : IntegrationTestBase() { assertThat(result.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.SUCCESS) assertThat(result.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) - assertThat(javaClassFile.exists()).isTrue() + assertThat(javaExample1ClassFile.exists()).isTrue() + assertThat(javaExample2ClassFile.exists()).isTrue() + assertSourceFileIsOnlyCompiledToOutputDir(cljCoreNsFile, cljSourceDir, cljOutputDir) + } + + @Test + fun `Incremental compilation with Clojure code depending on Java code when Java source unchanged`() { + // when + val firstResult = projectBuildRunner().withArguments("compileCljSSClojure").build() + + // then + assertThat(firstResult.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(firstResult.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertThat(javaExample1ClassFile.exists()).isTrue() + assertThat(javaExample2ClassFile.exists()).isTrue() assertSourceFileIsOnlyCompiledToOutputDir(cljCoreNsFile, cljSourceDir, cljOutputDir) + + // when + val secondResult = projectBuildRunner().withArguments("compileCljSSClojure").build() + + // then + println(secondResult.output) + assertThat(secondResult.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) + assertThat(secondResult.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.UP_TO_DATE) + + assertThat(javaExample1ClassFile.exists()).isTrue() + assertThat(javaExample2ClassFile.exists()).isTrue() + assertSourceFileIsOnlyCompiledToOutputDir(cljCoreNsFile, cljSourceDir, cljOutputDir) + } + + @Test + fun `Incremental compilation with Clojure code depending on Java code when Java source changes`() { + // when + val firstResult = projectBuildRunner().withArguments("compileCljSSClojure").build() + + // then + assertThat(firstResult.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(firstResult.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertThat(javaExample1ClassFile.exists()).isTrue() + assertThat(javaExample2ClassFile.exists()).isTrue() + assertSourceFileIsOnlyCompiledToOutputDir(cljCoreNsFile, cljSourceDir, cljOutputDir) + + // when + javaExample1SourceFile.delete() + val secondResult = projectBuildRunner().withArguments("compileCljSSClojure").buildAndFail() + + // then + assertThat(secondResult.task(":compileJavaSSJava").outcome).isEqualTo(TaskOutcome.SUCCESS) + assertThat(secondResult.task(":compileCljSSClojure").outcome).isEqualTo(TaskOutcome.FAILED) + + assertThat(secondResult.output).contains("java.lang.ClassNotFoundException: javaSS.Example1") } } \ No newline at end of file