// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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.devtools.build.buildjar.javac.plugins.processing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.buildjar.javac.plugins.BlazeJavaCompilerPlugin;
import com.google.devtools.build.buildjar.proto.JavaCompilation.CompilationUnit;
import com.google.devtools.build.buildjar.proto.JavaCompilation.Manifest;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** A module for information about the compilation's annotation processing. */
public class AnnotationProcessingModule {
/** A builder for {@link AnnotationProcessingModule}s. */
public static class Builder {
private Path sourceGenDir;
private Path manifestProto;
private final ImmutableSet.Builder<Path> sourceRoots = ImmutableSet.builder();
private Builder() {}
public AnnotationProcessingModule build() {
return new AnnotationProcessingModule(
sourceGenDir, manifestProto, validateSourceRoots(sourceRoots.build()));
}
/**
* Verify that source roots do not contain other source roots.
*
* <p>If one source root is an ancestor of another, the source path to use in the manifest will
* be ambiguous.
*/
private ImmutableSet<Path> validateSourceRoots(ImmutableSet<Path> roots) {
// It's sad that this is quadratic, but the number of source roots
// should be <= 2.
for (Path a : roots) {
for (Path b : roots) {
if (a.equals(b) || b.getNameCount() == 0) {
continue;
}
if (a.startsWith(b)) {
throw new IllegalArgumentException(
String.format("Source root %s is a parent of %s", b, a));
}
}
}
return roots;
}
public void setSourceGenDir(Path sourceGenDir) {
this.sourceGenDir = sourceGenDir;
}
public void setManifestProtoPath(Path manifestProto) {
this.manifestProto = manifestProto.toAbsolutePath();
}
public void addAllSourceRoots(Set<String> sourceRoots) {
for (String root : sourceRoots) {
this.sourceRoots.add(Paths.get(root));
}
}
}
private final boolean enabled;
private final Path sourceGenDir;
private final Path manifestProto;
private final ImmutableSet<Path> sourceRoots;
public boolean isGenerated(Path path) {
return path.startsWith(sourceGenDir);
}
public Path stripSourceRoot(Path path) {
if (path.startsWith(sourceGenDir)) {
return sourceGenDir.relativize(path);
}
for (Path sourceRoot : sourceRoots) {
if (path.startsWith(sourceRoot)) {
return sourceRoot.relativize(path);
}
}
return path;
}
private AnnotationProcessingModule(
Path sourceGenDir, Path manifestProto, ImmutableSet<Path> sourceRoots) {
this.sourceGenDir = sourceGenDir;
this.manifestProto = manifestProto;
this.sourceRoots = sourceRoots;
this.enabled = sourceGenDir != null && manifestProto != null;
}
public static Builder builder() {
return new Builder();
}
public void registerPlugin(ImmutableList.Builder<BlazeJavaCompilerPlugin> builder) {
if (enabled) {
builder.add(new AnnotationProcessingPlugin(this));
}
}
private final Map<String, CompilationUnit> units = new HashMap<>();
public void recordUnit(CompilationUnit unit) {
units.put(unit.getPath(), unit);
}
private Manifest buildManifestProto() {
Manifest.Builder builder = Manifest.newBuilder();
List<String> keys = new ArrayList<>(units.keySet());
Collections.sort(keys);
for (String key : keys) {
CompilationUnit unit = units.get(key);
builder.addCompilationUnit(unit);
}
return builder.build();
}
public void emitManifestProto() throws IOException {
if (!enabled) {
return;
}
try (OutputStream out = Files.newOutputStream(manifestProto)) {
buildManifestProto().writeTo(out);
} catch (IOException ex) {
throw new IOException("Cannot write manifest to " + manifestProto, ex);
}
}
}