blob: e757c275742f3d993fbd4268153b519447379980 [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
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000016import com.google.devtools.build.lib.analysis.BlazeDirectories;
Lukacs Berkie19ee272015-12-10 11:34:29 +000017import com.google.devtools.build.lib.cmdline.Label;
Nathan Harmata60108832016-03-25 08:02:42 +000018import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
19import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Damien Martin-Guillerez847a4182016-02-10 12:44:25 +000021import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
Nathan Harmata60108832016-03-25 08:02:42 +000022import com.google.devtools.build.lib.util.Preconditions;
Ulf Adamsef7e0452015-12-21 09:26:43 +000023import com.google.devtools.build.lib.vfs.Path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.devtools.build.lib.vfs.RootedPath;
25import com.google.devtools.build.skyframe.SkyFunction;
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000026import java.io.IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import java.util.concurrent.atomic.AtomicReference;
28
Nathan Harmatad4f75942016-10-18 08:55:17 +000029/** Common utilities for dealing with paths outside the package roots. */
Nathan Harmata029de3d2015-07-27 18:08:09 +000030public class ExternalFilesHelper {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031 private final AtomicReference<PathPackageLocator> pkgLocator;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000032 private final ExternalFileAction externalFileAction;
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000033 private final BlazeDirectories directories;
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000034
Nathan Harmata60108832016-03-25 08:02:42 +000035 // These variables are set to true from multiple threads, but only read in the main thread.
Janak Ramakrishnan83431062015-12-08 18:42:16 +000036 // So volatility or an AtomicBoolean is not needed.
Nathan Harmata60108832016-03-25 08:02:42 +000037 private boolean anyOutputFilesSeen = false;
38 private boolean anyNonOutputExternalFilesSeen = false;
Janak Ramakrishnan83431062015-12-08 18:42:16 +000039
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000040 public ExternalFilesHelper(
Nathan Harmatad4f75942016-10-18 08:55:17 +000041 AtomicReference<PathPackageLocator> pkgLocator,
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000042 ExternalFileAction externalFileAction,
43 BlazeDirectories directories) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044 this.pkgLocator = pkgLocator;
Nathan Harmata60108832016-03-25 08:02:42 +000045 this.externalFileAction = externalFileAction;
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000046 this.directories = directories;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000047 }
48
Nathan Harmatad4f75942016-10-18 08:55:17 +000049 /**
50 * The action to take when an external path is encountered. See {@link FileType} for the
51 * definition of "external".
52 */
53 public enum ExternalFileAction {
54 /**
55 * For paths of type {@link FileType#EXTERNAL_REPO}, introduce a Skyframe dependency on the
56 * 'external' package.
57 *
58 * <p>This is the default for Bazel, since it's required for correctness of the external
59 * repositories feature.
60 */
61 DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS,
Nathan Harmata0c7a42a2016-10-13 18:17:48 +000062
63 /**
Nathan Harmatad4f75942016-10-18 08:55:17 +000064 * For paths of type {@link FileType#EXTERNAL} or {@link FileType#OUTPUT}, assume the path does
65 * not exist and will never exist.
66 */
67 ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS,
68 }
69
70 /** Classification of a path encountered by Bazel. */
71 public enum FileType {
72 /** A path inside the package roots or in an external repository. */
73 INTERNAL,
74
75 /**
76 * A non {@link #EXTERNAL_REPO} path outside the package roots about which we may make no other
77 * assumptions.
78 */
79 EXTERNAL,
80
81 /**
82 * A path in Bazel's output tree that's a proper output of an action (*not* a source file in an
83 * external repository). Such files are theoretically mutable, but certain Bazel flags may tell
84 * Bazel to assume these paths are immutable.
Nathan Harmata60108832016-03-25 08:02:42 +000085 *
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000086 * <p>Note that {@link ExternalFilesHelper#maybeHandleExternalFile} is only used for {@link
87 * FileStateValue} and {@link DirectoryListingStateValue}, and also note that output files do
88 * not normally have corresponding {@link FileValue} instances (and thus also {@link
89 * FileStateValue} instances) in the Skyframe graph ({@link ArtifactFunction} only uses {@link
90 * FileValue}s for source files). But {@link FileStateValue}s for output files can still make
91 * their way into the Skyframe graph if e.g. a source file is a symlink to an output file.
Nathan Harmata60108832016-03-25 08:02:42 +000092 */
93 // TODO(nharmata): Consider an alternative design where we have an OutputFileDiffAwareness. This
94 // could work but would first require that we clean up all RootedPath usage.
95 OUTPUT,
96
97 /**
Nathan Harmatad4f75942016-10-18 08:55:17 +000098 * A path in the part of Bazel's output tree that contains (/ symlinks to) to external
Nathan Harmata60108832016-03-25 08:02:42 +000099 * repositories.
100 */
101 EXTERNAL_REPO,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102 }
103
Nathan Harmatad4f75942016-10-18 08:55:17 +0000104 /**
105 * Thrown by {@link #maybeHandleExternalFile} when an applicable path is processed (see
106 * {@link ExternalFileAction#ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS}.
107 */
108 static class NonexistentImmutableExternalFileException extends Exception {
109 }
110
Nathan Harmata60108832016-03-25 08:02:42 +0000111 static class ExternalFilesKnowledge {
112 final boolean anyOutputFilesSeen;
113 final boolean anyNonOutputExternalFilesSeen;
114
115 private ExternalFilesKnowledge(boolean anyOutputFilesSeen,
116 boolean anyNonOutputExternalFilesSeen) {
117 this.anyOutputFilesSeen = anyOutputFilesSeen;
118 this.anyNonOutputExternalFilesSeen = anyNonOutputExternalFilesSeen;
119 }
120 }
121
122 @ThreadCompatible
123 ExternalFilesKnowledge getExternalFilesKnowledge() {
124 return new ExternalFilesKnowledge(anyOutputFilesSeen, anyNonOutputExternalFilesSeen);
125 }
126
127 @ThreadCompatible
128 void setExternalFilesKnowledge(ExternalFilesKnowledge externalFilesKnowledge) {
129 anyOutputFilesSeen = externalFilesKnowledge.anyOutputFilesSeen;
130 anyNonOutputExternalFilesSeen = externalFilesKnowledge.anyNonOutputExternalFilesSeen;
131 }
132
133 ExternalFilesHelper cloneWithFreshExternalFilesKnowledge() {
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000134 return new ExternalFilesHelper(pkgLocator, externalFileAction, directories);
Nathan Harmata60108832016-03-25 08:02:42 +0000135 }
136
137 FileType getAndNoteFileType(RootedPath rootedPath) {
138 PathPackageLocator packageLocator = pkgLocator.get();
139 if (packageLocator.getPathEntries().contains(rootedPath.getRoot())) {
140 return FileType.INTERNAL;
141 }
142 // The outputBase may be null if we're not actually running a build.
143 Path outputBase = packageLocator.getOutputBase();
144 if (outputBase == null) {
145 anyNonOutputExternalFilesSeen = true;
Nathan Harmatad4f75942016-10-18 08:55:17 +0000146 return FileType.EXTERNAL;
Nathan Harmata60108832016-03-25 08:02:42 +0000147 }
148 if (rootedPath.asPath().startsWith(outputBase)) {
Kristina Chodorowbd016c92016-09-09 14:10:44 +0000149 Path externalRepoDir = outputBase.getRelative(Label.EXTERNAL_PACKAGE_NAME);
Nathan Harmata60108832016-03-25 08:02:42 +0000150 if (rootedPath.asPath().startsWith(externalRepoDir)) {
151 anyNonOutputExternalFilesSeen = true;
152 return FileType.EXTERNAL_REPO;
153 } else {
154 anyOutputFilesSeen = true;
155 return FileType.OUTPUT;
156 }
157 }
158 anyNonOutputExternalFilesSeen = true;
Nathan Harmatad4f75942016-10-18 08:55:17 +0000159 return FileType.EXTERNAL;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100160 }
161
Michajlo Matijkiwdb110942015-03-31 23:41:02 +0000162 /**
Nathan Harmatad4f75942016-10-18 08:55:17 +0000163 * If this instance is configured with
164 * {@link ExternalFileAction#DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS} and
165 * {@code rootedPath} isn't under a package root then this adds a dependency on the //external
166 * package. If the action is
167 * {@link ExternalFileAction#ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS}, it will throw
168 * a {@link NonexistentImmutableExternalFileException} instead.
Michajlo Matijkiwdb110942015-03-31 23:41:02 +0000169 */
Nathan Harmata60108832016-03-25 08:02:42 +0000170 @ThreadSafe
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000171 void maybeHandleExternalFile(RootedPath rootedPath, SkyFunction.Environment env)
Nathan Harmatad4f75942016-10-18 08:55:17 +0000172 throws NonexistentImmutableExternalFileException, IOException, InterruptedException {
Nathan Harmata60108832016-03-25 08:02:42 +0000173 FileType fileType = getAndNoteFileType(rootedPath);
174 if (fileType == FileType.INTERNAL) {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000175 return;
176 }
Nathan Harmatad4f75942016-10-18 08:55:17 +0000177 if (fileType == FileType.OUTPUT || fileType == FileType.EXTERNAL) {
178 if (externalFileAction
179 == ExternalFileAction.ASSUME_NON_EXISTENT_AND_IMMUTABLE_FOR_EXTERNAL_PATHS) {
180 throw new NonexistentImmutableExternalFileException();
Nathan Harmata60108832016-03-25 08:02:42 +0000181 }
Damien Martin-Guillerez4aa76cc2016-02-16 08:28:30 +0000182 return;
183 }
Nathan Harmata60108832016-03-25 08:02:42 +0000184 Preconditions.checkState(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000185 externalFileAction == ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS,
Nathan Harmata60108832016-03-25 08:02:42 +0000186 externalFileAction);
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000187 RepositoryFunction.addExternalFilesDependencies(rootedPath, directories, env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100188 }
189}