| // Copyright 2016 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.lib.rules.java; |
| |
| import com.google.auto.value.AutoValue; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.collect.nestedset.Depset; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.collect.nestedset.Order; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.StructImpl; |
| import com.google.devtools.build.lib.rules.java.JavaInfo.JavaInfoInternalProvider; |
| import com.google.devtools.build.lib.starlarkbuildapi.java.JavaCompilationInfoProviderApi; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.util.Objects; |
| import javax.annotation.Nonnull; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| |
| /** |
| * A class that provides compilation information in Java rules, for perusal of aspects and tools. |
| */ |
| @Immutable |
| @AutoValue |
| public abstract class JavaCompilationInfoProvider |
| implements JavaInfoInternalProvider, JavaCompilationInfoProviderApi<Artifact> { |
| |
| /** |
| * Transforms the {@code compilation_info} field from a {@link JavaInfo} into a native instance. |
| * |
| * @param javaInfo A {@link JavaInfo} instance. |
| * @return a {@link JavaCompilationInfoProvider} instance or {@code null} if the {@code |
| * compilation_info} field is not present in the supplied {@code javaInfo} |
| * @throws RuleErrorException if the {@code compilation_info} is of an incompatible type |
| * @throws EvalException if there are any errors accessing Starlark values |
| */ |
| @Nullable |
| static JavaCompilationInfoProvider fromStarlarkJavaInfo(StructImpl javaInfo) |
| throws RuleErrorException, EvalException { |
| Object value = javaInfo.getValue("compilation_info"); |
| return fromStarlarkCompilationInfo(value); |
| } |
| |
| /** |
| * Translates an instance of {@link JavaCompilationInfoProvider} for use in native code. |
| * |
| * @param value The object to translate |
| * @return a {@link JavaCompilationInfoProvider} instance, or null if the supplied value is null |
| * or {@link Starlark#NONE} |
| * @throws EvalException if there are errors reading any fields from the {@link StructImpl} |
| * @throws RuleErrorException if the supplied value is not compatible with {@link |
| * JavaCompilationInfoProvider} |
| */ |
| @Nullable |
| @VisibleForTesting |
| public static JavaCompilationInfoProvider fromStarlarkCompilationInfo(Object value) |
| throws EvalException, RuleErrorException { |
| if (value == null || value == Starlark.NONE) { |
| return null; |
| } else if (value instanceof JavaCompilationInfoProvider) { |
| return (JavaCompilationInfoProvider) value; |
| } else if (value instanceof StructImpl) { |
| StructImpl info = (StructImpl) value; |
| Builder builder = |
| new Builder() |
| .setJavacOpts( |
| Sequence.cast(info.getValue("javac_options"), String.class, "javac_options") |
| .getImmutableList()) |
| .setBootClasspath( |
| NestedSetBuilder.wrap( |
| Order.NAIVE_LINK_ORDER, |
| Sequence.noneableCast( |
| info.getValue("boot_classpath"), Artifact.class, "boot_classpath"))); |
| Object runtimeClasspath = info.getValue("runtime_classpath"); |
| if (runtimeClasspath != null) { |
| builder.setRuntimeClasspath( |
| Depset.noneableCast(runtimeClasspath, Artifact.class, "runtime_classpath")); |
| } |
| Object compilationClasspath = info.getValue("compilation_classpath"); |
| if (compilationClasspath != null) { |
| builder.setCompilationClasspath( |
| Depset.noneableCast(compilationClasspath, Artifact.class, "compilation_classpath")); |
| } |
| return builder.build(); |
| } |
| throw new RuleErrorException("expected java_compilation_info, got: " + Starlark.type(value)); |
| } |
| |
| @Override |
| public boolean isImmutable() { |
| return true; // immutable and Starlark-hashable |
| } |
| |
| /** Builder for {@link JavaCompilationInfoProvider}. */ |
| public static class Builder { |
| private ImmutableList<String> javacOpts = ImmutableList.of(); |
| private NestedSet<Artifact> runtimeClasspath; |
| private NestedSet<Artifact> compilationClasspath; |
| private NestedSet<Artifact> bootClasspath = NestedSetBuilder.emptySet(Order.STABLE_ORDER); |
| |
| @CanIgnoreReturnValue |
| public Builder setJavacOpts(@Nonnull ImmutableList<String> javacOpts) { |
| this.javacOpts = javacOpts; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public Builder setRuntimeClasspath(@Nullable NestedSet<Artifact> runtimeClasspath) { |
| this.runtimeClasspath = runtimeClasspath; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public Builder setCompilationClasspath(@Nullable NestedSet<Artifact> compilationClasspath) { |
| this.compilationClasspath = compilationClasspath; |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public Builder setBootClasspath(NestedSet<Artifact> bootClasspath) { |
| this.bootClasspath = Preconditions.checkNotNull(bootClasspath); |
| return this; |
| } |
| |
| public JavaCompilationInfoProvider build() throws RuleErrorException { |
| return new AutoValue_JavaCompilationInfoProvider( |
| JavaCompilationHelper.internJavacOpts(javacOpts), |
| runtimeClasspath, |
| compilationClasspath, |
| bootClasspath); |
| } |
| } |
| |
| @Override |
| public abstract ImmutableList<String> getJavacOpts(); |
| |
| @Nullable |
| public abstract NestedSet<Artifact> runtimeClasspath(); |
| |
| @Override |
| @Nullable |
| public Depset /*<Artifact>*/ getRuntimeClasspath() { |
| return runtimeClasspath() == null ? null : Depset.of(Artifact.class, runtimeClasspath()); |
| } |
| |
| @Nullable |
| public abstract NestedSet<Artifact> compilationClasspath(); |
| |
| @Override |
| @Nullable |
| public Depset /*<Artifact>*/ getCompilationClasspath() { |
| return compilationClasspath() == null |
| ? null |
| : Depset.of(Artifact.class, compilationClasspath()); |
| } |
| |
| @Override |
| public ImmutableList<Artifact> getBootClasspathList() { |
| return bootClasspath().toList(); |
| } |
| |
| public abstract NestedSet<Artifact> bootClasspath(); |
| |
| /* |
| * Underrides the @Autovalue implementation. |
| * We shouldn't be doing this, but this is necessary to allow Starlark-constructed instances to |
| * be compared with natively constructed instances. The difference arises only because of the |
| * boot classpath. The Starlark API returns a list, while we store a NestedSet in |
| * native for efficiency. When we reconstruct a native instance from a Starlark one, the list is |
| * wrapped in a new NestedSet instance. Since NestedSet equality relies on |
| * reference-equality, here, we perform a NestedSet#shallowEquals only for the bootClasspath to |
| * verify the contents are the same. |
| * Note: this is temporary, and is required only while JavaCompilationInfoProvider is still |
| * constructed in native code. Once the migration to Starlark is complete, this will be deleted as |
| * this class will no longer have any fields but will simply wrap the StarlarkInfo instance and |
| * delegate in each of its public methods. |
| */ |
| @Override |
| public final boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof JavaCompilationInfoProvider)) { |
| return false; |
| } |
| JavaCompilationInfoProvider other = (JavaCompilationInfoProvider) obj; |
| return Objects.equals(getJavacOpts(), other.getJavacOpts()) |
| && Objects.equals(getRuntimeClasspath(), other.getRuntimeClasspath()) |
| && Objects.equals(getCompilationClasspath(), other.getCompilationClasspath()) |
| && bootClasspath().shallowEquals(other.bootClasspath()); |
| } |
| |
| /* See comment for #equals above on why we need this. */ |
| @Override |
| public final int hashCode() { |
| return Objects.hash( |
| getJavacOpts(), |
| getRuntimeClasspath(), |
| getCompilationClasspath(), |
| bootClasspath().shallowHashCode()); |
| } |
| } |