Skip to content

Commit 3ea18cc

Browse files
ShreeM01fmeumkeertk
authored
[6.2.0]Add module_ctx.is_dev_dependency (#17934)
* Add `module_ctx.is_dev_dependency` Allows module extensions to determine whether a given tag represents a dev dependency. Fixes #17101 Work towards #17908 Closes #17909. PiperOrigin-RevId: 520645663 Change-Id: I3e3136a09d01d25fc706bcd0dfd7e53b6e7d5285 * Revert section that was accidentally cherry-picked --------- Co-authored-by: Fabian Meumertzheim <[email protected]> Co-authored-by: keertk <[email protected]>
1 parent d24f7cb commit 3ea18cc

File tree

8 files changed

+213
-67
lines changed

8 files changed

+213
-67
lines changed

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionContext.java

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.google.devtools.build.skyframe.SkyFunction.Environment;
2525
import java.util.Map;
2626
import javax.annotation.Nullable;
27+
import net.starlark.java.annot.Param;
28+
import net.starlark.java.annot.ParamType;
2729
import net.starlark.java.annot.StarlarkBuiltin;
2830
import net.starlark.java.annot.StarlarkMethod;
2931
import net.starlark.java.eval.EvalException;
@@ -99,4 +101,20 @@ protected ImmutableMap<String, String> getRemoteExecProperties() throws EvalExce
99101
public StarlarkList<StarlarkBazelModule> getModules() {
100102
return modules;
101103
}
104+
105+
@StarlarkMethod(
106+
name = "is_dev_dependency",
107+
doc =
108+
"Returns whether the given tag was specified on the result of a <a "
109+
+ "href=\"globals.html#use_extension\">use_extension</a> call with "
110+
+ "<code>devDependency = True</code>.",
111+
parameters = {
112+
@Param(
113+
name = "tag",
114+
doc = "A tag obtained from <a href=\"bazel_module.html#tags\">bazel_module.tags</a>.",
115+
allowedTypes = {@ParamType(type = TypeCheckedTag.class)})
116+
})
117+
public boolean isDevDependency(TypeCheckedTag tag) {
118+
return tag.isDevDependency();
119+
}
102120
}

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java

+76-57
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.common.collect.ImmutableList;
2626
import com.google.common.collect.ImmutableMap;
2727
import com.google.devtools.build.docgen.annot.DocumentMethods;
28+
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileGlobals.ModuleExtensionUsageBuilder.ModuleExtensionProxy;
2829
import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException;
2930
import com.google.devtools.build.lib.cmdline.RepositoryName;
3031
import java.util.ArrayList;
@@ -63,7 +64,7 @@ public class ModuleFileGlobals {
6364
private final boolean ignoreDevDeps;
6465
private final Module.Builder module;
6566
private final Map<String, ModuleKey> deps = new LinkedHashMap<>();
66-
private final List<ModuleExtensionProxy> extensionProxies = new ArrayList<>();
67+
private final List<ModuleExtensionUsageBuilder> extensionUsageBuilders = new ArrayList<>();
6768
private final Map<String, ModuleOverride> overrides = new HashMap<>();
6869
private final Map<String, RepoNameUsage> repoNameUsages = new HashMap<>();
6970

@@ -373,38 +374,37 @@ public void registerToolchains(Sequence<?> toolchainLabels) throws EvalException
373374
},
374375
useStarlarkThread = true)
375376
public ModuleExtensionProxy useExtension(
376-
String extensionBzlFile, String extensionName, boolean devDependency, StarlarkThread thread)
377-
throws EvalException {
378-
ModuleExtensionProxy newProxy =
379-
new ModuleExtensionProxy(extensionBzlFile, extensionName, thread.getCallerLocation());
377+
String extensionBzlFile, String extensionName, boolean devDependency, StarlarkThread thread) {
378+
ModuleExtensionUsageBuilder newUsageBuilder =
379+
new ModuleExtensionUsageBuilder(
380+
extensionBzlFile, extensionName, thread.getCallerLocation());
380381

381382
if (ignoreDevDeps && devDependency) {
382383
// This is a no-op proxy.
383-
return newProxy;
384+
return newUsageBuilder.getProxy(devDependency);
384385
}
385386

386-
// Find an existing proxy object corresponding to this extension.
387-
for (ModuleExtensionProxy proxy : extensionProxies) {
388-
if (proxy.extensionBzlFile.equals(extensionBzlFile)
389-
&& proxy.extensionName.equals(extensionName)) {
390-
return proxy;
387+
// Find an existing usage builder corresponding to this extension.
388+
for (ModuleExtensionUsageBuilder usageBuilder : extensionUsageBuilders) {
389+
if (usageBuilder.extensionBzlFile.equals(extensionBzlFile)
390+
&& usageBuilder.extensionName.equals(extensionName)) {
391+
return usageBuilder.getProxy(devDependency);
391392
}
392393
}
393394

394395
// If no such proxy exists, we can just use a new one.
395-
extensionProxies.add(newProxy);
396-
return newProxy;
396+
extensionUsageBuilders.add(newUsageBuilder);
397+
return newUsageBuilder.getProxy(devDependency);
397398
}
398399

399-
@StarlarkBuiltin(name = "module_extension_proxy", documented = false)
400-
class ModuleExtensionProxy implements Structure {
400+
class ModuleExtensionUsageBuilder {
401401
private final String extensionBzlFile;
402402
private final String extensionName;
403403
private final Location location;
404404
private final HashBiMap<String, String> imports;
405405
private final ImmutableList.Builder<Tag> tags;
406406

407-
ModuleExtensionProxy(String extensionBzlFile, String extensionName, Location location) {
407+
ModuleExtensionUsageBuilder(String extensionBzlFile, String extensionName, Location location) {
408408
this.extensionBzlFile = extensionBzlFile;
409409
this.extensionName = extensionName;
410410
this.location = location;
@@ -422,50 +422,69 @@ ModuleExtensionUsage buildUsage() {
422422
.build();
423423
}
424424

425-
void addImport(String localRepoName, String exportedName, Location location)
426-
throws EvalException {
427-
RepositoryName.validateUserProvidedRepoName(localRepoName);
428-
RepositoryName.validateUserProvidedRepoName(exportedName);
429-
addRepoNameUsage(localRepoName, "by a use_repo() call", location);
430-
if (imports.containsValue(exportedName)) {
431-
String collisionRepoName = imports.inverse().get(exportedName);
432-
throw Starlark.errorf(
433-
"The repo exported as '%s' by module extension '%s' is already imported at %s",
434-
exportedName, extensionName, repoNameUsages.get(collisionRepoName).getWhere());
435-
}
436-
imports.put(localRepoName, exportedName);
425+
/**
426+
* Creates a proxy with the specified dev_dependency bit that shares accumulated imports and
427+
* tags with all other such proxies, thus preserving their order across dev/non-dev deps.
428+
*/
429+
ModuleExtensionProxy getProxy(boolean devDependency) {
430+
return new ModuleExtensionProxy(devDependency);
437431
}
438432

439-
@Nullable
440-
@Override
441-
public Object getValue(String tagName) throws EvalException {
442-
return new StarlarkValue() {
443-
@StarlarkMethod(
444-
name = "call",
445-
selfCall = true,
446-
documented = false,
447-
extraKeywords = @Param(name = "kwargs"),
448-
useStarlarkThread = true)
449-
public void call(Dict<String, Object> kwargs, StarlarkThread thread) {
450-
tags.add(
451-
Tag.builder()
452-
.setTagName(tagName)
453-
.setAttributeValues(kwargs)
454-
.setLocation(thread.getCallerLocation())
455-
.build());
433+
@StarlarkBuiltin(name = "module_extension_proxy", documented = false)
434+
class ModuleExtensionProxy implements Structure {
435+
436+
private final boolean devDependency;
437+
438+
private ModuleExtensionProxy(boolean devDependency) {
439+
this.devDependency = devDependency;
440+
}
441+
442+
void addImport(String localRepoName, String exportedName, Location location)
443+
throws EvalException {
444+
RepositoryName.validateUserProvidedRepoName(localRepoName);
445+
RepositoryName.validateUserProvidedRepoName(exportedName);
446+
addRepoNameUsage(localRepoName, "by a use_repo() call", location);
447+
if (imports.containsValue(exportedName)) {
448+
String collisionRepoName = imports.inverse().get(exportedName);
449+
throw Starlark.errorf(
450+
"The repo exported as '%s' by module extension '%s' is already imported at %s",
451+
exportedName, extensionName, repoNameUsages.get(collisionRepoName).getWhere());
456452
}
457-
};
458-
}
453+
imports.put(localRepoName, exportedName);
454+
}
459455

460-
@Override
461-
public ImmutableCollection<String> getFieldNames() {
462-
return ImmutableList.of();
463-
}
456+
@Nullable
457+
@Override
458+
public Object getValue(String tagName) throws EvalException {
459+
return new StarlarkValue() {
460+
@StarlarkMethod(
461+
name = "call",
462+
selfCall = true,
463+
documented = false,
464+
extraKeywords = @Param(name = "kwargs"),
465+
useStarlarkThread = true)
466+
public void call(Dict<String, Object> kwargs, StarlarkThread thread) {
467+
tags.add(
468+
Tag.builder()
469+
.setTagName(tagName)
470+
.setAttributeValues(kwargs)
471+
.setDevDependency(devDependency)
472+
.setLocation(thread.getCallerLocation())
473+
.build());
474+
}
475+
};
476+
}
464477

465-
@Nullable
466-
@Override
467-
public String getErrorMessageForUnknownField(String field) {
468-
return null;
478+
@Override
479+
public ImmutableCollection<String> getFieldNames() {
480+
return ImmutableList.of();
481+
}
482+
483+
@Nullable
484+
@Override
485+
public String getErrorMessageForUnknownField(String field) {
486+
return null;
487+
}
469488
}
470489
}
471490

@@ -821,8 +840,8 @@ public Module buildModule() {
821840
.setDeps(ImmutableMap.copyOf(deps))
822841
.setOriginalDeps(ImmutableMap.copyOf(deps))
823842
.setExtensionUsages(
824-
extensionProxies.stream()
825-
.map(ModuleExtensionProxy::buildUsage)
843+
extensionUsageBuilders.stream()
844+
.map(ModuleExtensionUsageBuilder::buildUsage)
826845
.collect(toImmutableList()))
827846
.build();
828847
}

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Tag.java

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public abstract class Tag {
3333
/** All keyword arguments supplied to the tag instance. */
3434
public abstract Dict<String, Object> getAttributeValues();
3535

36+
/** Whether this tag was created using a proxy created with dev_dependency = True. */
37+
public abstract boolean isDevDependency();
38+
3639
/** The source location in the module file where this tag was created. */
3740
public abstract Location getLocation();
3841

@@ -48,6 +51,8 @@ public abstract static class Builder {
4851

4952
public abstract Builder setAttributeValues(Dict<String, Object> value);
5053

54+
public abstract Builder setDevDependency(boolean value);
55+
5156
public abstract Builder setLocation(Location value);
5257

5358
public abstract Tag build();

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/TypeCheckedTag.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
public class TypeCheckedTag implements Structure {
3535
private final TagClass tagClass;
3636
private final Object[] attrValues;
37+
private final boolean devDependency;
3738

38-
private TypeCheckedTag(TagClass tagClass, Object[] attrValues) {
39+
private TypeCheckedTag(TagClass tagClass, Object[] attrValues, boolean devDependency) {
3940
this.tagClass = tagClass;
4041
this.attrValues = attrValues;
42+
this.devDependency = devDependency;
4143
}
4244

4345
/** Creates a {@link TypeCheckedTag}. */
@@ -95,7 +97,15 @@ public static TypeCheckedTag create(TagClass tagClass, Tag tag, LabelConverter l
9597
attrValues[i] = Attribute.valueToStarlark(attr.getDefaultValueUnchecked());
9698
}
9799
}
98-
return new TypeCheckedTag(tagClass, attrValues);
100+
return new TypeCheckedTag(tagClass, attrValues, tag.isDevDependency());
101+
}
102+
103+
/**
104+
* Whether the tag was specified on an extension proxy created with <code>dev_dependency=True
105+
* </code>.
106+
*/
107+
public boolean isDevDependency() {
108+
return devDependency;
99109
}
100110

101111
@Override

src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodTestUtil.java

+8
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ public static TagClass createTagClass(Attribute... attrs) {
283283
public static class TestTagBuilder {
284284
private final Dict.Builder<String, Object> attrValuesBuilder = Dict.builder();
285285
private final String tagName;
286+
private boolean devDependency = false;
286287

287288
private TestTagBuilder(String tagName) {
288289
this.tagName = tagName;
@@ -294,11 +295,18 @@ public TestTagBuilder addAttr(String attrName, Object attrValue) {
294295
return this;
295296
}
296297

298+
@CanIgnoreReturnValue
299+
public TestTagBuilder setDevDependency() {
300+
devDependency = true;
301+
return this;
302+
}
303+
297304
public Tag build() {
298305
return Tag.builder()
299306
.setTagName(tagName)
300307
.setLocation(Location.BUILTIN)
301308
.setAttributeValues(attrValuesBuilder.buildImmutable())
309+
.setDevDependency(devDependency)
302310
.build();
303311
}
304312
}

src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ public void multipleModules_devDependency() throws Exception {
446446
" data_str = 'modules:'",
447447
" for mod in ctx.modules:",
448448
" for tag in mod.tags.tag:",
449-
" data_str += ' ' + tag.data",
449+
" data_str += ' ' + tag.data + ' ' + str(ctx.is_dev_dependency(tag))",
450450
" data_repo(name='ext_repo',data=data_str)",
451451
"tag=tag_class(attrs={'data':attr.string()})",
452452
"ext=module_extension(implementation=_ext_impl,tag_classes={'tag':tag})");
@@ -457,7 +457,8 @@ public void multipleModules_devDependency() throws Exception {
457457
if (result.hasError()) {
458458
throw result.getError().getException();
459459
}
460-
assertThat(result.get(skyKey).getModule().getGlobal("data")).isEqualTo("modules: root [email protected]");
460+
assertThat(result.get(skyKey).getModule().getGlobal("data"))
461+
.isEqualTo("modules: root True [email protected] False");
461462
}
462463

463464
@Test
@@ -497,7 +498,7 @@ public void multipleModules_ignoreDevDependency() throws Exception {
497498
" data_str = 'modules:'",
498499
" for mod in ctx.modules:",
499500
" for tag in mod.tags.tag:",
500-
" data_str += ' ' + tag.data",
501+
" data_str += ' ' + tag.data + ' ' + str(ctx.is_dev_dependency(tag))",
501502
" data_repo(name='ext_repo',data=data_str)",
502503
"tag=tag_class(attrs={'data':attr.string()})",
503504
"ext=module_extension(implementation=_ext_impl,tag_classes={'tag':tag})");
@@ -511,7 +512,8 @@ public void multipleModules_ignoreDevDependency() throws Exception {
511512
if (result.hasError()) {
512513
throw result.getError().getException();
513514
}
514-
assertThat(result.get(skyKey).getModule().getGlobal("data")).isEqualTo("modules: [email protected]");
515+
assertThat(result.get(skyKey).getModule().getGlobal("data"))
516+
.isEqualTo("modules: [email protected] False");
515517
}
516518

517519
@Test

0 commit comments

Comments
 (0)