blob: fabe2b5fc000bfd618485250e9aca5721d09d7b5 [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
Lukacs Berkie19ee272015-12-10 11:34:29 +000016import com.google.devtools.build.lib.cmdline.Label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Mark Schaller6df81792015-12-10 18:47:47 +000018import com.google.devtools.build.lib.util.Preconditions;
Ulf Adamsef7e0452015-12-21 09:26:43 +000019import com.google.devtools.build.lib.vfs.Path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.devtools.build.lib.vfs.RootedPath;
21import com.google.devtools.build.skyframe.SkyFunction;
22
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import java.util.concurrent.atomic.AtomicReference;
24
25/** Common utilities for dealing with files outside the package roots. */
Nathan Harmata029de3d2015-07-27 18:08:09 +000026public class ExternalFilesHelper {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027 private final AtomicReference<PathPackageLocator> pkgLocator;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000028 private final ExternalFileAction externalFileAction;
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000029
Janak Ramakrishnan83431062015-12-08 18:42:16 +000030 // This variable is set to true from multiple threads, but only read once, in the main thread.
31 // So volatility or an AtomicBoolean is not needed.
32 private boolean externalFileSeen = false;
33
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000034 /**
35 * @param pkgLocator an {@link AtomicReference} to a {@link PathPackageLocator} used to
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000036 * determine what files are internal.
37 * @param errorOnExternalFiles If files outside of package paths should be allowed.
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000038 */
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000039 public ExternalFilesHelper(
40 AtomicReference<PathPackageLocator> pkgLocator, boolean errorOnExternalFiles) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010041 this.pkgLocator = pkgLocator;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000042 if (errorOnExternalFiles) {
43 this.externalFileAction = ExternalFileAction.ERROR_OUT;
44 } else {
45 this.externalFileAction = ExternalFileAction.DEPEND_ON_EXTERNAL_PKG;
46 }
47 }
48
49 private enum ExternalFileAction {
50 // Re-check the files when the WORKSPACE file changes.
51 DEPEND_ON_EXTERNAL_PKG,
52
53 // Throw an exception if there is an external file.
54 ERROR_OUT,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055 }
56
Janak Ramakrishnan83431062015-12-08 18:42:16 +000057 boolean isExternalFileSeen() {
58 return externalFileSeen;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010059 }
60
Janak Ramakrishnan83431062015-12-08 18:42:16 +000061 static boolean isInternal(RootedPath rootedPath, PathPackageLocator packageLocator) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010062 // TODO(bazel-team): This is inefficient when there are a lot of package roots or there are a
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000063 // lot of external directories. Consider either explicitly preventing this case or using a more
64 // efficient approach here (e.g. use a trie for determining if a file is under an external
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010065 // directory).
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000066 return packageLocator.getPathEntries().contains(rootedPath.getRoot());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067 }
68
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000069 /**
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000070 * If this instance is configured with DEPEND_ON_EXTERNAL_PKG and rootedPath is a file that isn't
71 * under a package root then this adds a dependency on the //external package. If the action is
72 * ERROR_OUT, it will throw an error instead.
Michajlo Matijkiwdb110942015-03-31 23:41:02 +000073 */
74 public void maybeHandleExternalFile(RootedPath rootedPath, SkyFunction.Environment env)
75 throws FileOutsidePackageRootsException {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000076 if (isInternal(rootedPath, pkgLocator.get())) {
77 return;
78 }
79
Janak Ramakrishnan83431062015-12-08 18:42:16 +000080 externalFileSeen = true;
Lukacs Berkide2183d2015-12-16 11:25:36 +000081 if (externalFileAction == ExternalFileAction.ERROR_OUT) {
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000082 throw new FileOutsidePackageRootsException(rootedPath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083 }
Lukacs Berkide2183d2015-12-16 11:25:36 +000084
Ulf Adamsef7e0452015-12-21 09:26:43 +000085 // The outputBase may be null if we're not actually running a build.
86 Path outputBase = pkgLocator.get().getOutputBase();
87 if (outputBase != null && !rootedPath.asPath().startsWith(
Lukacs Berkide2183d2015-12-16 11:25:36 +000088 pkgLocator.get().getOutputBase().getRelative(Label.EXTERNAL_PATH_PREFIX))) {
89 return;
90 }
91
92 // For files that are under $OUTPUT_BASE/external, add a dependency on the //external package
93 // so that if the WORKSPACE file changes, the File/DirectoryStateValue will be re-evaluated.
94 //
95 // Note that:
96 // - We don't add a dependency on the parent directory at the package root boundary, so
97 // the only transitive dependencies from files inside the package roots to external files
98 // are through symlinks. So the upwards transitive closure of external files is small.
99 // - The only way other than external repositories for external source files to get into the
100 // skyframe graph in the first place is through symlinks outside the package roots, which we
101 // neither want to encourage nor optimize for since it is not common. So the set of external
102 // files is small.
103 PackageValue pkgValue = (PackageValue) env.getValue(PackageValue.key(
104 Label.EXTERNAL_PACKAGE_IDENTIFIER));
105 if (pkgValue == null) {
106 return;
107 }
108 Preconditions.checkState(!pkgValue.getPackage().containsErrors());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100109 }
110}