Skip to content

Commit 64554a3

Browse files
SalmaSamycopybara-github
authored andcommitted
Add 'environ' attribute to module extensions and update lockfile
- Add 'environ' to module extensions - Store the variables and their values in the lockfile - Compare the variables and values with the lockfile and re-evaluate or error if they differ PiperOrigin-RevId: 548964193 Change-Id: Ic2d52fe3332e93095c414d8bca4c9b4312bca8c2
1 parent da22d3f commit 64554a3

File tree

12 files changed

+219
-80
lines changed

12 files changed

+219
-80
lines changed

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,28 @@ public ImmutableList<String> getModuleExtensionDiff(
114114
ImmutableMap<ModuleKey, ModuleExtensionUsage> lockedExtensionUsages,
115115
ModuleExtensionId extensionId,
116116
byte[] transitiveDigest,
117+
ImmutableMap<String, String> envVariables,
117118
ImmutableMap<ModuleKey, ModuleExtensionUsage> extensionUsages) {
118119
ImmutableList.Builder<String> extDiff = new ImmutableList.Builder<>();
119120
if (lockedExtension == null) {
120-
extDiff.add("The module extension '" + extensionId + "' does not exist in the lockfile");
121-
} else {
122-
if (!Arrays.equals(transitiveDigest, lockedExtension.getBzlTransitiveDigest())) {
121+
return extDiff
122+
.add("The module extension '" + extensionId + "' does not exist in the lockfile")
123+
.build();
124+
}
125+
if (!Arrays.equals(transitiveDigest, lockedExtension.getBzlTransitiveDigest())) {
123126
extDiff.add(
124127
"The implementation of the extension '"
125128
+ extensionId
126129
+ "' or one of its transitive .bzl files has changed");
127-
}
128-
if (!extensionUsages.equals(lockedExtensionUsages)) {
129-
extDiff.add("The usages of the extension named '" + extensionId + "' has changed");
130-
}
130+
}
131+
if (!extensionUsages.equals(lockedExtensionUsages)) {
132+
extDiff.add("The usages of the extension '" + extensionId + "' has changed");
133+
}
134+
if (!envVariables.equals(lockedExtension.getEnvVariables())) {
135+
extDiff.add(
136+
"The environment variables the extension '"
137+
+ extensionId
138+
+ "' depends on (or their values) have changed");
131139
}
132140
return extDiff.build();
133141
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ public abstract class LockFileModuleExtension implements Postable {
3131
@SuppressWarnings("mutable")
3232
public abstract byte[] getBzlTransitiveDigest();
3333

34+
public abstract ImmutableMap<String, String> getEnvVariables();
35+
3436
public abstract ImmutableMap<String, RepoSpec> getGeneratedRepoSpecs();
3537

3638
public static LockFileModuleExtension create(
37-
byte[] transitiveDigest, ImmutableMap<String, RepoSpec> generatedRepoSpecs) {
38-
return new AutoValue_LockFileModuleExtension(transitiveDigest, generatedRepoSpecs);
39+
byte[] transitiveDigest,
40+
ImmutableMap<String, String> envVariables,
41+
ImmutableMap<String, RepoSpec> generatedRepoSpecs) {
42+
return new AutoValue_LockFileModuleExtension(
43+
transitiveDigest, envVariables, generatedRepoSpecs);
3944
}
4045
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.devtools.build.lib.bazel.bzlmod;
1616

1717
import com.google.auto.value.AutoValue;
18+
import com.google.common.collect.ImmutableList;
1819
import com.google.common.collect.ImmutableMap;
1920
import com.google.devtools.build.lib.cmdline.Label;
2021
import java.util.Optional;
@@ -44,6 +45,8 @@ public abstract class ModuleExtension implements StarlarkValue {
4445

4546
public abstract Location getLocation();
4647

48+
public abstract ImmutableList<String> getEnvVariables();
49+
4750
public static Builder builder() {
4851
return new AutoValue_ModuleExtension.Builder();
4952
}
@@ -61,6 +64,8 @@ public abstract static class Builder {
6164

6265
public abstract Builder setTagClasses(ImmutableMap<String, TagClass> value);
6366

67+
public abstract Builder setEnvVariables(ImmutableList<String> value);
68+
6469
public abstract ModuleExtension build();
6570
}
6671
}

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.google.devtools.build.lib.cmdline.RepositoryName;
3333
import com.google.devtools.build.lib.events.Event;
3434
import com.google.devtools.build.lib.rules.repository.NeedsSkyframeRestartException;
35+
import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
3536
import com.google.devtools.build.lib.runtime.ProcessWrapper;
3637
import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor;
3738
import com.google.devtools.build.lib.server.FailureDetails.ExternalDeps;
@@ -144,9 +145,16 @@ public SkyValue compute(SkyKey skyKey, Environment env)
144145
Transience.PERSISTENT);
145146
}
146147

147-
// Check the lockfile first for that module extension
148+
ModuleExtension extension = (ModuleExtension) exported;
149+
ImmutableMap<String, String> extensionEnvVars =
150+
RepositoryFunction.getEnvVarValues(env, extension.getEnvVariables());
151+
if (extensionEnvVars == null) {
152+
return null;
153+
}
148154
byte[] bzlTransitiveDigest =
149155
BazelModuleContext.of(bzlLoadValue.getModule()).bzlTransitiveDigest();
156+
157+
// Check the lockfile first for that module extension
150158
LockfileMode lockfileMode = BazelLockFileFunction.LOCKFILE_MODE.get(env);
151159
if (!lockfileMode.equals(LockfileMode.OFF)) {
152160
BazelLockFileValue lockfile = (BazelLockFileValue) env.getValue(BazelLockFileValue.KEY);
@@ -155,14 +163,18 @@ public SkyValue compute(SkyKey skyKey, Environment env)
155163
}
156164
SingleExtensionEvalValue singleExtensionEvalValue =
157165
tryGettingValueFromLockFile(
158-
extensionId, usagesValue, bzlTransitiveDigest, lockfileMode, lockfile);
166+
extensionId,
167+
extensionEnvVars,
168+
usagesValue,
169+
bzlTransitiveDigest,
170+
lockfileMode,
171+
lockfile);
159172
if (singleExtensionEvalValue != null) {
160173
return singleExtensionEvalValue;
161174
}
162175
}
163176

164177
// Run that extension!
165-
ModuleExtension extension = (ModuleExtension) exported;
166178
ImmutableMap<String, RepoSpec> generatedRepoSpecs =
167179
runModuleExtension(
168180
extensionId, extension, usagesValue, bzlLoadValue.getModule(), starlarkSemantics, env);
@@ -177,14 +189,16 @@ public SkyValue compute(SkyKey skyKey, Environment env)
177189
.post(
178190
ModuleExtensionResolutionEvent.create(
179191
extensionId,
180-
LockFileModuleExtension.create(bzlTransitiveDigest, generatedRepoSpecs)));
192+
LockFileModuleExtension.create(
193+
bzlTransitiveDigest, extensionEnvVars, generatedRepoSpecs)));
181194
}
182195
return createSingleExtentionValue(generatedRepoSpecs, usagesValue);
183196
}
184197

185198
@Nullable
186199
private SingleExtensionEvalValue tryGettingValueFromLockFile(
187200
ModuleExtensionId extensionId,
201+
ImmutableMap<String, String> envVariables,
188202
SingleExtensionUsagesValue usagesValue,
189203
byte[] bzlTransitiveDigest,
190204
LockfileMode lockfileMode,
@@ -205,7 +219,8 @@ private SingleExtensionEvalValue tryGettingValueFromLockFile(
205219
// If we have the extension, check if the implementation and usage haven't changed
206220
if (lockedExtension != null
207221
&& Arrays.equals(bzlTransitiveDigest, lockedExtension.getBzlTransitiveDigest())
208-
&& usagesValue.getExtensionUsages().equals(lockedExtensionUsages)) {
222+
&& usagesValue.getExtensionUsages().equals(lockedExtensionUsages)
223+
&& envVariables.equals(lockedExtension.getEnvVariables())) {
209224
return createSingleExtentionValue(lockedExtension.getGeneratedRepoSpecs(), usagesValue);
210225
} else if (lockfileMode.equals(LockfileMode.ERROR)) {
211226
ImmutableList<String> extDiff =
@@ -214,6 +229,7 @@ private SingleExtensionEvalValue tryGettingValueFromLockFile(
214229
lockedExtensionUsages,
215230
extensionId,
216231
bzlTransitiveDigest,
232+
envVariables,
217233
usagesValue.getExtensionUsages());
218234
throw new SingleExtensionEvalFunctionException(
219235
ExternalDepsException.withMessage(

src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ public Object moduleExtension(
291291
StarlarkCallable implementation,
292292
Dict<?, ?> tagClasses, // Dict<String, TagClass>
293293
Object doc, // <String> or Starlark.NONE
294+
Sequence<?> environ, // <String>
294295
StarlarkThread thread)
295296
throws EvalException {
296297
return ModuleExtension.builder()
@@ -300,6 +301,7 @@ public Object moduleExtension(
300301
.setDoc(Starlark.toJavaOptional(doc, String.class))
301302
.setDefiningBzlFileLabel(
302303
BzlInitThreadContext.fromOrFailFunction(thread, "module_extension").getBzlFile())
304+
.setEnvVariables(ImmutableList.copyOf(Sequence.cast(environ, String.class, "environ")))
303305
.setLocation(thread.getCallerLocation())
304306
.build();
305307
}

src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryFunction.java

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.common.annotations.VisibleForTesting;
2121
import com.google.common.base.Preconditions;
2222
import com.google.common.collect.ImmutableList;
23+
import com.google.common.collect.ImmutableMap;
2324
import com.google.common.io.BaseEncoding;
2425
import com.google.devtools.build.lib.actions.FileValue;
2526
import com.google.devtools.build.lib.analysis.BlazeDirectories;
@@ -51,6 +52,7 @@
5152
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
5253
import com.google.devtools.build.skyframe.SkyKey;
5354
import java.io.IOException;
55+
import java.util.Collection;
5456
import java.util.LinkedHashMap;
5557
import java.util.Map;
5658
import java.util.Objects;
@@ -188,9 +190,9 @@ public abstract RepositoryDirectoryValue.Builder fetch(
188190
throws InterruptedException, RepositoryFunctionException;
189191

190192
@SuppressWarnings("unchecked")
191-
private static Iterable<String> getEnviron(Rule rule) {
193+
private static Collection<String> getEnviron(Rule rule) {
192194
if (rule.isAttrDefined("$environ", Type.STRING_LIST)) {
193-
return (Iterable<String>) rule.getAttr("$environ");
195+
return (Collection<String>) rule.getAttr("$environ");
194196
}
195197
return ImmutableList.of();
196198
}
@@ -201,7 +203,7 @@ private static Iterable<String> getEnviron(Rule rule) {
201203
* is needed.
202204
*/
203205
public boolean verifyMarkerData(Rule rule, Map<String, String> markerData, Environment env)
204-
throws InterruptedException, RepositoryFunctionException {
206+
throws InterruptedException {
205207
return verifyEnvironMarkerData(markerData, env, getEnviron(rule))
206208
&& verifyMarkerDataForFiles(rule, markerData, env)
207209
&& verifySemanticsMarkerData(markerData, env);
@@ -306,33 +308,37 @@ public static RootedPath getRootedPathFromLabel(Label label, Environment env)
306308
protected Map<String, String> declareEnvironmentDependencies(
307309
Map<String, String> markerData, Environment env, Iterable<String> keys)
308310
throws InterruptedException {
309-
Map<String, String> environ = ActionEnvironmentFunction.getEnvironmentView(env, keys);
311+
ImmutableMap<String, String> envDep = getEnvVarValues(env, keys);
312+
if (envDep == null) {
313+
return null;
314+
}
315+
// Add the dependencies to the marker file
316+
keys.forEach(key -> markerData.put("ENV:" + key, envDep.get(key)));
317+
return envDep;
318+
}
310319

311-
// Returns true if there is a null value and we need to wait for some dependencies.
320+
@Nullable
321+
public static ImmutableMap<String, String> getEnvVarValues(Environment env, Iterable<String> keys)
322+
throws InterruptedException {
323+
ImmutableMap<String, String> environ = ActionEnvironmentFunction.getEnvironmentView(env, keys);
312324
if (environ == null) {
313325
return null;
314326
}
315-
316327
Map<String, String> repoEnvOverride = PrecomputedValue.REPO_ENV.get(env);
317328
if (repoEnvOverride == null) {
318329
return null;
319330
}
320331

321332
// Only depend on --repo_env values that are specified in the "environ" attribute.
322-
Map<String, String> repoEnv = new LinkedHashMap<String, String>(environ);
333+
ImmutableMap.Builder<String, String> repoEnv = ImmutableMap.builder();
334+
repoEnv.putAll(environ);
323335
for (String key : keys) {
324336
String value = repoEnvOverride.get(key);
325337
if (value != null) {
326338
repoEnv.put(key, value);
327339
}
328340
}
329-
330-
// Add the dependencies to the marker file
331-
for (Map.Entry<String, String> value : repoEnv.entrySet()) {
332-
markerData.put("ENV:" + value.getKey(), value.getValue());
333-
}
334-
335-
return repoEnv;
341+
return repoEnv.buildKeepingLast();
336342
}
337343

338344
/**
@@ -341,9 +347,9 @@ protected Map<String, String> declareEnvironmentDependencies(
341347
* Environment)} function to verify the values for environment variables.
342348
*/
343349
protected boolean verifyEnvironMarkerData(
344-
Map<String, String> markerData, Environment env, Iterable<String> keys)
350+
Map<String, String> markerData, Environment env, Collection<String> keys)
345351
throws InterruptedException {
346-
Map<String, String> environ = ActionEnvironmentFunction.getEnvironmentView(env, keys);
352+
ImmutableMap<String, String> environ = ActionEnvironmentFunction.getEnvironmentView(env, keys);
347353
if (env.valuesMissing()) {
348354
return false; // Returns false so caller knows to return immediately
349355
}
@@ -364,17 +370,18 @@ protected boolean verifyEnvironMarkerData(
364370

365371
// Verify that all environment variable in the marker file are also in keys
366372
for (String key : markerData.keySet()) {
367-
if (key.startsWith("ENV:") && !repoEnv.containsKey(key.substring(4))) {
373+
if (key.startsWith("ENV:") && !keys.contains(key.substring(4))) {
368374
return false;
369375
}
370376
}
377+
371378
// Now verify the values of the marker data
372-
for (Map.Entry<String, String> value : repoEnv.entrySet()) {
373-
if (!markerData.containsKey("ENV:" + value.getKey())) {
379+
for (String key : keys) {
380+
if (!markerData.containsKey("ENV:" + key)) {
374381
return false;
375382
}
376-
String markerValue = markerData.get("ENV:" + value.getKey());
377-
if (!Objects.equals(markerValue, value.getValue())) {
383+
String markerValue = markerData.get("ENV:" + key);
384+
if (!Objects.equals(markerValue, repoEnv.get(key))) {
378385
return false;
379386
}
380387
}
@@ -457,8 +464,7 @@ protected static String getPathAttr(Rule rule) throws RepositoryFunctionExceptio
457464
}
458465

459466
@VisibleForTesting
460-
protected static PathFragment getTargetPath(String userDefinedPath, Path workspace)
461-
throws RepositoryFunctionException {
467+
protected static PathFragment getTargetPath(String userDefinedPath, Path workspace) {
462468
PathFragment pathFragment = PathFragment.create(userDefinedPath);
463469
return workspace.getRelative(pathFragment).asFragment();
464470
}

src/main/java/com/google/devtools/build/lib/skyframe/ActionEnvironmentFunction.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.devtools.build.lib.skyframe;
1616

1717
import com.google.common.collect.ImmutableList;
18+
import com.google.common.collect.ImmutableMap;
1819
import com.google.devtools.build.lib.bugreport.BugReport;
1920
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
2021
import com.google.devtools.build.skyframe.AbstractSkyKey;
@@ -23,8 +24,6 @@
2324
import com.google.devtools.build.skyframe.SkyKey;
2425
import com.google.devtools.build.skyframe.SkyValue;
2526
import com.google.devtools.build.skyframe.SkyframeLookupResult;
26-
import java.util.Collections;
27-
import java.util.LinkedHashMap;
2827
import java.util.Map;
2928
import javax.annotation.Nullable;
3029

@@ -45,7 +44,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedExcept
4544
return env.getValue(ClientEnvironmentFunction.key(key));
4645
}
4746

48-
/** @return the SkyKey to invoke this function for the environment variable {@code variable}. */
47+
/** Returns the SkyKey to invoke this function for the environment variable {@code variable}. */
4948
public static Key key(String variable) {
5049
return Key.create(variable);
5150
}
@@ -81,8 +80,8 @@ public SkyKeyInterner<Key> getSkyKeyInterner() {
8180
* if and only if some dependencies from Skyframe still need to be resolved.
8281
*/
8382
@Nullable
84-
public static Map<String, String> getEnvironmentView(Environment env, Iterable<String> keys)
85-
throws InterruptedException {
83+
public static ImmutableMap<String, String> getEnvironmentView(
84+
Environment env, Iterable<String> keys) throws InterruptedException {
8685
ImmutableList.Builder<SkyKey> skyframeKeysBuilder = ImmutableList.builder();
8786
for (String key : keys) {
8887
skyframeKeysBuilder.add(key(key));
@@ -92,8 +91,8 @@ public static Map<String, String> getEnvironmentView(Environment env, Iterable<S
9291
if (env.valuesMissing()) {
9392
return null;
9493
}
95-
// To return the initial order and support null values, we use a LinkedHashMap.
96-
LinkedHashMap<String, String> result = new LinkedHashMap<>();
94+
95+
ImmutableMap.Builder<String, String> result = ImmutableMap.builder();
9796
for (SkyKey key : skyframeKeys) {
9897
ClientEnvironmentValue value = (ClientEnvironmentValue) values.get(key);
9998
if (value == null) {
@@ -102,8 +101,10 @@ public static Map<String, String> getEnvironmentView(Environment env, Iterable<S
102101
"ClientEnvironmentValue " + key + " was missing, this should never happen"));
103102
return null;
104103
}
105-
result.put(key.argument().toString(), value.getValue());
104+
if (value.getValue() != null) {
105+
result.put(key.argument().toString(), value.getValue());
106+
}
106107
}
107-
return Collections.unmodifiableMap(result);
108+
return result.buildOrThrow();
108109
}
109110
}

src/main/java/com/google/devtools/build/lib/skyframe/ClientEnvironmentFunction.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ public static Key key(String keyString) {
3030
return ClientEnvironmentFunction.Key.create(keyString);
3131
}
3232

33+
/** The Skyframe key for the client environment function. */
3334
@AutoCodec.VisibleForSerialization
3435
@AutoCodec
35-
static class Key extends AbstractSkyKey<String> {
36+
public static class Key extends AbstractSkyKey<String> {
3637
private static final SkyKeyInterner<Key> interner = SkyKey.newInterner();
3738

3839
private Key(String arg) {
@@ -64,7 +65,7 @@ public ClientEnvironmentFunction(AtomicReference<Map<String, String>> clientEnv)
6465

6566
@Nullable
6667
@Override
67-
public SkyValue compute(SkyKey key, Environment env) throws InterruptedException {
68+
public SkyValue compute(SkyKey key, Environment env) {
6869
return new ClientEnvironmentValue(clientEnv.get().get((String) key.argument()));
6970
}
7071
}

0 commit comments

Comments
 (0)