Skip to content

Commit

Permalink
Add withClasspath(Iterable<File>) to Compiler and JavaSourcesSubject …
Browse files Browse the repository at this point in the history
…and deprecate withClasspathFrom(ClassLoader)

RELNOTES=Add `withClasspath(Iterable<File>)` to `Compiler` and `JavaSourcesSubject` and deprecate `withClasspathFrom(ClassLoader)`

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185700333
  • Loading branch information
cushon authored and ronshapiro committed Feb 15, 2018
1 parent ebbc462 commit 7622471
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 30 deletions.
90 changes: 73 additions & 17 deletions src/main/java/com/google/testing/compile/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,39 @@
package com.google.testing.compile;

import static com.google.common.base.Functions.toStringFunction;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.tools.ToolProvider.getSystemJavaCompiler;

import com.google.auto.value.AutoValue;
import com.google.common.base.Joiner;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.base.StandardSystemProperty;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.testing.compile.Compilation.Status;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.Processor;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;

/** An object that can {@link #compile} Java source files. */
@AutoValue
// clashes with java.lang.Compiler (which is deprecated for removal in 9)
@SuppressWarnings("JavaLangClash")
public abstract class Compiler {

/** Returns the {@code javac} compiler. */
Expand All @@ -48,7 +58,8 @@ public static Compiler javac() {

/** Returns a {@link Compiler} that uses a given {@link JavaCompiler} instance. */
public static Compiler compiler(JavaCompiler javaCompiler) {
return new AutoValue_Compiler(javaCompiler, ImmutableList.of(), ImmutableList.of());
return new AutoValue_Compiler(
javaCompiler, ImmutableList.of(), ImmutableList.of(), Optional.empty());
}

abstract JavaCompiler javaCompiler();
Expand All @@ -59,6 +70,9 @@ public static Compiler compiler(JavaCompiler javaCompiler) {
/** The options passed to the compiler. */
public abstract ImmutableList<String> options();

/** The compilation class path. If not present, the system class path is used. */
public abstract Optional<ImmutableList<File>> classPath();

/**
* Uses annotation processors during compilation. These replace any previously specified.
*
Expand All @@ -78,7 +92,7 @@ public final Compiler withProcessors(Processor... processors) {
* @return a new instance with the same options and the given processors
*/
public final Compiler withProcessors(Iterable<? extends Processor> processors) {
return copy(ImmutableList.copyOf(processors), options());
return copy(ImmutableList.copyOf(processors), options(), classPath());
}

/**
Expand All @@ -96,21 +110,30 @@ public final Compiler withOptions(Object... options) {
* @return a new instance with the same processors and the given options
*/
public final Compiler withOptions(Iterable<?> options) {
return copy(processors(), FluentIterable.from(options).transform(toStringFunction()).toList());
return copy(
processors(),
FluentIterable.from(options).transform(toStringFunction()).toList(),
classPath());
}

/**
* Uses the classpath from the passed on classloader (and its parents) for the compilation
* instead of the system classpath.
* Uses the classpath from the passed on classloader (and its parents) for the compilation instead
* of the system classpath.
*
* @throws IllegalArgumentException if the given classloader had classpaths which we could not
* determine or use for compilation.
* @deprecated prefer {@link #withClasspath(Iterable)}. This method only supports {@link
* URLClassLoader} and the default system classloader, and {@link File}s are usually a more
* natural way to expression compilation classpaths than class loaders.
*/
@Deprecated
public final Compiler withClasspathFrom(ClassLoader classloader) {
String classpath = getClasspathFromClassloader(classloader);
ImmutableList<String> options =
ImmutableList.<String>builder().add("-classpath").add(classpath).addAll(options()).build();
return copy(processors(), options);
return copy(processors(), options(), Optional.of(getClasspathFromClassloader(classloader)));
}

/** Uses the given classpath for the compilation instead of the system classpath. */
public final Compiler withClasspath(Iterable<File> classPath) {
return copy(processors(), options(), Optional.of(ImmutableList.copyOf(classPath)));
}

/**
Expand All @@ -132,6 +155,16 @@ public final Compilation compile(Iterable<? extends JavaFileObject> files) {
InMemoryJavaFileManager fileManager =
new InMemoryJavaFileManager(
javaCompiler().getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8));
classPath()
.ifPresent(
classPath -> {
try {
fileManager.setLocation(StandardLocation.CLASS_PATH, classPath);
} catch (IOException e) {
// impossible by specification
throw new UncheckedIOException(e);
}
});
CompilationTask task =
javaCompiler()
.getTask(
Expand All @@ -156,20 +189,38 @@ public final Compilation compile(Iterable<? extends JavaFileObject> files) {
return compilation;
}

@VisibleForTesting static final ClassLoader platformClassLoader = getPlatformClassLoader();

private static ClassLoader getPlatformClassLoader() {
try {
// JDK >= 9
return (ClassLoader) ClassLoader.class.getMethod("getPlatformClassLoader").invoke(null);
} catch (ReflectiveOperationException e) {
// Java <= 8
return null;
}
}

/**
* Returns the current classpaths of the given classloader including its parents.
*
* @throws IllegalArgumentException if the given classloader had classpaths which we could not
* determine or use for compilation.
*/
private static String getClasspathFromClassloader(ClassLoader currentClassloader) {
private static ImmutableList<File> getClasspathFromClassloader(ClassLoader currentClassloader) {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

// Concatenate search paths from all classloaders in the hierarchy 'till the system classloader.
Set<String> classpaths = new LinkedHashSet<>();
while (true) {
if (currentClassloader == systemClassLoader) {
classpaths.add(StandardSystemProperty.JAVA_CLASS_PATH.value());
Iterables.addAll(
classpaths,
Splitter.on(StandardSystemProperty.PATH_SEPARATOR.value())
.split(StandardSystemProperty.JAVA_CLASS_PATH.value()));
break;
}
if (currentClassloader == platformClassLoader) {
break;
}
if (currentClassloader instanceof URLClassLoader) {
Expand All @@ -185,16 +236,21 @@ private static String getClasspathFromClassloader(ClassLoader currentClassloader
}
} else {
throw new IllegalArgumentException(
"Classpath for compilation could not be extracted "
+ "since given classloader is not an instance of URLClassloader");
String.format(
"Classpath for compilation could not be extracted "
+ "since %s is not an instance of URLClassloader",
currentClassloader));
}
currentClassloader = currentClassloader.getParent();
}

return Joiner.on(StandardSystemProperty.PATH_SEPARATOR.value()).join(classpaths);
return classpaths.stream().map(File::new).collect(toImmutableList());
}

private Compiler copy(ImmutableList<Processor> processors, ImmutableList<String> options) {
return new AutoValue_Compiler(javaCompiler(), processors, options);
private Compiler copy(
ImmutableList<Processor> processors,
ImmutableList<String> options,
Optional<ImmutableList<File>> classPath) {
return new AutoValue_Compiler(javaCompiler(), processors, options, classPath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2018 Google, Inc.
*
* 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 com.google.testing.compile;

import java.io.File;
import java.io.IOException;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;

/**
* Forwards calls to a given {@link StandardJavaFileManager}. Subclasses of this class might
* override some of these methods and might also provide additional fields and methods.
*/
class ForwardingStandardJavaFileManager extends ForwardingJavaFileManager<StandardJavaFileManager>
implements StandardJavaFileManager {

/**
* Creates a new instance of ForwardingStandardJavaFileManager.
*
* @param fileManager delegate to this file manager
*/
ForwardingStandardJavaFileManager(StandardJavaFileManager fileManager) {
super(fileManager);
}

@Override
public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
Iterable<? extends File> files) {
return fileManager.getJavaFileObjectsFromFiles(files);
}

@Override
public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
return fileManager.getJavaFileObjects(files);
}

@Override
public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
return fileManager.getJavaFileObjects(names);
}

@Override
public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
return fileManager.getJavaFileObjectsFromStrings(names);
}

@Override
public void setLocation(Location location, Iterable<? extends File> path) throws IOException {
fileManager.setLocation(location, path);
}

@Override
public Iterable<? extends File> getLocation(Location location) {
return fileManager.getLocation(location);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@
import java.nio.charset.Charset;
import java.util.Map.Entry;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;

/**
Expand All @@ -47,7 +46,7 @@
* @author Gregory Kick
*/
// TODO(gak): under java 1.7 this could all be done with a PathFileManager
final class InMemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
final class InMemoryJavaFileManager extends ForwardingStandardJavaFileManager {
private final LoadingCache<URI, JavaFileObject> inMemoryFileObjects =
CacheBuilder.newBuilder().build(new CacheLoader<URI, JavaFileObject>() {
@Override
Expand All @@ -56,7 +55,7 @@ public JavaFileObject load(URI key) {
}
});

InMemoryJavaFileManager(JavaFileManager fileManager) {
InMemoryJavaFileManager(StandardJavaFileManager fileManager) {
super(fileManager);
}

Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/google/testing/compile/JavaSourcesSubject.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.google.testing.compile.CompilationSubject.DiagnosticOnLine;
import com.google.testing.compile.Parser.ParseResult;
import com.sun.source.tree.CompilationUnitTree;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
Expand All @@ -65,6 +66,7 @@ public final class JavaSourcesSubject
implements CompileTester, ProcessedCompileTesterFactory {
private final List<String> options = new ArrayList<String>(Arrays.asList("-Xlint"));
@Nullable private ClassLoader classLoader;
@Nullable private ImmutableList<File> classPath;

JavaSourcesSubject(FailureMetadata failureMetadata, Iterable<? extends JavaFileObject> subject) {
super(failureMetadata, subject);
Expand All @@ -82,12 +84,24 @@ public JavaSourcesSubject withCompilerOptions(String... options) {
return this;
}

/**
* @deprecated prefer {@link #withClasspath(Iterable)}. This method only supports {@link
* URLClassLoader} and the default system classloader, and {@link File}s are usually a more
* natural way to expression compilation classpaths than class loaders.
*/
@Deprecated
@Override
public JavaSourcesSubject withClasspathFrom(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}

@Override
public JavaSourcesSubject withClasspath(Iterable<File> classPath) {
this.classPath = ImmutableList.copyOf(classPath);
return this;
}

@Override
public CompileTester processedWith(Processor first, Processor... rest) {
return processedWith(Lists.asList(first, rest));
Expand Down Expand Up @@ -312,6 +326,9 @@ private Compilation compilation() {
if (classLoader != null) {
compiler = compiler.withClasspathFrom(classLoader);
}
if (classPath != null) {
compiler = compiler.withClasspath(classPath);
}
return compiler.compile(actual());
}
}
Expand Down Expand Up @@ -577,11 +594,22 @@ public JavaSourcesSubject withCompilerOptions(String... options) {
return delegate.withCompilerOptions(options);
}

/**
* @deprecated prefer {@link #withClasspath(Iterable)}. This method only supports {@link
* URLClassLoader} and the default system classloader, and {@link File}s are usually a more
* natural way to expression compilation classpaths than class loaders.
*/
@Deprecated
@Override
public JavaSourcesSubject withClasspathFrom(ClassLoader classLoader) {
return delegate.withClasspathFrom(classLoader);
}

@Override
public JavaSourcesSubject withClasspath(Iterable<File> classPath) {
return delegate.withClasspath(classPath);
}

@Override
public CompileTester processedWith(Processor first, Processor... rest) {
return delegate.newCompilationClause(Lists.asList(first, rest));
Expand Down

0 comments on commit 7622471

Please sign in to comment.