From commits-return-9846-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Fri Nov 15 11:00:17 2019 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [207.244.88.153]) by mx-eu-01.ponee.io (Postfix) with SMTP id 0FFE5180658 for ; Fri, 15 Nov 2019 12:00:16 +0100 (CET) Received: (qmail 98080 invoked by uid 500); 15 Nov 2019 11:00:16 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 98070 invoked by uid 99); 15 Nov 2019 11:00:16 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 15 Nov 2019 11:00:16 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 4042B8B688; Fri, 15 Nov 2019 11:00:16 +0000 (UTC) Date: Fri, 15 Nov 2019 11:00:16 +0000 To: "commits@groovy.apache.org" Subject: [groovy] branch master updated: GROOVY-8775, GROOVY-9197: Ant: separate JVM and compilation classpaths MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <157381561601.16852.5743192879297112366@gitbox.apache.org> From: sunlan@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: groovy X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: b2842cbce83546fdc73d351351cd114c5db23393 X-Git-Newrev: c6e50bab27dc705116b93daccd62d53160c99938 X-Git-Rev: c6e50bab27dc705116b93daccd62d53160c99938 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. sunlan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy.git The following commit(s) were added to refs/heads/master by this push: new c6e50ba GROOVY-8775, GROOVY-9197: Ant: separate JVM and compilation classpaths c6e50ba is described below commit c6e50bab27dc705116b93daccd62d53160c99938 Author: Eric Milles AuthorDate: Mon Nov 11 15:02:53 2019 -0600 GROOVY-8775, GROOVY-9197: Ant: separate JVM and compilation classpaths --- src/test/groovy/bugs/Groovy9197.groovy | 61 +++++++ .../main/java/org/codehaus/groovy/ant/Groovyc.java | 190 ++++++++++++--------- .../org/codehaus/groovy/ant/GroovycTest.xml | 20 +++ .../groovy/ant/MakesExternalReference.java | 39 +++++ .../org/codehaus/groovy/ant/commons-lang3-3.4.jar | Bin 0 -> 434678 bytes .../org/codehaus/groovy/ant/GroovycTest.java | 7 + 6 files changed, 236 insertions(+), 81 deletions(-) diff --git a/src/test/groovy/bugs/Groovy9197.groovy b/src/test/groovy/bugs/Groovy9197.groovy new file mode 100644 index 0000000..f752f35 --- /dev/null +++ b/src/test/groovy/bugs/Groovy9197.groovy @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.bugs + +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit +import org.junit.Test + +import static groovy.grape.Grape.resolve + +final class Groovy9197 { + + @Test + void testJointCompilationClasspathPropagation() { + def uris = resolve(autoDownload:true, classLoader:new GroovyClassLoader(null), + [groupId:'org.apache.commons', artifactId:'commons-lang3', version:'3.9']) + + def config = new CompilerConfiguration( + classpath: new File(uris[0]).path, + targetDirectory: File.createTempDir(), + jointCompilationOptions: [memStub: true] + ) + + def parentDir = File.createTempDir() + try { + def pojo = new File(parentDir, 'Pojo.java') + pojo.write ''' + import static org.apache.commons.lang3.StringUtils.isEmpty; + public class Pojo { + public static void main(String[] args) { + assert !isEmpty(" "); + assert isEmpty(""); + } + } + ''' + + def unit = new JavaAwareCompilationUnit(config) + unit.addSources(pojo) + unit.compile() + } finally { + parentDir.deleteDir() + config.targetDirectory.deleteDir() + } + } +} diff --git a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java index a72af08..ccc6efe 100644 --- a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java +++ b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java @@ -19,6 +19,7 @@ package org.codehaus.groovy.ant; import groovy.lang.GroovyClassLoader; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; import org.apache.groovy.io.StringBuilderWriter; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; @@ -31,6 +32,7 @@ import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.util.GlobPatternMapper; import org.apache.tools.ant.util.SourceFileScanner; +import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.SourceExtensionHandler; @@ -38,8 +40,8 @@ import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods; import org.codehaus.groovy.tools.ErrorReporter; import org.codehaus.groovy.tools.FileSystemCompiler; -import org.codehaus.groovy.tools.RootLoader; import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit; +import org.objectweb.asm.ClassVisitor; import picocli.CommandLine; import java.io.File; @@ -47,11 +49,13 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -67,17 +71,22 @@ import java.util.StringTokenizer; *
  * <?xml version="1.0"?>
  * <project name="MyGroovyBuild" default="compile">
- *   <property name="groovy.home" value="/Path/To/Groovy"/>
+ *   <property name="groovy.home" location="/Path/To/Groovy"/>
  *   <property name="groovy.version" value="X.Y.Z"/>
- *   <path id="groovy.classpath">
- *     <fileset dir="${groovy.home}/lib">
- *       <include name="groovy-*${groovy.version}.jar" />
- *     </fileset>
- *   </path>
- *   <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.classpath"/>
+ *
+ *   <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">
+ *     <classpath>
+ *       <fileset file="${groovy.home}/lib/groovy-${groovy.version}.jar"/>
+ *       <fileset file="${groovy.home}/lib/groovy-ant-${groovy.version}.jar"/>
+ *     </classpath>
+ *   </taskdef>
  *
  *   <target name="compile" description="compile groovy sources">
- *     <groovyc srcdir="src" listfiles="true" classpathref="groovy.classpath"/>
+ *     <groovyc srcdir="src" destdir="bin" fork="true" listfiles="true" includeantruntime="false"/>
+ *       <classpath>
+ *         <fileset dir="${groovy.home}/lib" includes="groovy-*${groovy.version}.jar" excludes="groovy-ant-${groovy.version}.jar"/>
+ *       </classpath>
+ *     </groovyc>
  *   </target>
  * </project>
  * 
@@ -85,13 +94,13 @@ import java.util.StringTokenizer; * This task can take the following arguments: *
    *
  • srcdir
  • - *
  • scriptExtension
  • - *
  • targetBytecode
  • *
  • destdir
  • *
  • sourcepath
  • *
  • sourcepathRef
  • *
  • classpath
  • *
  • classpathRef
  • + *
  • scriptExtension
  • + *
  • targetBytecode
  • *
  • listfiles
  • *
  • failonerror
  • *
  • proceed
  • @@ -123,39 +132,42 @@ import java.util.StringTokenizer; *
* Of these arguments, the srcdir and destdir are required. *

- *

When this task executes, it will recursively scan srcdir and destdir looking for Groovy source files - * to compile. This task makes its compile decision based on timestamp. + * When this task executes, it will recursively scan srcdir and destdir looking + * for Groovy source files to compile. This task makes its compile decision based + * on timestamp. *

* A more elaborate build file showing joint compilation: *

  * <?xml version="1.0"?>
  * <project name="MyJointBuild" default="compile">
- *   <property name="groovy.home" value="/Path/To/Groovy"/>
+ *   <property name="groovy.home" location="/Path/To/Groovy"/>
  *   <property name="groovy.version" value="X.Y.Z"/>
  *
- *   <path id="groovy.classpath">
+ *   <path id="classpath.main">
  *     <fileset dir="${groovy.home}/lib">
- *       <include name="groovy-*${groovy.version}.jar" />
+ *       <include name="groovy-*${groovy.version}.jar"/>
+ *       <exclude name="groovy-ant-${groovy.version}.jar"/>
  *     </fileset>
  *   </path>
  *
- *   <target name="clean" description="remove all built files">
- *     <delete dir="classes" />
- *   </target>
+ *   <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">
+ *     <classpath>
+ *       <fileset file="${groovy.home}/lib/groovy-${groovy.version}.jar"/>
+ *       <fileset file="${groovy.home}/lib/groovy-ant-${groovy.version}.jar"/>
+ *     </classpath>
+ *   </taskdef>
  *
- *   <target name="compile" depends="init" description="compile java and groovy sources">
- *     <mkdir dir="classes" />
- *     <groovyc destdir="classes" srcdir="src" listfiles="true" keepStubs="true" stubdir="stubs">
- *       <javac debug="on" deprecation="true"/>
- *       <classpath>
- *         <fileset dir="classes"/>
- *         <path refid="groovy.classpath"/>
- *       </classpath>
- *     </groovyc>
+ *   <target name="clean">
+ *     <delete dir="bin" failonerror="false"/>
  *   </target>
  *
- *   <target name="init">
- *     <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.classpath"/>
+ *   <target name="compile" depends="clean" description="compile java and groovy sources">
+ *     <mkdir dir="bin"/>
+ *
+ *     <groovyc srcdir="src" destdir="bin" stubdir="stubs" keepStubs="true"
+ *      fork="true" includeantruntime="false" classpathref="classpath.main">
+ *       <javac debug="true" source="1.8" target="1.8"/>
+ *     </groovyc>
  *   </target>
  * </project>
  * 
@@ -165,7 +177,7 @@ import java.util.StringTokenizer; * Can also be used from {@link groovy.ant.AntBuilder} to allow the build file to be scripted in Groovy. */ public class Groovyc extends MatchingTask { - private static final URL[] EMPTY_URL_ARRAY = new URL[0]; + private static final File[] EMPTY_FILE_ARRAY = new File[0]; private static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -176,20 +188,20 @@ public class Groovyc extends MatchingTask { private Path compileClasspath; private Path compileSourcepath; private String encoding; - private boolean stacktrace = false; - private boolean verbose = false; + private boolean stacktrace; + private boolean verbose; private boolean includeAntRuntime = true; - private boolean includeJavaRuntime = false; - private boolean fork = false; + private boolean includeJavaRuntime; + private boolean fork; private File forkJavaHome; - private String forkedExecutable = null; + private String forkedExecutable; private String memoryInitialSize; private String memoryMaximumSize; private String scriptExtension = "*.groovy"; - private String targetBytecode = null; + private String targetBytecode; protected boolean failOnError = true; - protected boolean listFiles = false; + protected boolean listFiles; protected File[] compileList = EMPTY_FILE_ARRAY; private String updatedProperty; @@ -214,12 +226,12 @@ public class Groovyc extends MatchingTask { /** * If true, generates metadata for reflection on method parameter names (jdk8+ only). Defaults to false. */ - private boolean parameters = false; + private boolean parameters; /** * If true, enable preview Java features (JEP 12) (jdk12+ only). Defaults to false. */ - private boolean previewFeatures = false; + private boolean previewFeatures; /** * Adds a path for source compilation. @@ -1071,13 +1083,6 @@ public class Groovyc extends MatchingTask { } private void doForkCommandLineList(List commandLineList, Path classpath, String separator) { - if (includeAntRuntime) { - classpath.addExisting(new Path(getProject()).concatSystemClasspath("last")); - } - if (includeJavaRuntime) { - classpath.addJavaRuntime(); - } - if (forkedExecutable != null && !forkedExecutable.isEmpty()) { commandLineList.add(forkedExecutable); } else { @@ -1089,38 +1094,61 @@ public class Groovyc extends MatchingTask { } commandLineList.add(javaHome + separator + "bin" + separator + "java"); } - commandLineList.add("-classpath"); - commandLineList.add(getClasspathRelative(classpath)); - String fileEncoding = System.getProperty("file.encoding"); - if (fileEncoding != null && !fileEncoding.isEmpty()) { - commandLineList.add("-Dfile.encoding=" + fileEncoding); + String[] bootstrapClasspath; + ClassLoader loader = getClass().getClassLoader(); + if (loader instanceof AntClassLoader) { + bootstrapClasspath = ((AntClassLoader) loader).getClasspath().split(File.pathSeparator); + } else { + Class[] bootstrapClasses = { + FileSystemCompilerFacade.class, + FileSystemCompiler.class, + ParseTreeVisitor.class, + ClassVisitor.class, + CommandLine.class, + }; + bootstrapClasspath = Arrays.stream(bootstrapClasses).map(Groovyc::getLocation) + .map(uri -> new File(uri).getAbsolutePath()).distinct().toArray(String[]::new); } - if (targetBytecode != null) { - commandLineList.add("-Dgroovy.target.bytecode=" + targetBytecode); + if (bootstrapClasspath.length > 0) { + commandLineList.add("-classpath"); + commandLineList.add(getClasspathRelative(bootstrapClasspath)); } + if (memoryInitialSize != null && !memoryInitialSize.isEmpty()) { commandLineList.add("-Xms" + memoryInitialSize); } if (memoryMaximumSize != null && !memoryMaximumSize.isEmpty()) { commandLineList.add("-Xmx" + memoryMaximumSize); } + if (targetBytecode != null) { + commandLineList.add("-Dgroovy.target.bytecode=" + targetBytecode); + } if (!"*.groovy".equals(getScriptExtension())) { String tmpExtension = getScriptExtension(); if (tmpExtension.startsWith("*.")) tmpExtension = tmpExtension.substring(1); commandLineList.add("-Dgroovy.default.scriptExtension=" + tmpExtension); } + commandLineList.add(FileSystemCompilerFacade.class.getName()); + commandLineList.add("--classpath"); + if (includeAntRuntime) { + classpath.addExisting(new Path(getProject()).concatSystemClasspath("last")); + } + if (includeJavaRuntime) { + classpath.addJavaRuntime(); + } + commandLineList.add(getClasspathRelative(classpath.list())); if (forceLookupUnnamedFiles) { commandLineList.add("--forceLookupUnnamedFiles"); } } - private String getClasspathRelative(Path classpath) { + private String getClasspathRelative(String[] classpath) { String baseDir = getProject().getBaseDir().getAbsolutePath(); StringBuilder sb = new StringBuilder(); - for (String next : classpath.list()) { + for (String next : classpath) { if (sb.length() > 0) { sb.append(File.pathSeparatorChar); } @@ -1133,6 +1161,14 @@ public class Groovyc extends MatchingTask { return sb.toString(); } + private static URI getLocation(Class clazz) { + try { + return clazz.getProtectionDomain().getCodeSource().getLocation().toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + /** * Add "groovyc" parameters to the commandLineList, based on the ant configuration. * @@ -1214,7 +1250,7 @@ public class Groovyc extends MatchingTask { } private String[] makeCommandLine(List commandLineList) { - log.verbose("Compilation arguments:\n" + DefaultGroovyMethods.join((Iterable) commandLineList, "\n")); + log.info("Compilation arguments:\n" + DefaultGroovyMethods.join((Iterable) commandLineList, "\n")); return commandLineList.toArray(EMPTY_STRING_ARRAY); } @@ -1302,10 +1338,9 @@ public class Groovyc extends MatchingTask { Path classpath = Optional.ofNullable(getClasspath()).orElse(new Path(getProject())); List jointOptions = extractJointOptions(classpath); - String separator = System.getProperty("file.separator"); List commandLineList = new ArrayList<>(); - if (fork) doForkCommandLineList(commandLineList, classpath, separator); + if (fork) doForkCommandLineList(commandLineList, classpath, File.separator); doNormalCommandLineList(commandLineList, jointOptions, classpath); addSourceFiles(commandLineList); @@ -1359,23 +1394,17 @@ public class Groovyc extends MatchingTask { } protected GroovyClassLoader buildClassLoaderFor() { + if (fork) { + throw new GroovyBugError("Cannot use Groovyc#buildClassLoaderFor() for forked compilation"); + } // GROOVY-5044 - if (!fork && !getIncludeantruntime()) { + if (!getIncludeantruntime()) { throw new IllegalArgumentException("The includeAntRuntime=false option is not compatible with fork=false"); } - ClassLoader parent = - AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public ClassLoader run() { - return getIncludeantruntime() - ? getClass().getClassLoader() - : new AntClassLoader(new RootLoader(EMPTY_URL_ARRAY, null), getProject(), getClasspath()); - } - }); - if (parent instanceof AntClassLoader) { - AntClassLoader antLoader = (AntClassLoader) parent; + ClassLoader loader = getClass().getClassLoader(); + if (loader instanceof AntClassLoader) { + AntClassLoader antLoader = (AntClassLoader) loader; String[] pathElm = antLoader.getClasspath().split(File.pathSeparator); List classpath = configuration.getClasspath(); /* @@ -1406,13 +1435,13 @@ public class Groovyc extends MatchingTask { } } - GroovyClassLoader loader = AccessController.doPrivileged( - (PrivilegedAction) () -> new GroovyClassLoader(parent, configuration)); + GroovyClassLoader groovyLoader = AccessController.doPrivileged( + (PrivilegedAction) () -> new GroovyClassLoader(loader, configuration)); if (!forceLookupUnnamedFiles) { // in normal case we don't need to do script lookups - loader.setResourceLoader(filename -> null); + groovyLoader.setResourceLoader(filename -> null); } - return loader; + return groovyLoader; } private Set getScriptExtensions() { @@ -1423,11 +1452,10 @@ public class Groovyc extends MatchingTask { if (scriptExtensions.isEmpty()) { scriptExtensions.add(getScriptExtension().substring(2)); // first extension will be the one set explicitly on - Path classpath = getClasspath() != null ? getClasspath() : new Path(getProject()); - final String[] pe = classpath.list(); + Path classpath = Optional.ofNullable(getClasspath()).orElse(new Path(getProject())); try (GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader())) { - for (String file : pe) { - loader.addClasspath(file); + for (String element : classpath.list()) { + loader.addClasspath(element); } scriptExtensions.addAll(SourceExtensionHandler.getRegisteredExtensions(loader)); } catch (IOException e) { diff --git a/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovycTest.xml b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovycTest.xml index d4fb8d2..73bb824 100644 --- a/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovycTest.xml +++ b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/GroovycTest.xml @@ -187,6 +187,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/MakesExternalReference.java b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/MakesExternalReference.java new file mode 100644 index 0000000..e5d2c73 --- /dev/null +++ b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/MakesExternalReference.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.codehaus.groovy.ant; + +import java.io.*; + +import static org.apache.commons.lang3.StringUtils.isEmpty; + +public class MakesExternalReference { + public static void main(String[] args) throws IOException { + FileOutputStream fout = new FileOutputStream( + new File("target/classes/groovy/test/org/codehaus/groovy/ant/MakesExternalReference_Result.txt")); + try { + assert !isEmpty(" "); + fout.write("OK.".getBytes()); + } finally { + try { + fout.close(); + } catch (IOException ignore) { + } + } + } +} diff --git a/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/commons-lang3-3.4.jar b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/commons-lang3-3.4.jar new file mode 100644 index 0000000..8ec91d4 Binary files /dev/null and b/subprojects/groovy-ant/src/test-resources/org/codehaus/groovy/ant/commons-lang3-3.4.jar differ diff --git a/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java b/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java index cb60c5d..cec5dd6 100644 --- a/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java +++ b/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java @@ -252,6 +252,13 @@ public class GroovycTest extends GroovyTestCase { ensureFails("noForkNoAntRuntime"); } + // GROOVY-9197 + public void testJointCompilationPropagatesClasspath() { + ensureNotPresent("MakesExternalReference"); + project.executeTarget("jointForkedCompilation_ExternalJarOnClasspath"); + ensureResultOK("MakesExternalReference"); + } + private void ensureExecutes(String target) { ensureNotPresent("GroovycTest1"); project.executeTarget(target);