1616package com .google .testing .compile ;
1717
1818import static com .google .common .base .Functions .toStringFunction ;
19+ import static com .google .common .collect .ImmutableList .toImmutableList ;
1920import static java .nio .charset .StandardCharsets .UTF_8 ;
2021import static javax .tools .ToolProvider .getSystemJavaCompiler ;
2122
2223import com .google .auto .value .AutoValue ;
23- import com .google .common .base .Joiner ;
24+ import com .google .common .annotations .VisibleForTesting ;
25+ import com .google .common .base .Splitter ;
2426import com .google .common .base .StandardSystemProperty ;
2527import com .google .common .collect .FluentIterable ;
2628import com .google .common .collect .ImmutableList ;
2729import com .google .common .collect .ImmutableSet ;
30+ import com .google .common .collect .Iterables ;
2831import com .google .testing .compile .Compilation .Status ;
32+ import java .io .File ;
33+ import java .io .IOException ;
34+ import java .io .UncheckedIOException ;
2935import java .net .URL ;
3036import java .net .URLClassLoader ;
3137import java .util .LinkedHashSet ;
3238import java .util .Locale ;
39+ import java .util .Optional ;
3340import java .util .Set ;
3441import javax .annotation .processing .Processor ;
3542import javax .tools .DiagnosticCollector ;
3643import javax .tools .JavaCompiler ;
3744import javax .tools .JavaCompiler .CompilationTask ;
3845import javax .tools .JavaFileObject ;
46+ import javax .tools .StandardLocation ;
3947
4048/** An object that can {@link #compile} Java source files. */
4149@ AutoValue
50+ // clashes with java.lang.Compiler (which is deprecated for removal in 9)
51+ @ SuppressWarnings ("JavaLangClash" )
4252public abstract class Compiler {
4353
4454 /** Returns the {@code javac} compiler. */
@@ -48,7 +58,8 @@ public static Compiler javac() {
4858
4959 /** Returns a {@link Compiler} that uses a given {@link JavaCompiler} instance. */
5060 public static Compiler compiler (JavaCompiler javaCompiler ) {
51- return new AutoValue_Compiler (javaCompiler , ImmutableList .of (), ImmutableList .of ());
61+ return new AutoValue_Compiler (
62+ javaCompiler , ImmutableList .of (), ImmutableList .of (), Optional .empty ());
5263 }
5364
5465 abstract JavaCompiler javaCompiler ();
@@ -59,6 +70,9 @@ public static Compiler compiler(JavaCompiler javaCompiler) {
5970 /** The options passed to the compiler. */
6071 public abstract ImmutableList <String > options ();
6172
73+ /** The compilation class path. If not present, the system class path is used. */
74+ public abstract Optional <ImmutableList <File >> classPath ();
75+
6276 /**
6377 * Uses annotation processors during compilation. These replace any previously specified.
6478 *
@@ -78,7 +92,7 @@ public final Compiler withProcessors(Processor... processors) {
7892 * @return a new instance with the same options and the given processors
7993 */
8094 public final Compiler withProcessors (Iterable <? extends Processor > processors ) {
81- return copy (ImmutableList .copyOf (processors ), options ());
95+ return copy (ImmutableList .copyOf (processors ), options (), classPath () );
8296 }
8397
8498 /**
@@ -96,21 +110,30 @@ public final Compiler withOptions(Object... options) {
96110 * @return a new instance with the same processors and the given options
97111 */
98112 public final Compiler withOptions (Iterable <?> options ) {
99- return copy (processors (), FluentIterable .from (options ).transform (toStringFunction ()).toList ());
113+ return copy (
114+ processors (),
115+ FluentIterable .from (options ).transform (toStringFunction ()).toList (),
116+ classPath ());
100117 }
101118
102119 /**
103- * Uses the classpath from the passed on classloader (and its parents) for the compilation
104- * instead of the system classpath.
120+ * Uses the classpath from the passed on classloader (and its parents) for the compilation instead
121+ * of the system classpath.
105122 *
106123 * @throws IllegalArgumentException if the given classloader had classpaths which we could not
107124 * determine or use for compilation.
125+ * @deprecated prefer {@link #withClasspath(Iterable)}. This method only supports {@link
126+ * URLClassLoader} and the default system classloader, and {@link File}s are usually a more
127+ * natural way to expression compilation classpaths than class loaders.
108128 */
129+ @ Deprecated
109130 public final Compiler withClasspathFrom (ClassLoader classloader ) {
110- String classpath = getClasspathFromClassloader (classloader );
111- ImmutableList <String > options =
112- ImmutableList .<String >builder ().add ("-classpath" ).add (classpath ).addAll (options ()).build ();
113- return copy (processors (), options );
131+ return copy (processors (), options (), Optional .of (getClasspathFromClassloader (classloader )));
132+ }
133+
134+ /** Uses the given classpath for the compilation instead of the system classpath. */
135+ public final Compiler withClasspath (Iterable <File > classPath ) {
136+ return copy (processors (), options (), Optional .of (ImmutableList .copyOf (classPath )));
114137 }
115138
116139 /**
@@ -132,6 +155,16 @@ public final Compilation compile(Iterable<? extends JavaFileObject> files) {
132155 InMemoryJavaFileManager fileManager =
133156 new InMemoryJavaFileManager (
134157 javaCompiler ().getStandardFileManager (diagnosticCollector , Locale .getDefault (), UTF_8 ));
158+ classPath ()
159+ .ifPresent (
160+ classPath -> {
161+ try {
162+ fileManager .setLocation (StandardLocation .CLASS_PATH , classPath );
163+ } catch (IOException e ) {
164+ // impossible by specification
165+ throw new UncheckedIOException (e );
166+ }
167+ });
135168 CompilationTask task =
136169 javaCompiler ()
137170 .getTask (
@@ -156,20 +189,38 @@ public final Compilation compile(Iterable<? extends JavaFileObject> files) {
156189 return compilation ;
157190 }
158191
192+ @ VisibleForTesting static final ClassLoader platformClassLoader = getPlatformClassLoader ();
193+
194+ private static ClassLoader getPlatformClassLoader () {
195+ try {
196+ // JDK >= 9
197+ return (ClassLoader ) ClassLoader .class .getMethod ("getPlatformClassLoader" ).invoke (null );
198+ } catch (ReflectiveOperationException e ) {
199+ // Java <= 8
200+ return null ;
201+ }
202+ }
203+
159204 /**
160205 * Returns the current classpaths of the given classloader including its parents.
161206 *
162207 * @throws IllegalArgumentException if the given classloader had classpaths which we could not
163208 * determine or use for compilation.
164209 */
165- private static String getClasspathFromClassloader (ClassLoader currentClassloader ) {
210+ private static ImmutableList < File > getClasspathFromClassloader (ClassLoader currentClassloader ) {
166211 ClassLoader systemClassLoader = ClassLoader .getSystemClassLoader ();
167212
168213 // Concatenate search paths from all classloaders in the hierarchy 'till the system classloader.
169214 Set <String > classpaths = new LinkedHashSet <>();
170215 while (true ) {
171216 if (currentClassloader == systemClassLoader ) {
172- classpaths .add (StandardSystemProperty .JAVA_CLASS_PATH .value ());
217+ Iterables .addAll (
218+ classpaths ,
219+ Splitter .on (StandardSystemProperty .PATH_SEPARATOR .value ())
220+ .split (StandardSystemProperty .JAVA_CLASS_PATH .value ()));
221+ break ;
222+ }
223+ if (currentClassloader == platformClassLoader ) {
173224 break ;
174225 }
175226 if (currentClassloader instanceof URLClassLoader ) {
@@ -185,16 +236,21 @@ private static String getClasspathFromClassloader(ClassLoader currentClassloader
185236 }
186237 } else {
187238 throw new IllegalArgumentException (
188- "Classpath for compilation could not be extracted "
189- + "since given classloader is not an instance of URLClassloader" );
239+ String .format (
240+ "Classpath for compilation could not be extracted "
241+ + "since %s is not an instance of URLClassloader" ,
242+ currentClassloader ));
190243 }
191244 currentClassloader = currentClassloader .getParent ();
192245 }
193246
194- return Joiner . on ( StandardSystemProperty . PATH_SEPARATOR . value ()). join ( classpaths );
247+ return classpaths . stream (). map ( File :: new ). collect ( toImmutableList () );
195248 }
196249
197- private Compiler copy (ImmutableList <Processor > processors , ImmutableList <String > options ) {
198- return new AutoValue_Compiler (javaCompiler (), processors , options );
250+ private Compiler copy (
251+ ImmutableList <Processor > processors ,
252+ ImmutableList <String > options ,
253+ Optional <ImmutableList <File >> classPath ) {
254+ return new AutoValue_Compiler (javaCompiler (), processors , options , classPath );
199255 }
200256}
0 commit comments