blob: 441bf6939e9e94467d5acf2d3dbe9a3d8e278c35 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.skyframe;
15
tomlua155b532017-11-08 20:12:47 +010016import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
18import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
19import com.google.devtools.build.lib.skyframe.FileStateValue.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.devtools.build.lib.vfs.PathFragment;
21import com.google.devtools.build.lib.vfs.RootedPath;
janakrbfdad902017-05-03 21:38:28 +020022import com.google.devtools.build.skyframe.LegacySkyKey;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import com.google.devtools.build.skyframe.SkyKey;
24import com.google.devtools.build.skyframe.SkyValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import javax.annotation.Nullable;
27
28/**
29 * A value that corresponds to a file (or directory or symlink or non-existent file), fully
30 * accounting for symlinks (e.g. proper dependencies on ancestor symlinks so as to be incrementally
31 * correct). Anything in Skyframe that cares about the fully resolved path of a file (e.g. anything
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000032 * that cares about the contents of a file) should have a dependency on the corresponding {@link
33 * FileValue}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034 *
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000035 * <p>Note that the existence of a file value does not imply that the file exists on the filesystem.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036 * File values for missing files will be created on purpose in order to facilitate incremental
37 * builds in the case those files have reappeared.
38 *
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000039 * <p>This class contains the relevant metadata for a file, although not the contents. Note that
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040 * since a FileValue doesn't store its corresponding SkyKey, it's possible for the FileValues for
41 * two different paths to be the same.
42 *
Janak Ramakrishnanad77f972016-07-29 20:58:42 +000043 * <p>This should not be used for build outputs; use {@link ArtifactSkyKey} to create keys for
44 * those.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045 */
46@Immutable
47@ThreadSafe
48public abstract class FileValue implements SkyValue {
49
Janak Ramakrishnan42a68302015-06-18 19:55:31 +000050 public boolean exists() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051 return realFileStateValue().getType() != Type.NONEXISTENT;
52 }
53
54 public boolean isSymlink() {
55 return false;
56 }
57
58 /**
Nathan Harmatad8b6ff22015-10-20 21:54:34 +000059 * Returns true if this value corresponds to a file or symlink to an existing regular or special
60 * file. If so, its parent directory is guaranteed to exist.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010061 */
62 public boolean isFile() {
Nathan Harmatad8b6ff22015-10-20 21:54:34 +000063 return realFileStateValue().getType() == Type.REGULAR_FILE
64 || realFileStateValue().getType() == Type.SPECIAL_FILE;
65 }
66
67 /**
68 * Returns true if this value corresponds to a file or symlink to an existing special file. If so,
69 * its parent directory is guaranteed to exist.
70 */
71 public boolean isSpecialFile() {
72 return realFileStateValue().getType() == Type.SPECIAL_FILE;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073 }
74
75 /**
76 * Returns true if the file is a directory or a symlink to an existing directory. If so, its
77 * parent directory is guaranteed to exist.
78 */
79 public boolean isDirectory() {
80 return realFileStateValue().getType() == Type.DIRECTORY;
81 }
82
83 /**
84 * Returns the real rooted path of the file, taking ancestor symlinks into account. For example,
85 * the rooted path ['root']/['a/b'] is really ['root']/['c/b'] if 'a' is a symlink to 'b'. Note
86 * that ancestor symlinks outside the root boundary are not taken into consideration.
87 */
88 public abstract RootedPath realRootedPath();
89
Michajlo Matijkiw9880b692015-10-27 23:05:49 +000090 public abstract FileStateValue realFileStateValue();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091
92 /**
93 * Returns the unresolved link target if {@link #isSymlink()}.
94 *
95 * <p>This is useful if the caller wants to, for example, duplicate a relative symlink. An actual
96 * example could be a build rule that copies a set of input files to the output directory, but
97 * upon encountering symbolic links it can decide between copying or following them.
98 */
99 PathFragment getUnresolvedLinkTarget() {
100 throw new IllegalStateException(this.toString());
101 }
102
103 long getSize() {
104 Preconditions.checkState(isFile(), this);
105 return realFileStateValue().getSize();
106 }
107
108 @Nullable
109 byte[] getDigest() {
110 Preconditions.checkState(isFile(), this);
111 return realFileStateValue().getDigest();
112 }
113
114 /**
115 * Returns a key for building a file value for the given root-relative path.
116 */
117 @ThreadSafe
118 public static SkyKey key(RootedPath rootedPath) {
janakrbfdad902017-05-03 21:38:28 +0200119 return LegacySkyKey.create(SkyFunctions.FILE, rootedPath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 }
121
122 /**
123 * Only intended to be used by {@link FileFunction}. Should not be used for symlink cycles.
124 */
125 static FileValue value(RootedPath rootedPath, FileStateValue fileStateValue,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000126 RootedPath realRootedPath, FileStateValue realFileStateValue) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 if (rootedPath.equals(realRootedPath)) {
128 Preconditions.checkState(fileStateValue.getType() != FileStateValue.Type.SYMLINK,
129 "rootedPath: %s, fileStateValue: %s, realRootedPath: %s, realFileStateValue: %s",
130 rootedPath, fileStateValue, realRootedPath, realFileStateValue);
131 return new RegularFileValue(rootedPath, fileStateValue);
132 } else {
133 if (fileStateValue.getType() == FileStateValue.Type.SYMLINK) {
134 return new SymlinkFileValue(realRootedPath, realFileStateValue,
135 fileStateValue.getSymlinkTarget());
136 } else {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000137 return new DifferentRealPathFileValue(
138 realRootedPath, realFileStateValue);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 }
140 }
141 }
142
143 /**
144 * Implementation of {@link FileValue} for files whose fully resolved path is the same as the
145 * requested path. For example, this is the case for the path "foo/bar/baz" if neither 'foo' nor
146 * 'foo/bar' nor 'foo/bar/baz' are symlinks.
147 */
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000148 public static final class RegularFileValue extends FileValue {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149
150 private final RootedPath rootedPath;
151 private final FileStateValue fileStateValue;
152
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000153 public RegularFileValue(RootedPath rootedPath, FileStateValue fileState) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100154 this.rootedPath = Preconditions.checkNotNull(rootedPath);
155 this.fileStateValue = Preconditions.checkNotNull(fileState);
156 }
157
158 @Override
159 public RootedPath realRootedPath() {
160 return rootedPath;
161 }
162
163 @Override
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000164 public FileStateValue realFileStateValue() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100165 return fileStateValue;
166 }
167
168 @Override
169 public boolean equals(Object obj) {
170 if (obj == null) {
171 return false;
172 }
173 if (obj.getClass() != RegularFileValue.class) {
174 return false;
175 }
176 RegularFileValue other = (RegularFileValue) obj;
177 return rootedPath.equals(other.rootedPath) && fileStateValue.equals(other.fileStateValue);
178 }
179
180 @Override
181 public int hashCode() {
182 return Objects.hash(rootedPath, fileStateValue);
183 }
184
185 @Override
186 public String toString() {
187 return rootedPath + ", " + fileStateValue;
188 }
189 }
190
191 /**
192 * Base class for {@link FileValue}s for files whose fully resolved path is different than the
193 * requested path. For example, this is the case for the path "foo/bar/baz" if at least one of
194 * 'foo', 'foo/bar', or 'foo/bar/baz' is a symlink.
195 */
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000196 public static class DifferentRealPathFileValue extends FileValue {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100197
198 protected final RootedPath realRootedPath;
199 protected final FileStateValue realFileStateValue;
200
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000201 public DifferentRealPathFileValue(RootedPath realRootedPath,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000202 FileStateValue realFileStateValue) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100203 this.realRootedPath = Preconditions.checkNotNull(realRootedPath);
204 this.realFileStateValue = Preconditions.checkNotNull(realFileStateValue);
205 }
206
207 @Override
208 public RootedPath realRootedPath() {
209 return realRootedPath;
210 }
211
212 @Override
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000213 public FileStateValue realFileStateValue() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100214 return realFileStateValue;
215 }
216
217 @Override
218 public boolean equals(Object obj) {
219 if (obj == null) {
220 return false;
221 }
222 if (obj.getClass() != DifferentRealPathFileValue.class) {
223 return false;
224 }
225 DifferentRealPathFileValue other = (DifferentRealPathFileValue) obj;
226 return realRootedPath.equals(other.realRootedPath)
227 && realFileStateValue.equals(other.realFileStateValue);
228 }
229
230 @Override
231 public int hashCode() {
232 return Objects.hash(realRootedPath, realFileStateValue);
233 }
234
235 @Override
236 public String toString() {
237 return realRootedPath + ", " + realFileStateValue + " (symlink ancestor)";
238 }
239 }
240
241 /** Implementation of {@link FileValue} for files that are symlinks. */
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000242 public static final class SymlinkFileValue extends DifferentRealPathFileValue {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100243 private final PathFragment linkValue;
244
Michajlo Matijkiw9880b692015-10-27 23:05:49 +0000245 public SymlinkFileValue(RootedPath realRootedPath, FileStateValue realFileState,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000246 PathFragment linkTarget) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100247 super(realRootedPath, realFileState);
248 this.linkValue = linkTarget;
249 }
250
251 @Override
252 public boolean isSymlink() {
253 return true;
254 }
255
256 @Override
257 public PathFragment getUnresolvedLinkTarget() {
258 return linkValue;
259 }
260
261 @Override
262 public boolean equals(Object obj) {
263 if (obj == null) {
264 return false;
265 }
266 if (obj.getClass() != SymlinkFileValue.class) {
267 return false;
268 }
269 SymlinkFileValue other = (SymlinkFileValue) obj;
270 return realRootedPath.equals(other.realRootedPath)
271 && realFileStateValue.equals(other.realFileStateValue)
272 && linkValue.equals(other.linkValue);
273 }
274
275 @Override
276 public int hashCode() {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000277 return Objects.hash(
278 realRootedPath, realFileStateValue, linkValue, Boolean.TRUE);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100279 }
280
281 @Override
282 public String toString() {
283 return String.format("symlink (real_path=%s, real_state=%s, link_value=%s)",
284 realRootedPath, realFileStateValue, linkValue);
285 }
286 }
287}