blob: fb1c50887e9190cebbbfb2889be373ab4fcee27a [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.
14
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000015package com.google.devtools.build.lib.packages;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010016
Michajlo Matijkiw586f80f2016-06-09 18:51:18 +000017import com.google.common.base.Objects;
tomlua155b532017-11-08 20:12:47 +010018import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableSet;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000021import com.google.common.collect.Lists;
22import com.google.devtools.build.lib.cmdline.Label;
Laszlo Csomorb136315c2016-01-19 14:03:29 +000023import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
24import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
gregce59f5cba2020-07-22 12:18:43 -070025import com.google.devtools.build.lib.starlarkbuildapi.FilesetEntryApi;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import java.util.Collection;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import java.util.LinkedHashSet;
29import java.util.List;
Jingwen Chen91b867f2018-05-11 08:56:06 -070030import java.util.Locale;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032import javax.annotation.Nullable;
ilist56454242021-09-24 01:08:05 -070033import net.starlark.java.annot.StarlarkMethod;
34import net.starlark.java.eval.EvalException;
adonovan450c7ad2020-09-14 13:00:21 -070035import net.starlark.java.eval.Printer;
ilist56454242021-09-24 01:08:05 -070036import net.starlark.java.eval.StarlarkThread;
adonovan450c7ad2020-09-14 13:00:21 -070037import net.starlark.java.eval.StarlarkValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038
39/**
40 * FilesetEntry is a value object used to represent a "FilesetEntry" inside a "Fileset" BUILD rule.
41 */
Laszlo Csomorb136315c2016-01-19 14:03:29 +000042@Immutable
43@ThreadSafe
Googler34f70582019-11-25 12:27:34 -080044public final class FilesetEntry implements StarlarkValue, FilesetEntryApi {
Lukacs Berkide27f9e2015-09-17 12:18:45 +000045
jhorvitz9302ebd2021-06-28 11:09:06 -070046 private static final SymlinkBehavior DEFAULT_SYMLINK_BEHAVIOR = SymlinkBehavior.COPY;
Laszlo Csomorb136315c2016-01-19 14:03:29 +000047 public static final String DEFAULT_STRIP_PREFIX = ".";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +000048 public static final String STRIP_PREFIX_WORKSPACE = "%workspace%";
Laszlo Csomorb136315c2016-01-19 14:03:29 +000049
Lukacs Berkide27f9e2015-09-17 12:18:45 +000050 @Override
51 public boolean isImmutable() {
adonovan121224e2020-05-18 08:39:45 -070052 return true;
Lukacs Berkide27f9e2015-09-17 12:18:45 +000053 }
54
Lukacs Berkide27f9e2015-09-17 12:18:45 +000055 @Override
Googler34f70582019-11-25 12:27:34 -080056 public void repr(Printer printer) {
vladmos46907932017-06-30 14:01:45 +020057 printer.append("FilesetEntry(srcdir = ");
jhorvitz9302ebd2021-06-28 11:09:06 -070058 printer.repr(srcLabel.toString());
vladmos46907932017-06-30 14:01:45 +020059 printer.append(", files = ");
jhorvitz9302ebd2021-06-28 11:09:06 -070060 printer.repr(files == null ? ImmutableList.of() : Lists.transform(files, Label::toString));
vladmos46907932017-06-30 14:01:45 +020061 printer.append(", excludes = ");
jhorvitz9302ebd2021-06-28 11:09:06 -070062 printer.repr(excludes == null ? ImmutableList.of() : excludes.asList());
vladmos46907932017-06-30 14:01:45 +020063 printer.append(", destdir = ");
jhorvitz9302ebd2021-06-28 11:09:06 -070064 printer.repr(destDir.getPathString());
vladmos46907932017-06-30 14:01:45 +020065 printer.append(", strip_prefix = ");
jhorvitz9302ebd2021-06-28 11:09:06 -070066 printer.repr(stripPrefix);
vladmos46907932017-06-30 14:01:45 +020067 printer.append(", symlinks = ");
jhorvitz9302ebd2021-06-28 11:09:06 -070068 printer.repr(symlinkBehavior.toString());
vladmos46907932017-06-30 14:01:45 +020069 printer.append(")");
Lukacs Berkide27f9e2015-09-17 12:18:45 +000070 }
71
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072 /** SymlinkBehavior decides what to do when a source file of a FilesetEntry is a symlink. */
Laszlo Csomorb136315c2016-01-19 14:03:29 +000073 @Immutable
74 @ThreadSafe
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075 public enum SymlinkBehavior {
76 /** Just copies the symlink as-is. May result in dangling links. */
77 COPY,
78 /** Follow the link and make the destination point to the absolute path of the final target. */
79 DEREFERENCE;
80
81 public static SymlinkBehavior parse(String value) throws IllegalArgumentException {
Jingwen Chen91b867f2018-05-11 08:56:06 -070082 return valueOf(value.toUpperCase(Locale.ENGLISH));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083 }
84
85 @Override
86 public String toString() {
87 return super.toString().toLowerCase();
88 }
89 }
90
91 private final Label srcLabel;
92 @Nullable private final ImmutableList<Label> files;
93 @Nullable private final ImmutableSet<String> excludes;
94 private final PathFragment destDir;
95 private final SymlinkBehavior symlinkBehavior;
96 private final String stripPrefix;
97
98 /**
99 * Constructs a FilesetEntry with the given values.
100 *
101 * @param srcLabel the label of the source directory. Must be non-null.
102 * @param files The explicit files to include. May be null.
103 * @param excludes The files to exclude. Man be null. May only be non-null if files is null.
104 * @param destDir The target-relative output directory.
105 * @param symlinkBehavior how to treat symlinks on the input. See
106 * {@link FilesetEntry.SymlinkBehavior}.
107 * @param stripPrefix the prefix to strip from the package-relative path. If ".", keep only the
108 * basename.
109 */
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000110 public FilesetEntry(
111 Label srcLabel,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112 @Nullable List<Label> files,
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000113 @Nullable Collection<String> excludes,
114 @Nullable String destDir,
115 @Nullable SymlinkBehavior symlinkBehavior,
116 @Nullable String stripPrefix) {
117 this.srcLabel = Preconditions.checkNotNull(srcLabel);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 this.files = files == null ? null : ImmutableList.copyOf(files);
119 this.excludes = (excludes == null || excludes.isEmpty()) ? null : ImmutableSet.copyOf(excludes);
nharmatab4060b62017-04-04 17:11:39 +0000120 this.destDir = PathFragment.create((destDir == null) ? "" : destDir);
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000121 this.symlinkBehavior = symlinkBehavior == null ? DEFAULT_SYMLINK_BEHAVIOR : symlinkBehavior;
122 this.stripPrefix = stripPrefix == null ? DEFAULT_STRIP_PREFIX : stripPrefix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100123 }
124
125 /**
126 * @return the source label.
127 */
128 public Label getSrcLabel() {
129 return srcLabel;
130 }
131
ilist56454242021-09-24 01:08:05 -0700132 /** Returns the source label. */
133 @StarlarkMethod(name = "srcdir", documented = false, useStarlarkThread = true)
134 public Label getSrcStarlark(StarlarkThread thread) throws EvalException {
135 BuiltinRestriction.throwIfNotBuiltinUsage(thread);
136 return getSrcLabel();
137 }
138
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 /**
140 * @return the destDir. Non null.
141 */
142 public PathFragment getDestDir() {
143 return destDir;
144 }
145
ilist56454242021-09-24 01:08:05 -0700146 @StarlarkMethod(name = "destdir", documented = false, useStarlarkThread = true)
147 public String getStarlarkDestDir(StarlarkThread thread) throws EvalException {
148 BuiltinRestriction.throwIfNotBuiltinUsage(thread);
149 return getDestDir().toString();
150 }
151
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100152 /**
153 * @return how symlinks should be handled.
154 */
155 public SymlinkBehavior getSymlinkBehavior() {
156 return symlinkBehavior;
157 }
158
ilist56454242021-09-24 01:08:05 -0700159 @StarlarkMethod(name = "symlinks", documented = false, useStarlarkThread = true)
160 public String getSymlinkBehaviorStarlark(StarlarkThread thread) throws EvalException {
161 BuiltinRestriction.throwIfNotBuiltinUsage(thread);
162 return getSymlinkBehavior().toString();
163 }
164
165 /** Returns an immutable set of excludes. Null if none specified. */
166 @StarlarkMethod(
167 name = "excludes",
168 documented = false,
169 useStarlarkThread = true,
170 allowReturnNones = true)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100171 @Nullable
ilist56454242021-09-24 01:08:05 -0700172 public ImmutableList<String> getExcludesStarlark(StarlarkThread thread) throws EvalException {
173 BuiltinRestriction.throwIfNotBuiltinUsage(thread);
174 return getExcludes() == null ? null : getExcludes().asList();
175 }
176
177 /** Returns an immutable set of excludes. Null if none specified. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100178 public ImmutableSet<String> getExcludes() {
179 return excludes;
180 }
181
ilist56454242021-09-24 01:08:05 -0700182 /** Returns an immutable list of file labels. Null if none specified. */
183 @StarlarkMethod(
184 name = "files",
185 documented = false,
186 useStarlarkThread = true,
187 allowReturnNones = true)
188 @Nullable
189 public ImmutableList<Label> getFilesStarlark(StarlarkThread thread) throws EvalException {
190 BuiltinRestriction.throwIfNotBuiltinUsage(thread);
191 return getFiles();
192 }
193
194 /** Returns an immutable list of file labels. Null if none specified. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100195 @Nullable
196 public ImmutableList<Label> getFiles() {
197 return files;
198 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199 /**
200 * @return true if this Fileset should get files from the source directory.
201 */
202 public boolean isSourceFileset() {
203 return "BUILD".equals(srcLabel.getName());
204 }
205
206 /**
207 * @return all prerequisite labels in the FilesetEntry.
208 */
209 public Collection<Label> getLabels() {
210 Set<Label> labels = new LinkedHashSet<>();
211 if (files != null) {
212 labels.addAll(files);
213 } else {
214 labels.add(srcLabel);
215 }
216 return labels;
217 }
218
ilist56454242021-09-24 01:08:05 -0700219 /** Returns the prefix that should be stripped from package-relative path names. */
220 @StarlarkMethod(name = "strip_prefix", documented = false, useStarlarkThread = true)
221 public String getStripPrefixStarlark(StarlarkThread thread) throws EvalException {
222 BuiltinRestriction.throwIfNotBuiltinUsage(thread);
223 return getStripPrefix();
224 }
225
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100226 public String getStripPrefix() {
227 return stripPrefix;
228 }
229
230 /**
231 * @return null if the entry is valid, and a human-readable error message otherwise.
232 */
233 @Nullable
234 public String validate() {
235 if (excludes != null && files != null) {
236 return "Cannot specify both 'files' and 'excludes' in a FilesetEntry";
237 } else if (files != null && !isSourceFileset()) {
238 return "Cannot specify files with Fileset label '" + srcLabel + "'";
239 } else if (destDir.isAbsolute()) {
240 return "Cannot specify absolute destdir '" + destDir + "'";
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000241 } else if (!stripPrefix.equals(DEFAULT_STRIP_PREFIX) && files == null) {
242 return "If the strip prefix is not \"" + DEFAULT_STRIP_PREFIX + "\", files must be specified";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +0000243 } else if (stripPrefix.startsWith("/")) {
244 return "Cannot specify absolute strip prefix; perhaps you need to use \""
245 + STRIP_PREFIX_WORKSPACE + "\"";
nharmatab4060b62017-04-04 17:11:39 +0000246 } else if (PathFragment.create(stripPrefix).containsUplevelReferences()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100247 return "Strip prefix must not contain uplevel references";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +0000248 } else if (stripPrefix.startsWith("%") && !stripPrefix.startsWith(STRIP_PREFIX_WORKSPACE)) {
249 return "If the strip_prefix starts with \"%\" then it must start with \""
250 + STRIP_PREFIX_WORKSPACE + "\"";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100251 } else {
252 return null;
253 }
254 }
255
256 @Override
257 public String toString() {
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000258 return String.format(
259 "FilesetEntry(srcdir=%s, destdir=%s, strip_prefix=%s, symlinks=%s, "
260 + "%d file(s) and %d excluded)",
261 srcLabel,
262 destDir,
263 stripPrefix,
264 symlinkBehavior,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100265 files != null ? files.size() : 0,
266 excludes != null ? excludes.size() : 0);
267 }
Michajlo Matijkiw586f80f2016-06-09 18:51:18 +0000268
269 @Override
270 public int hashCode() {
271 return Objects.hashCode(srcLabel, files, excludes, destDir, symlinkBehavior, stripPrefix);
272 }
273
274 @Override
275 public boolean equals(Object other) {
276 if (this == other) {
277 return true;
278 }
279
280 if (!(other instanceof FilesetEntry)) {
281 return false;
282 }
283
284 FilesetEntry that = (FilesetEntry) other;
285 return Objects.equal(srcLabel, that.srcLabel)
286 && Objects.equal(files, that.files)
287 && Objects.equal(excludes, that.excludes)
288 && Objects.equal(destDir, that.destDir)
289 && Objects.equal(symlinkBehavior, that.symlinkBehavior)
290 && Objects.equal(stripPrefix, that.stripPrefix);
291 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100292}