• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.cache;
18 
19 import build.bazel.remote.execution.v2.Digest;
20 import build.bazel.remote.execution.v2.Directory;
21 import build.bazel.remote.execution.v2.DirectoryNode;
22 import build.bazel.remote.execution.v2.FileNode;
23 
24 import com.google.auto.value.AutoValue;
25 
26 import java.io.File;
27 import java.io.IOException;
28 import java.util.Arrays;
29 import java.util.LinkedHashMap;
30 import java.util.TreeSet;
31 
32 /** A merkle tree representation as defined by the remote execution api. */
33 @AutoValue
34 public abstract class MerkleTree {
35 
36     /** Builds a merkle tree for the {@code directory}. */
buildFromDir(File directory)37     public static MerkleTree buildFromDir(File directory) throws IOException {
38         if (!directory.exists() || !directory.isDirectory()) {
39             throw new IllegalArgumentException(
40                     String.format("Directory %s does not exist or is not a Directory!", directory));
41         }
42 
43         LinkedHashMap<Digest, File> digestToFile = new LinkedHashMap<>();
44         LinkedHashMap<Digest, Directory> digestToSubdir = new LinkedHashMap<>();
45         Directory.Builder rootBuilder = Directory.newBuilder();
46 
47         // Sort the files, so that two equivalent directory messages have matching digests.
48         TreeSet<File> files = new TreeSet<>(Arrays.asList(directory.listFiles()));
49         for (File f : files) {
50             if (f.isFile()) {
51                 Digest digest = DigestCalculator.compute(f);
52                 digestToFile.putIfAbsent(digest, f);
53                 rootBuilder.addFiles(
54                         FileNode.newBuilder()
55                                 .setDigest(digest)
56                                 .setName(f.getName())
57                                 .setIsExecutable(f.canExecute()));
58             }
59             if (f.isDirectory()) {
60                 MerkleTree childTree = buildFromDir(f);
61                 rootBuilder.addDirectories(
62                         DirectoryNode.newBuilder()
63                                 .setDigest(childTree.rootDigest())
64                                 .setName(childTree.rootName()));
65                 digestToSubdir.putIfAbsent(childTree.rootDigest(), childTree.root());
66                 childTree.digestToSubdir().forEach(digestToSubdir::putIfAbsent);
67                 childTree.digestToFile().forEach(digestToFile::putIfAbsent);
68             }
69         }
70 
71         Directory root = rootBuilder.build();
72         return new AutoValue_MerkleTree(
73                 directory.getName(),
74                 root,
75                 DigestCalculator.compute(root),
76                 digestToFile,
77                 digestToSubdir);
78     }
79 
80     /** The name of the root {@link Directory} of this Merkle tree. */
rootName()81     public abstract String rootName();
82 
83     /** The root {@link Directory} of this Merkle tree. */
root()84     public abstract Directory root();
85 
86     /**
87      * The {@link Digest} of the root {@link Directory} of this Merkle tree. Note, this is only
88      * consumed by the cache client.
89      */
rootDigest()90     public abstract Digest rootDigest();
91 
92     /** The map of digests to files within this merkle tree. */
digestToFile()93     public abstract LinkedHashMap<Digest, File> digestToFile();
94 
95     /** The map of digests to Sub-directories within this merkle tree. */
digestToSubdir()96     public abstract LinkedHashMap<Digest, Directory> digestToSubdir();
97 }
98