blob: 792cc5fa136e6ac5d416a7289b60e518291b7e26 [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;
26
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000027import java.io.IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import java.util.concurrent.atomic.AtomicReference;
29
30/** Common utilities for dealing with files outside the package roots. */
Nathan Harmata029de3d2015-07-27 18:08:09 +000031public class ExternalFilesHelper {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032 private final AtomicReference<PathPackageLocator> pkgLocator;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000033 private final ExternalFileAction externalFileAction;
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000034 private final BlazeDirectories directories;
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000035
Nathan Harmata60108832016-03-25 08:02:42 +000036 // These variables are set to true from multiple threads, but only read in the main thread.
Janak Ramakrishnan83431062015-12-08 18:42:16 +000037 // So volatility or an AtomicBoolean is not needed.
Nathan Harmata60108832016-03-25 08:02:42 +000038 private boolean anyOutputFilesSeen = false;
39 private boolean anyNonOutputExternalFilesSeen = false;
Janak Ramakrishnan83431062015-12-08 18:42:16 +000040
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000041 /**
42 * @param pkgLocator an {@link AtomicReference} to a {@link PathPackageLocator} used to
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000043 * determine what files are internal.
44 * @param errorOnExternalFiles If files outside of package paths should be allowed.
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000045 */
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000046 public ExternalFilesHelper(
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000047 AtomicReference<PathPackageLocator> pkgLocator, boolean errorOnExternalFiles,
48 BlazeDirectories directories) {
Nathan Harmata60108832016-03-25 08:02:42 +000049 this(
50 pkgLocator,
51 errorOnExternalFiles
52 ? ExternalFileAction.ERROR_OUT
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000053 : ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_FILES,
54 directories);
Nathan Harmata60108832016-03-25 08:02:42 +000055 }
56
57 private ExternalFilesHelper(AtomicReference<PathPackageLocator> pkgLocator,
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000058 ExternalFileAction externalFileAction,
59 BlazeDirectories directories) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060 this.pkgLocator = pkgLocator;
Nathan Harmata60108832016-03-25 08:02:42 +000061 this.externalFileAction = externalFileAction;
Kristina Chodorow5a2936f2016-04-22 17:02:19 +000062 this.directories = directories;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000063 }
64
65 private enum ExternalFileAction {
Nathan Harmata60108832016-03-25 08:02:42 +000066 /** Re-check the files when the WORKSPACE file changes. */
67 DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_FILES,
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000068
Nathan Harmata60108832016-03-25 08:02:42 +000069 /** Throw an exception if there is an external file. */
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000070 ERROR_OUT,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071 }
72
Nathan Harmata60108832016-03-25 08:02:42 +000073 enum FileType {
74 /** A file inside the package roots or in an external repository. */
75 INTERNAL,
76
77 /** A file outside the package roots about which we may make no other assumptions. */
78 EXTERNAL_MUTABLE,
79
80 /**
81 * A file in Bazel's output tree that's a proper output of an action (*not* a source file in an
82 * external repository). Such files are theoretically mutable, but certain Blaze flags may tell
83 * Blaze to assume these files are immutable.
84 *
85 * Note that {@link ExternalFilesHelper#maybeHandleExternalFile} is only used for
86 * {@link FileStateValue} and {@link DirectoryStateValue}, and also note that output files do
87 * not normally have corresponding {@link FileValue} instances (and thus also
88 * {@link FileStateValue} instances) in the Skyframe graph ({@link ArtifactFunction} only uses
89 * {@link FileValue}s for source files). But {@link FileStateValue}s for output files can still
90 * make their way into the Skyframe graph if e.g. a source file is a symlink to an output file.
91 */
92 // TODO(nharmata): Consider an alternative design where we have an OutputFileDiffAwareness. This
93 // could work but would first require that we clean up all RootedPath usage.
94 OUTPUT,
95
96 /**
97 * A file in the part of Bazel's output tree that contains (/ symlinks to) to external
98 * repositories.
99 */
100 EXTERNAL_REPO,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 }
102
Nathan Harmata60108832016-03-25 08:02:42 +0000103 static class ExternalFilesKnowledge {
104 final boolean anyOutputFilesSeen;
105 final boolean anyNonOutputExternalFilesSeen;
106
107 private ExternalFilesKnowledge(boolean anyOutputFilesSeen,
108 boolean anyNonOutputExternalFilesSeen) {
109 this.anyOutputFilesSeen = anyOutputFilesSeen;
110 this.anyNonOutputExternalFilesSeen = anyNonOutputExternalFilesSeen;
111 }
112 }
113
114 @ThreadCompatible
115 ExternalFilesKnowledge getExternalFilesKnowledge() {
116 return new ExternalFilesKnowledge(anyOutputFilesSeen, anyNonOutputExternalFilesSeen);
117 }
118
119 @ThreadCompatible
120 void setExternalFilesKnowledge(ExternalFilesKnowledge externalFilesKnowledge) {
121 anyOutputFilesSeen = externalFilesKnowledge.anyOutputFilesSeen;
122 anyNonOutputExternalFilesSeen = externalFilesKnowledge.anyNonOutputExternalFilesSeen;
123 }
124
125 ExternalFilesHelper cloneWithFreshExternalFilesKnowledge() {
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000126 return new ExternalFilesHelper(pkgLocator, externalFileAction, directories);
Nathan Harmata60108832016-03-25 08:02:42 +0000127 }
128
129 FileType getAndNoteFileType(RootedPath rootedPath) {
130 PathPackageLocator packageLocator = pkgLocator.get();
131 if (packageLocator.getPathEntries().contains(rootedPath.getRoot())) {
132 return FileType.INTERNAL;
133 }
134 // The outputBase may be null if we're not actually running a build.
135 Path outputBase = packageLocator.getOutputBase();
136 if (outputBase == null) {
137 anyNonOutputExternalFilesSeen = true;
138 return FileType.EXTERNAL_MUTABLE;
139 }
140 if (rootedPath.asPath().startsWith(outputBase)) {
141 Path externalRepoDir = outputBase.getRelative(Label.EXTERNAL_PATH_PREFIX);
142 if (rootedPath.asPath().startsWith(externalRepoDir)) {
143 anyNonOutputExternalFilesSeen = true;
144 return FileType.EXTERNAL_REPO;
145 } else {
146 anyOutputFilesSeen = true;
147 return FileType.OUTPUT;
148 }
149 }
150 anyNonOutputExternalFilesSeen = true;
151 return FileType.EXTERNAL_MUTABLE;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100152 }
153
Michajlo Matijkiwdb110942015-03-31 23:41:02 +0000154 /**
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000155 * If this instance is configured with DEPEND_ON_EXTERNAL_PKG and rootedPath is a file that isn't
156 * under a package root then this adds a dependency on the //external package. If the action is
157 * ERROR_OUT, it will throw an error instead.
Michajlo Matijkiwdb110942015-03-31 23:41:02 +0000158 */
Nathan Harmata60108832016-03-25 08:02:42 +0000159 @ThreadSafe
Michajlo Matijkiwdb110942015-03-31 23:41:02 +0000160 public void maybeHandleExternalFile(RootedPath rootedPath, SkyFunction.Environment env)
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000161 throws IOException {
Nathan Harmata60108832016-03-25 08:02:42 +0000162 FileType fileType = getAndNoteFileType(rootedPath);
163 if (fileType == FileType.INTERNAL) {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000164 return;
165 }
Nathan Harmata60108832016-03-25 08:02:42 +0000166 if (fileType == FileType.OUTPUT || fileType == FileType.EXTERNAL_MUTABLE) {
167 if (externalFileAction == ExternalFileAction.ERROR_OUT) {
168 throw new FileOutsidePackageRootsException(rootedPath);
169 }
Damien Martin-Guillerez4aa76cc2016-02-16 08:28:30 +0000170 return;
171 }
Nathan Harmata60108832016-03-25 08:02:42 +0000172 Preconditions.checkState(
173 externalFileAction == ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_FILES,
174 externalFileAction);
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000175 RepositoryFunction.addExternalFilesDependencies(rootedPath, directories, env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100176 }
177}