Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 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. |
| 14 | package com.google.devtools.build.lib.skyframe; |
| 15 | |
tomlu | a155b53 | 2017-11-08 20:12:47 +0100 | [diff] [blame] | 16 | import com.google.common.base.Preconditions; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 17 | import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| 18 | import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; |
| 19 | import com.google.devtools.build.lib.skyframe.FileStateValue.Type; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 20 | import com.google.devtools.build.lib.vfs.PathFragment; |
| 21 | import com.google.devtools.build.lib.vfs.RootedPath; |
janakr | bfdad90 | 2017-05-03 21:38:28 +0200 | [diff] [blame] | 22 | import com.google.devtools.build.skyframe.LegacySkyKey; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 23 | import com.google.devtools.build.skyframe.SkyKey; |
| 24 | import com.google.devtools.build.skyframe.SkyValue; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 25 | import java.util.Objects; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 26 | import 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 Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 32 | * that cares about the contents of a file) should have a dependency on the corresponding {@link |
| 33 | * FileValue}. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 34 | * |
Janak Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 35 | * <p>Note that the existence of a file value does not imply that the file exists on the filesystem. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 36 | * 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 Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 39 | * <p>This class contains the relevant metadata for a file, although not the contents. Note that |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 40 | * 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 Ramakrishnan | ad77f97 | 2016-07-29 20:58:42 +0000 | [diff] [blame] | 43 | * <p>This should not be used for build outputs; use {@link ArtifactSkyKey} to create keys for |
| 44 | * those. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 45 | */ |
| 46 | @Immutable |
| 47 | @ThreadSafe |
| 48 | public abstract class FileValue implements SkyValue { |
| 49 | |
Janak Ramakrishnan | 42a6830 | 2015-06-18 19:55:31 +0000 | [diff] [blame] | 50 | public boolean exists() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 51 | return realFileStateValue().getType() != Type.NONEXISTENT; |
| 52 | } |
| 53 | |
| 54 | public boolean isSymlink() { |
| 55 | return false; |
| 56 | } |
| 57 | |
| 58 | /** |
Nathan Harmata | d8b6ff2 | 2015-10-20 21:54:34 +0000 | [diff] [blame] | 59 | * 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 Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 61 | */ |
| 62 | public boolean isFile() { |
Nathan Harmata | d8b6ff2 | 2015-10-20 21:54:34 +0000 | [diff] [blame] | 63 | 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 Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 73 | } |
| 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 Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 90 | public abstract FileStateValue realFileStateValue(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 91 | |
| 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) { |
janakr | bfdad90 | 2017-05-03 21:38:28 +0200 | [diff] [blame] | 119 | return LegacySkyKey.create(SkyFunctions.FILE, rootedPath); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 120 | } |
| 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 Chodorow | f9fdc8d | 2015-12-08 12:49:31 +0000 | [diff] [blame] | 126 | RootedPath realRootedPath, FileStateValue realFileStateValue) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 127 | 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 Chodorow | f9fdc8d | 2015-12-08 12:49:31 +0000 | [diff] [blame] | 137 | return new DifferentRealPathFileValue( |
| 138 | realRootedPath, realFileStateValue); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 139 | } |
| 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 Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 148 | public static final class RegularFileValue extends FileValue { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 149 | |
| 150 | private final RootedPath rootedPath; |
| 151 | private final FileStateValue fileStateValue; |
| 152 | |
Michajlo Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 153 | public RegularFileValue(RootedPath rootedPath, FileStateValue fileState) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 154 | 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 Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 164 | public FileStateValue realFileStateValue() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 165 | 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 Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 196 | public static class DifferentRealPathFileValue extends FileValue { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 197 | |
| 198 | protected final RootedPath realRootedPath; |
| 199 | protected final FileStateValue realFileStateValue; |
| 200 | |
Michajlo Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 201 | public DifferentRealPathFileValue(RootedPath realRootedPath, |
Kristina Chodorow | f9fdc8d | 2015-12-08 12:49:31 +0000 | [diff] [blame] | 202 | FileStateValue realFileStateValue) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 203 | 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 Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 213 | public FileStateValue realFileStateValue() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 214 | 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 Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 242 | public static final class SymlinkFileValue extends DifferentRealPathFileValue { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 243 | private final PathFragment linkValue; |
| 244 | |
Michajlo Matijkiw | 9880b69 | 2015-10-27 23:05:49 +0000 | [diff] [blame] | 245 | public SymlinkFileValue(RootedPath realRootedPath, FileStateValue realFileState, |
Kristina Chodorow | f9fdc8d | 2015-12-08 12:49:31 +0000 | [diff] [blame] | 246 | PathFragment linkTarget) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 247 | 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 Chodorow | f9fdc8d | 2015-12-08 12:49:31 +0000 | [diff] [blame] | 277 | return Objects.hash( |
| 278 | realRootedPath, realFileStateValue, linkValue, Boolean.TRUE); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 279 | } |
| 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 | } |