/*
* Copyright 2013-present Facebook, 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.facebook.buck.jvm.java;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.PathSourcePath;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.TestExecutionContext;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.integration.TemporaryPaths;
import com.facebook.buck.util.MockClassLoader;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import javax.lang.model.SourceVersion;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class Jsr199JavacIntegrationTest {
public static final ImmutableSortedSet<Path> SOURCE_PATHS =
ImmutableSortedSet.of(Paths.get("Example.java"));
@Rule public TemporaryPaths tmp = new TemporaryPaths();
private Path pathToSrcsList;
@Before
public void setUp() {
pathToSrcsList = tmp.getRoot().resolve("srcs_list");
}
@Test
public void testGetDescription() throws IOException {
Jsr199Javac javac = createJavac(/* withSyntaxError */ false);
String pathToOutputDir = tmp.getRoot().resolve("out").toAbsolutePath().toString();
assertEquals(
String.format(
"javac -source %s -target %s -g "
+ "-d %s "
+ "-classpath '' "
+ "@"
+ pathToSrcsList.toString(),
JavacOptions.TARGETED_JAVA_VERSION,
JavacOptions.TARGETED_JAVA_VERSION,
pathToOutputDir),
javac.getDescription(
ImmutableList.of(
"-source",
JavacOptions.TARGETED_JAVA_VERSION,
"-target",
JavacOptions.TARGETED_JAVA_VERSION,
"-g",
"-d",
pathToOutputDir,
"-classpath",
"''"),
SOURCE_PATHS,
pathToSrcsList));
}
@Test
public void testGetShortName() throws IOException {
Jsr199Javac javac = createJavac(/* withSyntaxError */ false);
assertEquals("javac", javac.getShortName());
}
@Test
public void testClassesFile() throws IOException, InterruptedException {
Jsr199Javac javac = createJavac(/* withSyntaxError */ false);
ExecutionContext executionContext = TestExecutionContext.newInstance();
JavacExecutionContext javacExecutionContext =
JavacExecutionContext.of(
new JavacEventSinkToBuckEventBusBridge(executionContext.getBuckEventBus()),
executionContext.getStdErr(),
executionContext.getClassLoaderCache(),
executionContext.getVerbosity(),
executionContext.getCellPathResolver(),
executionContext.getJavaPackageFinder(),
createProjectFilesystem(),
NoOpClassUsageFileWriter.instance(),
executionContext.getEnvironment(),
executionContext.getProcessExecutor(),
ImmutableList.of(),
Optional.empty());
int exitCode =
javac.buildWithClasspath(
javacExecutionContext,
BuildTargetFactory.newInstance("//some:example"),
ImmutableList.of(),
ImmutableList.of(),
SOURCE_PATHS,
pathToSrcsList,
Optional.empty(),
JavacCompilationMode.FULL);
assertEquals("javac should exit with code 0.", exitCode, 0);
assertTrue(Files.exists(pathToSrcsList));
assertTrue(Files.isRegularFile(pathToSrcsList));
assertEquals(
"Example.java",
new String(Files.readAllBytes(pathToSrcsList), StandardCharsets.UTF_8).trim());
}
/**
* There was a bug where `BuildTargetSourcePath` sources were written to the classes file using
* their string representation, rather than their resolved path.
*/
@Test
public void shouldWriteResolvedBuildTargetSourcePathsToClassesFile()
throws IOException, InterruptedException {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
BuildRule rule = new FakeBuildRule("//:fake", pathResolver);
resolver.addToIndex(rule);
Jsr199Javac javac = createJavac(/* withSyntaxError */ false);
ExecutionContext executionContext = TestExecutionContext.newInstance();
JavacExecutionContext javacExecutionContext =
JavacExecutionContext.of(
new JavacEventSinkToBuckEventBusBridge(executionContext.getBuckEventBus()),
executionContext.getStdErr(),
executionContext.getClassLoaderCache(),
executionContext.getVerbosity(),
executionContext.getCellPathResolver(),
executionContext.getJavaPackageFinder(),
createProjectFilesystem(),
NoOpClassUsageFileWriter.instance(),
executionContext.getEnvironment(),
executionContext.getProcessExecutor(),
ImmutableList.of(),
Optional.empty());
int exitCode =
javac.buildWithClasspath(
javacExecutionContext,
BuildTargetFactory.newInstance("//some:example"),
ImmutableList.of(),
ImmutableList.of(),
SOURCE_PATHS,
pathToSrcsList,
Optional.empty(),
JavacCompilationMode.FULL);
assertEquals("javac should exit with code 0.", exitCode, 0);
assertTrue(Files.exists(pathToSrcsList));
assertTrue(Files.isRegularFile(pathToSrcsList));
assertEquals(
"Example.java",
new String(Files.readAllBytes(pathToSrcsList), StandardCharsets.UTF_8).trim());
}
public static final class MockJavac implements JavaCompiler {
public MockJavac() {}
@Override
public Set<SourceVersion> getSourceVersions() {
return ImmutableSet.of(SourceVersion.RELEASE_7);
}
@Override
public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) {
throw new UnsupportedOperationException("abcdef");
}
@Override
public int isSupportedOption(String option) {
return -1;
}
@Override
public StandardJavaFileManager getStandardFileManager(
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Locale locale,
Charset charset) {
throw new UnsupportedOperationException("abcdef");
}
@Override
public CompilationTask getTask(
Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits) {
throw new UnsupportedOperationException("abcdef");
}
}
@Test
public void shouldUseSpecifiedJavacJar() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
BuildRule rule = new FakeBuildRule("//:fake", pathResolver);
resolver.addToIndex(rule);
Path fakeJavacJar = Paths.get("ae036e57-77a7-4356-a79c-0f85b1a3290d", "fakeJavac.jar");
ExecutionContext executionContext = TestExecutionContext.newInstance();
MockClassLoader mockClassLoader =
new MockClassLoader(
ClassLoader.getSystemClassLoader(),
ImmutableMap.of(JavacSpec.COM_SUN_TOOLS_JAVAC_API_JAVAC_TOOL, MockJavac.class));
executionContext
.getClassLoaderCache()
.injectClassLoader(
ClassLoader.getSystemClassLoader(),
ImmutableList.of(fakeJavacJar.toUri().toURL()),
mockClassLoader);
Jsr199Javac javac = createJavac(/* withSyntaxError */ false, Optional.of(fakeJavacJar));
JavacExecutionContext javacExecutionContext =
JavacExecutionContext.of(
new JavacEventSinkToBuckEventBusBridge(executionContext.getBuckEventBus()),
executionContext.getStdErr(),
executionContext.getClassLoaderCache(),
executionContext.getVerbosity(),
executionContext.getCellPathResolver(),
executionContext.getJavaPackageFinder(),
createProjectFilesystem(),
NoOpClassUsageFileWriter.instance(),
executionContext.getEnvironment(),
executionContext.getProcessExecutor(),
ImmutableList.of(fakeJavacJar),
Optional.empty());
boolean caught = false;
try {
javac.buildWithClasspath(
javacExecutionContext,
BuildTargetFactory.newInstance("//some:example"),
ImmutableList.of(),
ImmutableList.of(),
SOURCE_PATHS,
pathToSrcsList,
Optional.empty(),
JavacCompilationMode.FULL);
fail("Did not expect compilation to succeed");
} catch (UnsupportedOperationException ex) {
if (ex.toString().contains("abcdef")) {
caught = true;
}
}
assertTrue("mock Java compiler should throw", caught);
}
private Jsr199Javac createJavac(boolean withSyntaxError, Optional<Path> javacJar)
throws IOException {
Path exampleJava = tmp.newFile("Example.java");
Files.write(
exampleJava,
Joiner.on('\n')
.join(
"package com.example;", "", "public class Example {" + (withSyntaxError ? "" : "}"))
.getBytes(Charsets.UTF_8));
Path pathToOutputDirectory = Paths.get("out");
tmp.newFolder(pathToOutputDirectory.toString());
Optional<SourcePath> jar =
javacJar.map(p -> new PathSourcePath(new FakeProjectFilesystem(), p));
if (jar.isPresent()) {
return new JarBackedJavac(
JavacSpec.COM_SUN_TOOLS_JAVAC_API_JAVAC_TOOL, ImmutableSet.of(jar.get()));
}
return new JdkProvidedInMemoryJavac();
}
private Jsr199Javac createJavac(boolean withSyntaxError) throws IOException {
return createJavac(withSyntaxError, Optional.empty());
}
private ProjectFilesystem createProjectFilesystem() throws InterruptedException {
return new ProjectFilesystem(tmp.getRoot());
}
}