|
75 | 75 | import com.google.devtools.build.lib.actions.Spawn; |
76 | 76 | import com.google.devtools.build.lib.actions.SpawnResult; |
77 | 77 | import com.google.devtools.build.lib.actions.Spawns; |
78 | | -import com.google.devtools.build.lib.actions.UserExecException; |
79 | 78 | import com.google.devtools.build.lib.actions.cache.MetadataInjector; |
80 | 79 | import com.google.devtools.build.lib.analysis.platform.PlatformUtils; |
81 | 80 | import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent; |
82 | 81 | import com.google.devtools.build.lib.events.Event; |
83 | 82 | import com.google.devtools.build.lib.events.Reporter; |
84 | 83 | import com.google.devtools.build.lib.exec.SpawnInputExpander.InputWalker; |
85 | 84 | import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext; |
| 85 | +import com.google.devtools.build.lib.exec.local.LocalEnvProvider; |
86 | 86 | import com.google.devtools.build.lib.profiler.Profiler; |
87 | 87 | import com.google.devtools.build.lib.profiler.ProfilerTask; |
88 | 88 | import com.google.devtools.build.lib.profiler.SilentCloseable; |
|
108 | 108 | import com.google.devtools.build.lib.remote.util.Utils.InMemoryOutput; |
109 | 109 | import com.google.devtools.build.lib.server.FailureDetails.RemoteExecution; |
110 | 110 | import com.google.devtools.build.lib.skyframe.TreeArtifactValue; |
| 111 | +import com.google.devtools.build.lib.util.Fingerprint; |
111 | 112 | import com.google.devtools.build.lib.util.io.FileOutErr; |
112 | 113 | import com.google.devtools.build.lib.vfs.FileSystemUtils; |
113 | 114 | import com.google.devtools.build.lib.vfs.Path; |
114 | 115 | import com.google.devtools.build.lib.vfs.PathFragment; |
| 116 | +import com.google.devtools.build.lib.worker.WorkerKey; |
| 117 | +import com.google.devtools.build.lib.worker.WorkerOptions; |
| 118 | +import com.google.devtools.build.lib.worker.WorkerParser; |
| 119 | +import com.google.devtools.common.options.Options; |
115 | 120 | import com.google.protobuf.ByteString; |
116 | 121 | import com.google.protobuf.ExtensionRegistry; |
117 | 122 | import com.google.protobuf.Message; |
@@ -365,7 +370,12 @@ private SortedMap<PathFragment, ActionInput> buildOutputDirMap(Spawn spawn) { |
365 | 370 | return outputDirMap; |
366 | 371 | } |
367 | 372 |
|
368 | | - private MerkleTree buildInputMerkleTree(Spawn spawn, SpawnExecutionContext context) |
| 373 | + // ???! Add ToolSignature to build input |
| 374 | + private MerkleTree buildInputMerkleTree( |
| 375 | + Spawn spawn, |
| 376 | + SpawnExecutionContext context, |
| 377 | + ToolSignature toolSignature |
| 378 | + ) |
369 | 379 | throws IOException, ForbiddenActionInputException { |
370 | 380 | // Add output directories to inputs so that they are created as empty directories by the |
371 | 381 | // executor. The spec only requires the executor to create the parent directory of an output |
@@ -394,7 +404,13 @@ private MerkleTree buildInputMerkleTree(Spawn spawn, SpawnExecutionContext conte |
394 | 404 | newInputMap.putAll(outputDirMap); |
395 | 405 | inputMap = newInputMap; |
396 | 406 | } |
397 | | - return MerkleTree.build(inputMap, context.getMetadataProvider(), execRoot, digestUtil); |
| 407 | + return MerkleTree.build( |
| 408 | + inputMap, |
| 409 | + toolSignature == null ? ImmutableSet.of() : toolSignature.toolInputs, |
| 410 | + context.getMetadataProvider(), |
| 411 | + execRoot, |
| 412 | + digestUtil |
| 413 | + ); |
398 | 414 | } |
399 | 415 | } |
400 | 416 |
|
@@ -441,12 +457,46 @@ private static ByteString buildSalt(Spawn spawn) { |
441 | 457 |
|
442 | 458 | /** Creates a new {@link RemoteAction} instance from spawn. */ |
443 | 459 | public RemoteAction buildRemoteAction(Spawn spawn, SpawnExecutionContext context) |
444 | | - throws IOException, UserExecException, ForbiddenActionInputException { |
445 | | - final MerkleTree merkleTree = buildInputMerkleTree(spawn, context); |
| 460 | + throws IOException, ExecException, ForbiddenActionInputException, InterruptedException { |
| 461 | + // ???! remove the call to the ToolSignature-less buildInputMerkleTree |
| 462 | + // final MerkleTree merkleTree = buildInputMerkleTree(spawn, context); |
| 463 | + ToolSignature toolSignature = |
| 464 | + remoteOptions.markToolInputs |
| 465 | + && Spawns.supportsWorkers(spawn) |
| 466 | + && !spawn.getToolFiles().isEmpty() |
| 467 | + ? computePersistentWorkerSignature(spawn, context) |
| 468 | + : null; |
| 469 | + final MerkleTree merkleTree = buildInputMerkleTree(spawn, context, toolSignature); |
| 470 | + |
446 | 471 |
|
447 | 472 | // Get the remote platform properties. |
448 | 473 | Platform platform = PlatformUtils.getPlatformProto(spawn, remoteOptions); |
449 | 474 |
|
| 475 | + if (toolSignature != null) { |
| 476 | + |
| 477 | + StringBuilder cmdStr = new StringBuilder(); |
| 478 | + for (String c : toolSignature.command) { |
| 479 | + cmdStr.append(c); |
| 480 | + cmdStr.append(" "); |
| 481 | + } |
| 482 | + cmdStr.deleteCharAt(cmdStr.length()-1); |
| 483 | + |
| 484 | + Map<String, String> platformAdditionalProperties = |
| 485 | + ImmutableMap.of( |
| 486 | + "persistentWorkerKey", toolSignature.toolHash, |
| 487 | + "persistentWorkerCommand", cmdStr.toString() |
| 488 | + ); |
| 489 | + |
| 490 | + platform = |
| 491 | + PlatformUtils.getPlatformProto( |
| 492 | + spawn, |
| 493 | + remoteOptions, |
| 494 | + platformAdditionalProperties |
| 495 | + ); |
| 496 | + } else { |
| 497 | + platform = PlatformUtils.getPlatformProto(spawn, remoteOptions); |
| 498 | + } |
| 499 | + |
450 | 500 | Command command = |
451 | 501 | buildCommand( |
452 | 502 | spawn.getOutputFiles(), |
@@ -484,6 +534,16 @@ public RemoteAction buildRemoteAction(Spawn spawn, SpawnExecutionContext context |
484 | 534 | action, |
485 | 535 | actionKey); |
486 | 536 | } |
| 537 | + |
| 538 | + @Nullable |
| 539 | + private ToolSignature computePersistentWorkerSignature(Spawn spawn, SpawnExecutionContext context) |
| 540 | + throws IOException, ExecException, InterruptedException { |
| 541 | + WorkerParser workerParser = |
| 542 | + new WorkerParser( |
| 543 | + execRoot, Options.getDefaults(WorkerOptions.class), LocalEnvProvider.NOOP, null); |
| 544 | + WorkerKey workerKey = workerParser.compute(spawn, context).getWorkerKey(); |
| 545 | + return new ToolSignature(workerKey); |
| 546 | + } |
487 | 547 |
|
488 | 548 | /** A value class representing the result of remotely executed {@link RemoteAction}. */ |
489 | 549 | public static class RemoteActionResult { |
@@ -1468,4 +1528,34 @@ void report(Event evt) { |
1468 | 1528 | reporter.handle(evt); |
1469 | 1529 | } |
1470 | 1530 | } |
| 1531 | + |
| 1532 | + |
| 1533 | + // ???! "key" field name is kinda... meh so I'll change it. |
| 1534 | + /** |
| 1535 | + * A simple value class combining a hash of the tool inputs (and their digests) as well as a set |
| 1536 | + * of the relative paths of all tool inputs. |
| 1537 | + * |
| 1538 | + * ???! We also need to spell out the actual command. |
| 1539 | + * This effectively means that this class is a subset of WorkerKey |
| 1540 | + */ |
| 1541 | + private static final class ToolSignature { |
| 1542 | + |
| 1543 | + private final ImmutableList<String> command; |
| 1544 | + private final ImmutableMap<String, String> env; |
| 1545 | + private final Set<PathFragment> toolInputs; |
| 1546 | + private final String toolHash; |
| 1547 | + |
| 1548 | + private ToolSignature(WorkerKey workerKey) { |
| 1549 | + this.command = workerKey.getArgs(); |
| 1550 | + this.env = workerKey.getEnv(); |
| 1551 | + this.toolInputs = workerKey.getWorkerFilesWithHashes().keySet(); |
| 1552 | + |
| 1553 | + Fingerprint fingerprint = new Fingerprint(); |
| 1554 | + fingerprint.addBytes(workerKey.getWorkerFilesCombinedHash().asBytes()); |
| 1555 | + fingerprint.addIterableStrings(workerKey.getArgs()); |
| 1556 | + fingerprint.addStringMap(workerKey.getEnv()); |
| 1557 | + |
| 1558 | + this.toolHash = fingerprint.hexDigestAndReset(); |
| 1559 | + } |
| 1560 | + } |
1471 | 1561 | } |
0 commit comments