blob: 456148a1f4db6314840bdea3208a654f21358f9a [file] [log] [blame]
// Copyright 2021 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.bazel.bzlmod;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.docgen.annot.DocCategory;
import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager;
import com.google.devtools.build.lib.bazel.repository.starlark.StarlarkBaseExternalContext;
import com.google.devtools.build.lib.runtime.ProcessWrapper;
import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
import java.util.Map;
import javax.annotation.Nullable;
import net.starlark.java.annot.Param;
import net.starlark.java.annot.ParamType;
import net.starlark.java.annot.StarlarkBuiltin;
import net.starlark.java.annot.StarlarkMethod;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.NoneType;
import net.starlark.java.eval.Sequence;
import net.starlark.java.eval.StarlarkList;
import net.starlark.java.eval.StarlarkSemantics;
/** The Starlark object passed to the implementation function of module extensions. */
@StarlarkBuiltin(
name = "module_ctx",
category = DocCategory.BUILTIN,
doc =
"The context of the module extension containing helper functions and information about"
+ " pertinent tags across the dependency graph. You get a module_ctx object as an"
+ " argument to the <code>implementation</code> function when you create a module"
+ " extension.")
public class ModuleExtensionContext extends StarlarkBaseExternalContext {
private final ModuleExtensionId extensionId;
private final StarlarkList<StarlarkBazelModule> modules;
private final boolean rootModuleHasNonDevDependency;
protected ModuleExtensionContext(
Path workingDirectory,
Environment env,
Map<String, String> envVariables,
DownloadManager downloadManager,
double timeoutScaling,
@Nullable ProcessWrapper processWrapper,
StarlarkSemantics starlarkSemantics,
@Nullable RepositoryRemoteExecutor remoteExecutor,
ModuleExtensionId extensionId,
StarlarkList<StarlarkBazelModule> modules,
boolean rootModuleHasNonDevDependency) {
super(
workingDirectory,
env,
envVariables,
downloadManager,
timeoutScaling,
processWrapper,
starlarkSemantics,
remoteExecutor);
this.extensionId = extensionId;
this.modules = modules;
this.rootModuleHasNonDevDependency = rootModuleHasNonDevDependency;
}
public Path getWorkingDirectory() {
return workingDirectory;
}
@Override
protected String getIdentifyingStringForLogging() {
return String.format(
"module extension %s in %s", extensionId.getExtensionName(), extensionId.getBzlFileLabel());
}
@Override
protected boolean isRemotable() {
// Maybe we can some day support remote execution, but not today.
return false;
}
@Override
protected ImmutableMap<String, String> getRemoteExecProperties() throws EvalException {
return ImmutableMap.of();
}
@StarlarkMethod(
name = "modules",
structField = true,
doc =
"A list of all the Bazel modules in the external dependency graph that use this module "
+ "extension, each of which is a <a href=\"../builtins/bazel_module.html\">"
+ "bazel_module</a> object that exposes all the tags it specified for this extension."
+ " The iteration order of this dictionary is guaranteed to be the same as"
+ " breadth-first search starting from the root module.")
public StarlarkList<StarlarkBazelModule> getModules() {
return modules;
}
@StarlarkMethod(
name = "is_dev_dependency",
doc =
"Returns whether the given tag was specified on the result of a <a "
+ "href=\"globals.html#use_extension\">use_extension</a> call with "
+ "<code>devDependency = True</code>.",
parameters = {
@Param(
name = "tag",
doc = "A tag obtained from <a href=\"bazel_module.html#tags\">bazel_module.tags</a>.",
allowedTypes = {@ParamType(type = TypeCheckedTag.class)})
})
public boolean isDevDependency(TypeCheckedTag tag) {
return tag.isDevDependency();
}
@StarlarkMethod(
name = "is_isolated",
doc =
"Whether this particular usage of the extension had <code>isolate = True</code> "
+ "specified and is thus isolated from all other usages."
+ "<p>This field is currently experimental and only available with the flag "
+ "<code>--experimental_isolated_extension_usages</code>.",
structField = true,
enableOnlyWithFlag = "-experimental_isolated_extension_usages")
public boolean isIsolated() {
return extensionId.getIsolationKey().isPresent();
}
@StarlarkMethod(
name = "root_module_has_non_dev_dependency",
doc = "Whether the root module uses this extension as a non-dev dependency.",
structField = true)
public boolean rootModuleHasNonDevDependency() {
return rootModuleHasNonDevDependency;
}
@StarlarkMethod(
name = "extension_metadata",
doc =
"Constructs an opaque object that can be returned from the module extension's"
+ " implementation function to provide metadata about the repositories generated by"
+ " the extension to Bazel.",
parameters = {
@Param(
name = "root_module_direct_deps",
doc =
"The names of the repositories that the extension considers to be direct"
+ " dependencies of the root module. If the root module imports additional"
+ " repositories or does not import all of these repositories via <a"
+ " href=\"../globals/module.html#use_repo\"><code>use_repo</code></a>, Bazel"
+ " will print a warning and a fixup command when the extension is"
+ " evaluated.<p>If one of <code>root_module_direct_deps</code> and"
+ " <code>root_module_direct_dev_deps</code> is specified, the other has to be"
+ " as well. The lists specified by these two parameters must be"
+ " disjoint.<p>Exactly one of <code>root_module_direct_deps</code> and"
+ " <code>root_module_direct_dev_deps</code> can be set to the special value"
+ " <code>\"all\"</code>, which is treated as if a list with the names of"
+ " all repositories generated by the extension was specified as the value.",
positional = false,
named = true,
defaultValue = "None",
allowedTypes = {
@ParamType(type = Sequence.class, generic1 = String.class),
@ParamType(type = String.class),
@ParamType(type = NoneType.class)
}),
@Param(
name = "root_module_direct_dev_deps",
doc =
"The names of the repositories that the extension considers to be direct dev"
+ " dependencies of the root module. If the root module imports additional"
+ " repositories or does not import all of these repositories via <a"
+ " href=\"../globals/module.html#use_repo\"><code>use_repo</code></a> on an"
+ " extension proxy created with <code><a"
+ " href=\"../globals/module.html#use_extension\">use_extension</a>(...,"
+ " dev_dependency = True)</code>, Bazel will print a warning and a fixup"
+ " command when the extension is evaluated.<p>If one of"
+ " <code>root_module_direct_deps</code> and"
+ " <code>root_module_direct_dev_deps</code> is specified, the other has to be"
+ " as well. The lists specified by these two parameters must be"
+ " disjoint.<p>Exactly one of <code>root_module_direct_deps</code> and"
+ " <code>root_module_direct_dev_deps</code> can be set to the special value"
+ " <code>\"all\"</code>, which is treated as if a list with the names of"
+ " all repositories generated by the extension was specified as the value.",
positional = false,
named = true,
defaultValue = "None",
allowedTypes = {
@ParamType(type = Sequence.class, generic1 = String.class),
@ParamType(type = String.class),
@ParamType(type = NoneType.class)
}),
})
public ModuleExtensionMetadata extensionMetadata(
Object rootModuleDirectDepsUnchecked, Object rootModuleDirectDevDepsUnchecked)
throws EvalException {
return ModuleExtensionMetadata.create(
rootModuleDirectDepsUnchecked, rootModuleDirectDevDepsUnchecked, extensionId);
}
}