blob: 8b41b8075f99190264ebe700ddbd6f4fa2dbb075 [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;
John Field585d1a02015-12-16 16:03:52 +000025import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
vladmos46907932017-06-30 14:01:45 +020026import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
John Field585d1a02015-12-16 16:03:52 +000027import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import java.util.Collection;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000030import java.util.Collections;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import java.util.LinkedHashSet;
32import java.util.List;
Jingwen Chen91b867f2018-05-11 08:56:06 -070033import java.util.Locale;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035import javax.annotation.Nullable;
36
37/**
38 * FilesetEntry is a value object used to represent a "FilesetEntry" inside a "Fileset" BUILD rule.
39 */
Lukacs Berkide27f9e2015-09-17 12:18:45 +000040@SkylarkModule(
41 name = "FilesetEntry",
42 doc = "",
43 documented = false)
Laszlo Csomorb136315c2016-01-19 14:03:29 +000044@Immutable
45@ThreadSafe
Lukacs Berkide27f9e2015-09-17 12:18:45 +000046public final class FilesetEntry implements SkylarkValue {
47
Laszlo Csomorb136315c2016-01-19 14:03:29 +000048 public static final SymlinkBehavior DEFAULT_SYMLINK_BEHAVIOR = SymlinkBehavior.COPY;
49 public static final String DEFAULT_STRIP_PREFIX = ".";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +000050 public static final String STRIP_PREFIX_WORKSPACE = "%workspace%";
Laszlo Csomorb136315c2016-01-19 14:03:29 +000051
Lukacs Berkide27f9e2015-09-17 12:18:45 +000052 @Override
53 public boolean isImmutable() {
Laszlo Csomorb136315c2016-01-19 14:03:29 +000054 // TODO(laszlocsomor): set this to true. I think we could do this right now, but am not sure.
55 // Maybe we have to verify that Skylark recognizes every member's type to be recursively
56 // immutable; as of 15/01/2016 this is not true for enum types in general, to name an example.
Lukacs Berkide27f9e2015-09-17 12:18:45 +000057 return false;
58 }
59
Lukacs Berki6e91eb92015-09-21 09:12:37 +000060 public static List<String> makeStringList(List<Label> labels) {
61 if (labels == null) {
62 return Collections.emptyList();
63 }
64 List<String> strings = Lists.newArrayListWithCapacity(labels.size());
65 for (Label label : labels) {
66 strings.add(label.toString());
67 }
68 return strings;
69 }
70
71 public static List<?> makeList(Collection<?> list) {
72 return list == null ? Lists.newArrayList() : Lists.newArrayList(list);
73 }
74
Lukacs Berkide27f9e2015-09-17 12:18:45 +000075 @Override
vladmos46907932017-06-30 14:01:45 +020076 public void repr(SkylarkPrinter printer) {
77 printer.append("FilesetEntry(srcdir = ");
78 printer.repr(getSrcLabel().toString());
79 printer.append(", files = ");
80 printer.repr(makeStringList(getFiles()));
81 printer.append(", excludes = ");
82 printer.repr(makeList(getExcludes()));
83 printer.append(", destdir = ");
84 printer.repr(getDestDir().getPathString());
85 printer.append(", strip_prefix = ");
86 printer.repr(getStripPrefix());
87 printer.append(", symlinks = ");
88 printer.repr(getSymlinkBehavior().toString());
89 printer.append(")");
Lukacs Berkide27f9e2015-09-17 12:18:45 +000090 }
91
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092 /** SymlinkBehavior decides what to do when a source file of a FilesetEntry is a symlink. */
Laszlo Csomorb136315c2016-01-19 14:03:29 +000093 @Immutable
94 @ThreadSafe
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095 public enum SymlinkBehavior {
96 /** Just copies the symlink as-is. May result in dangling links. */
97 COPY,
98 /** Follow the link and make the destination point to the absolute path of the final target. */
99 DEREFERENCE;
100
101 public static SymlinkBehavior parse(String value) throws IllegalArgumentException {
Jingwen Chen91b867f2018-05-11 08:56:06 -0700102 return valueOf(value.toUpperCase(Locale.ENGLISH));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100103 }
104
105 @Override
106 public String toString() {
107 return super.toString().toLowerCase();
108 }
109 }
110
111 private final Label srcLabel;
112 @Nullable private final ImmutableList<Label> files;
113 @Nullable private final ImmutableSet<String> excludes;
114 private final PathFragment destDir;
115 private final SymlinkBehavior symlinkBehavior;
116 private final String stripPrefix;
117
118 /**
119 * Constructs a FilesetEntry with the given values.
120 *
121 * @param srcLabel the label of the source directory. Must be non-null.
122 * @param files The explicit files to include. May be null.
123 * @param excludes The files to exclude. Man be null. May only be non-null if files is null.
124 * @param destDir The target-relative output directory.
125 * @param symlinkBehavior how to treat symlinks on the input. See
126 * {@link FilesetEntry.SymlinkBehavior}.
127 * @param stripPrefix the prefix to strip from the package-relative path. If ".", keep only the
128 * basename.
129 */
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000130 public FilesetEntry(
131 Label srcLabel,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100132 @Nullable List<Label> files,
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000133 @Nullable Collection<String> excludes,
134 @Nullable String destDir,
135 @Nullable SymlinkBehavior symlinkBehavior,
136 @Nullable String stripPrefix) {
137 this.srcLabel = Preconditions.checkNotNull(srcLabel);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100138 this.files = files == null ? null : ImmutableList.copyOf(files);
139 this.excludes = (excludes == null || excludes.isEmpty()) ? null : ImmutableSet.copyOf(excludes);
nharmatab4060b62017-04-04 17:11:39 +0000140 this.destDir = PathFragment.create((destDir == null) ? "" : destDir);
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000141 this.symlinkBehavior = symlinkBehavior == null ? DEFAULT_SYMLINK_BEHAVIOR : symlinkBehavior;
142 this.stripPrefix = stripPrefix == null ? DEFAULT_STRIP_PREFIX : stripPrefix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 }
144
145 /**
146 * @return the source label.
147 */
148 public Label getSrcLabel() {
149 return srcLabel;
150 }
151
152 /**
153 * @return the destDir. Non null.
154 */
155 public PathFragment getDestDir() {
156 return destDir;
157 }
158
159 /**
160 * @return how symlinks should be handled.
161 */
162 public SymlinkBehavior getSymlinkBehavior() {
163 return symlinkBehavior;
164 }
165
166 /**
kush92a225d2017-08-10 23:24:56 +0200167 * @return an immutable set of excludes. Null if none specified.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100168 */
169 @Nullable
170 public ImmutableSet<String> getExcludes() {
171 return excludes;
172 }
173
174 /**
175 * @return an immutable list of file labels. Null if none specified.
176 */
177 @Nullable
178 public ImmutableList<Label> getFiles() {
179 return files;
180 }
181
182 /**
183 * @return true if this Fileset should get files from the source directory.
184 */
185 public boolean isSourceFileset() {
186 return "BUILD".equals(srcLabel.getName());
187 }
188
189 /**
190 * @return all prerequisite labels in the FilesetEntry.
191 */
192 public Collection<Label> getLabels() {
193 Set<Label> labels = new LinkedHashSet<>();
194 if (files != null) {
195 labels.addAll(files);
196 } else {
197 labels.add(srcLabel);
198 }
199 return labels;
200 }
201
202 /**
203 * @return the prefix that should be stripped from package-relative path names.
204 */
205 public String getStripPrefix() {
206 return stripPrefix;
207 }
208
209 /**
210 * @return null if the entry is valid, and a human-readable error message otherwise.
211 */
212 @Nullable
213 public String validate() {
214 if (excludes != null && files != null) {
215 return "Cannot specify both 'files' and 'excludes' in a FilesetEntry";
216 } else if (files != null && !isSourceFileset()) {
217 return "Cannot specify files with Fileset label '" + srcLabel + "'";
218 } else if (destDir.isAbsolute()) {
219 return "Cannot specify absolute destdir '" + destDir + "'";
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000220 } else if (!stripPrefix.equals(DEFAULT_STRIP_PREFIX) && files == null) {
221 return "If the strip prefix is not \"" + DEFAULT_STRIP_PREFIX + "\", files must be specified";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +0000222 } else if (stripPrefix.startsWith("/")) {
223 return "Cannot specify absolute strip prefix; perhaps you need to use \""
224 + STRIP_PREFIX_WORKSPACE + "\"";
nharmatab4060b62017-04-04 17:11:39 +0000225 } else if (PathFragment.create(stripPrefix).containsUplevelReferences()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100226 return "Strip prefix must not contain uplevel references";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +0000227 } else if (stripPrefix.startsWith("%") && !stripPrefix.startsWith(STRIP_PREFIX_WORKSPACE)) {
228 return "If the strip_prefix starts with \"%\" then it must start with \""
229 + STRIP_PREFIX_WORKSPACE + "\"";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100230 } else {
231 return null;
232 }
233 }
234
235 @Override
236 public String toString() {
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000237 return String.format(
238 "FilesetEntry(srcdir=%s, destdir=%s, strip_prefix=%s, symlinks=%s, "
239 + "%d file(s) and %d excluded)",
240 srcLabel,
241 destDir,
242 stripPrefix,
243 symlinkBehavior,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100244 files != null ? files.size() : 0,
245 excludes != null ? excludes.size() : 0);
246 }
Michajlo Matijkiw586f80f2016-06-09 18:51:18 +0000247
248 @Override
249 public int hashCode() {
250 return Objects.hashCode(srcLabel, files, excludes, destDir, symlinkBehavior, stripPrefix);
251 }
252
253 @Override
254 public boolean equals(Object other) {
255 if (this == other) {
256 return true;
257 }
258
259 if (!(other instanceof FilesetEntry)) {
260 return false;
261 }
262
263 FilesetEntry that = (FilesetEntry) other;
264 return Objects.equal(srcLabel, that.srcLabel)
265 && Objects.equal(files, that.files)
266 && Objects.equal(excludes, that.excludes)
267 && Objects.equal(destDir, that.destDir)
268 && Objects.equal(symlinkBehavior, that.symlinkBehavior)
269 && Objects.equal(stripPrefix, that.stripPrefix);
270 }
271
272
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100273}