| // Copyright 2014 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 static com.google.devtools.build.lib.skyframe.BzlLoadValue.keyForBuiltins; |
| |
| 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.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.FilesToRunProvider; |
| import com.google.devtools.build.lib.analysis.PackageSpecificationProvider; |
| import com.google.devtools.build.lib.analysis.ProviderCollection; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.RuleErrorConsumer; |
| import com.google.devtools.build.lib.analysis.platform.ToolchainInfo; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.Depset; |
| import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException; |
| 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.Info; |
| import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.StarlarkInfo; |
| import com.google.devtools.build.lib.packages.StarlarkInfoWithSchema; |
| import com.google.devtools.build.lib.packages.StarlarkProviderWrapper; |
| import com.google.devtools.build.lib.packages.StructImpl; |
| import com.google.devtools.build.lib.rules.java.JavaPluginInfo.JavaPluginData; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.Dict; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkValue; |
| |
| /** Information about the JDK used by the <code>java_*</code> rules. */ |
| @Immutable |
| public final class JavaToolchainProvider extends StarlarkInfoWrapper { |
| |
| public static final StarlarkProviderWrapper<JavaToolchainProvider> PROVIDER = new Provider(); |
| |
| private JavaToolchainProvider(StarlarkInfo underlying) { |
| super(underlying); |
| } |
| |
| @Override |
| public int hashCode() { |
| try { |
| // StructImpl.hashcode() is too expensive, just the label should be enough |
| return getToolchainLabel().hashCode(); |
| } catch (RuleErrorException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof JavaToolchainProvider)) { |
| return false; |
| } |
| return underlying.equals(((JavaToolchainProvider) obj).underlying); |
| } |
| |
| /** Returns the Java Toolchain associated with the rule being analyzed or {@code null}. */ |
| public static JavaToolchainProvider from(RuleContext ruleContext) { |
| ToolchainInfo toolchainInfo = |
| ruleContext.getToolchainInfo( |
| ruleContext |
| .getPrerequisite(JavaRuleClasses.JAVA_TOOLCHAIN_TYPE_ATTRIBUTE_NAME) |
| .getLabel()); |
| return from(toolchainInfo, ruleContext); |
| } |
| |
| @VisibleForTesting |
| public static JavaToolchainProvider from(ProviderCollection collection) { |
| ToolchainInfo toolchainInfo = collection.get(ToolchainInfo.PROVIDER); |
| return from(toolchainInfo, null); |
| } |
| |
| @Nullable |
| private static JavaToolchainProvider from( |
| ToolchainInfo toolchainInfo, @Nullable RuleErrorConsumer errorConsumer) { |
| if (toolchainInfo != null) { |
| try { |
| JavaToolchainProvider provider = |
| JavaToolchainProvider.PROVIDER.wrap(toolchainInfo.getValue("java", Info.class)); |
| if (provider != null) { |
| return provider; |
| } |
| } catch (EvalException | RuleErrorException e) { |
| if (errorConsumer != null) { |
| errorConsumer.ruleError( |
| String.format("There was an error reading the Java toolchain: %s", e)); |
| } |
| } |
| } |
| if (errorConsumer != null) { |
| errorConsumer.ruleError("The selected Java toolchain is not a JavaToolchainProvider"); |
| } |
| return null; |
| } |
| |
| /** Returns the label for this {@code java_toolchain}. */ |
| public Label getToolchainLabel() throws RuleErrorException { |
| return Preconditions.checkNotNull(getUnderlyingValue("label", Label.class)); |
| } |
| |
| /** Returns the target Java bootclasspath. */ |
| public BootClassPathInfo getBootclasspath() throws RuleErrorException { |
| return BootClassPathInfo.PROVIDER.wrap(getUnderlyingValue("_bootclasspath_info", Info.class)); |
| } |
| |
| /** Returns the {@link Artifact}s of compilation tools. */ |
| public NestedSet<Artifact> getTools() throws RuleErrorException { |
| return getUnderlyingNestedSet("tools", Artifact.class); |
| } |
| |
| /** Returns the {@link JavaToolchainTool} for JavaBuilder */ |
| public JavaToolchainTool getJavaBuilder() throws RuleErrorException { |
| return JavaToolchainTool.fromStarlark(getUnderlyingValue("_javabuilder", StructImpl.class)); |
| } |
| |
| /** Returns the {@link JavaToolchainTool} for the header compiler */ |
| @Nullable |
| public JavaToolchainTool getHeaderCompiler() throws RuleErrorException { |
| return JavaToolchainTool.fromStarlark(getUnderlyingValue("_header_compiler", StructImpl.class)); |
| } |
| |
| /** |
| * Returns the {@link FilesToRunProvider} of the Header Compiler deploy jar for direct-classpath, |
| * non-annotation processing actions. |
| */ |
| @Nullable |
| public JavaToolchainTool getHeaderCompilerDirect() throws RuleErrorException { |
| return JavaToolchainTool.fromStarlark( |
| getUnderlyingValue("_header_compiler_direct", StructImpl.class)); |
| } |
| |
| @Nullable |
| @VisibleForTesting |
| public StructImpl getAndroidLint() throws RuleErrorException { |
| return getUnderlyingValue("_android_linter", StructImpl.class); |
| } |
| |
| @Nullable |
| public JspecifyInfo jspecifyInfo() throws RuleErrorException { |
| return JspecifyInfo.fromStarlark(getUnderlyingValue("_jspecify_info", StarlarkValue.class)); |
| } |
| |
| @Nullable |
| public JavaToolchainTool getBytecodeOptimizer() throws RuleErrorException { |
| return JavaToolchainTool.fromStarlark( |
| getUnderlyingValue("_bytecode_optimizer", StructImpl.class)); |
| } |
| |
| public ImmutableList<Artifact> getLocalJavaOptimizationConfiguration() throws RuleErrorException { |
| return getUnderlyingSequence("_local_java_optimization_config", Artifact.class) |
| .getImmutableList(); |
| } |
| |
| /** Returns class names of annotation processors that are built in to the header compiler. */ |
| public ImmutableSet<String> getHeaderCompilerBuiltinProcessors() throws RuleErrorException { |
| return getUnderlyingNestedSet("_header_compiler_builtin_processors", String.class).toSet(); |
| } |
| |
| public ImmutableSet<String> getReducedClasspathIncompatibleProcessors() |
| throws RuleErrorException { |
| return getUnderlyingNestedSet("_reduced_classpath_incompatible_processors", String.class) |
| .toSet(); |
| } |
| |
| /** |
| * Returns {@code true} if header compilation should be forcibly disabled, overriding |
| * --java_header_compilation. |
| */ |
| public boolean getForciblyDisableHeaderCompilation() throws RuleErrorException { |
| return getUnderlyingValue("_forcibly_disable_header_compilation", Boolean.class); |
| } |
| |
| /** Returns the {@link FilesToRunProvider} of the SingleJar tool. */ |
| public FilesToRunProvider getSingleJar() throws RuleErrorException { |
| return getUnderlyingValue("single_jar", FilesToRunProvider.class); |
| } |
| |
| /** |
| * Return the {@link FilesToRunProvider} of the tool that enforces one-version compliance of Java |
| * binaries. |
| */ |
| @Nullable |
| public FilesToRunProvider getOneVersionBinary() throws RuleErrorException { |
| return getUnderlyingValue("_one_version_tool", FilesToRunProvider.class); |
| } |
| |
| /** Return the {@link Artifact} of the allowlist used by the one-version compliance checker. */ |
| @Nullable |
| public Artifact getOneVersionAllowlist() throws RuleErrorException { |
| return getUnderlyingValue("_one_version_allowlist", Artifact.class); |
| } |
| |
| /** |
| * Return the {@link Artifact} of the one-version allowlist for tests used by the one-version |
| * compliance checker. |
| */ |
| @Nullable |
| public Artifact oneVersionAllowlistForTests() throws RuleErrorException { |
| return getUnderlyingValue("_one_version_allowlist_for_tests", Artifact.class); |
| } |
| |
| /** Returns the {@link Artifact} of the GenClass deploy jar */ |
| public Artifact getGenClass() throws RuleErrorException { |
| return getUnderlyingValue("_gen_class", Artifact.class); |
| } |
| |
| /** |
| * Returns the {@link Artifact} of the latest timezone data resource jar that can be loaded by |
| * Java 8 binaries. |
| */ |
| @Nullable |
| @VisibleForTesting |
| public Artifact getTimezoneData() throws RuleErrorException { |
| return getUnderlyingValue("_timezone_data", Artifact.class); |
| } |
| |
| /** Returns the ijar executable */ |
| public FilesToRunProvider getIjar() throws RuleErrorException { |
| return getUnderlyingValue("ijar", FilesToRunProvider.class); |
| } |
| |
| /** Returns the map of target environment-specific javacopts. */ |
| private NestedSet<String> getCompatibleJavacOptions(String key) throws RuleErrorException { |
| try { |
| return Dict.noneableCast( |
| underlying.getValue("_compatible_javacopts"), |
| String.class, |
| Depset.class, |
| "_compatible_javacopts") |
| .getOrDefault( |
| key, Depset.of(String.class, NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER))) |
| .getSet(String.class); |
| } catch (TypeException | EvalException e) { |
| throw new RuleErrorException(e); |
| } |
| } |
| |
| public ImmutableList<String> getCompatibleJavacOptionsAsList(String key) |
| throws RuleErrorException { |
| return JavaHelper.tokenizeJavaOptions(getCompatibleJavacOptions(key)); |
| } |
| |
| private NestedSet<String> javacOptions() throws RuleErrorException { |
| return getUnderlyingNestedSet("_javacopts", String.class); |
| } |
| |
| public ImmutableList<String> getJavacOptionsAsList(RuleContext ruleContext) |
| throws RuleErrorException { |
| ImmutableList.Builder<String> result = |
| ImmutableList.<String>builder().addAll(JavaHelper.tokenizeJavaOptions(javacOptions())); |
| if (ruleContext != null) { |
| // TODO(b/78512644): require ruleContext to be non-null after java_common.default_javac_opts |
| // is turned down |
| result.addAll( |
| ruleContext.getFragment(JavaConfiguration.class).getDefaultJavacFlagsForStarlarkAsList()); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Returns the NestedSet of default options for the JVM running the java compiler and associated |
| * tools. |
| */ |
| public NestedSet<String> getJvmOptions() throws RuleErrorException { |
| return getUnderlyingNestedSet("jvm_opt", String.class); |
| } |
| |
| /** Returns whether JavaBuilders supports running as a persistent worker or not. */ |
| public boolean getJavacSupportsWorkers() throws RuleErrorException { |
| return getUnderlyingValue("_javac_supports_workers", Boolean.class); |
| } |
| |
| /** Returns whether JavaBuilders supports running persistent workers in multiplex mode */ |
| public boolean getJavacSupportsMultiplexWorkers() throws RuleErrorException { |
| return getUnderlyingValue("_javac_supports_multiplex_workers", Boolean.class); |
| } |
| |
| /** Returns whether JavaBuilders supports running persistent workers with cancellation */ |
| public boolean getJavacSupportsWorkerCancellation() throws RuleErrorException { |
| return getUnderlyingValue("_javac_supports_worker_cancellation", Boolean.class); |
| } |
| |
| /** Returns whether JavaBuilders supports running multiplex persistent workers in sandbox mode */ |
| public boolean getJavacSupportsWorkerMultiplexSandboxing() throws RuleErrorException { |
| return getUnderlyingValue("_javac_supports_worker_multiplex_sandboxing", Boolean.class); |
| } |
| |
| /** Returns the global {@code java_package_configuration} data. */ |
| public ImmutableList<JavaPackageConfigurationProvider> packageConfiguration() |
| throws RuleErrorException { |
| return JavaPackageConfigurationProvider.wrapSequence( |
| getUnderlyingSequence("_package_configuration", StructImpl.class)); |
| } |
| |
| public FilesToRunProvider getJacocoRunner() throws RuleErrorException { |
| return getUnderlyingValue("jacocorunner", FilesToRunProvider.class); |
| } |
| |
| public FilesToRunProvider getProguardAllowlister() throws RuleErrorException { |
| return getUnderlyingValue("proguard_allowlister", FilesToRunProvider.class); |
| } |
| |
| public JavaRuntimeInfo getJavaRuntime() throws RuleErrorException { |
| return JavaRuntimeInfo.PROVIDER.wrap(getUnderlyingValue("java_runtime", Info.class)); |
| } |
| |
| @AutoValue |
| abstract static class JspecifyInfo { |
| |
| abstract JavaPluginData jspecifyProcessor(); |
| |
| abstract NestedSet<Artifact> jspecifyImplicitDeps(); |
| |
| abstract ImmutableList<String> jspecifyJavacopts(); |
| |
| abstract ImmutableList<PackageSpecificationProvider> jspecifyPackages(); |
| |
| boolean matches(Label label) { |
| for (PackageSpecificationProvider provider : jspecifyPackages()) { |
| for (PackageGroupContents specifications : provider.getPackageSpecifications().toList()) { |
| if (specifications.containsPackage(label.getPackageIdentifier())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| @Nullable |
| static JspecifyInfo fromStarlark(@Nullable StarlarkValue value) throws RuleErrorException { |
| if (value == null || value == Starlark.NONE) { |
| return null; |
| } else if (value instanceof StructImpl struct) { |
| try { |
| return new AutoValue_JavaToolchainProvider_JspecifyInfo( |
| JavaPluginData.wrap(struct.getValue("processor")), |
| Depset.noneableCast( |
| struct.getValue("implicit_deps"), Artifact.class, "implicit_deps"), |
| Sequence.noneableCast(struct.getValue("javacopts"), String.class, "javacopts") |
| .getImmutableList(), |
| Sequence.noneableCast( |
| struct.getValue("packages"), PackageSpecificationProvider.class, "packages") |
| .getImmutableList()); |
| } catch (EvalException e) { |
| throw new RuleErrorException(e); |
| } |
| } else { |
| throw new RuleErrorException("expected JspecifyInfo, got: " + Starlark.type(value)); |
| } |
| } |
| } |
| |
| private static class Provider extends StarlarkProviderWrapper<JavaToolchainProvider> { |
| |
| private Provider() { |
| super( |
| keyForBuiltins( |
| Label.parseCanonicalUnchecked("@_builtins//:common/java/java_toolchain.bzl")), |
| "JavaToolchainInfo"); |
| } |
| |
| @Override |
| public JavaToolchainProvider wrap(Info value) throws RuleErrorException { |
| if (value instanceof StarlarkInfoWithSchema |
| && value.getProvider().getKey().equals(getKey())) { |
| return new JavaToolchainProvider((StarlarkInfo) value); |
| } else { |
| throw new RuleErrorException( |
| "got value of type '" + Starlark.type(value) + "', want 'JavaToolchainInfo'"); |
| } |
| } |
| } |
| } |