blob: 06b7aa2d1df5255d59ea5662a7eb6e9ebecd8103 [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;
cparsonsc7eef962018-06-11 13:15:43 -070025import com.google.devtools.build.lib.skylarkbuildapi.FilesetEntryApi;
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 */
Laszlo Csomorb136315c2016-01-19 14:03:29 +000040@Immutable
41@ThreadSafe
cparsonsc7eef962018-06-11 13:15:43 -070042public final class FilesetEntry implements SkylarkValue, FilesetEntryApi {
Lukacs Berkide27f9e2015-09-17 12:18:45 +000043
Laszlo Csomorb136315c2016-01-19 14:03:29 +000044 public static final SymlinkBehavior DEFAULT_SYMLINK_BEHAVIOR = SymlinkBehavior.COPY;
45 public static final String DEFAULT_STRIP_PREFIX = ".";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +000046 public static final String STRIP_PREFIX_WORKSPACE = "%workspace%";
Laszlo Csomorb136315c2016-01-19 14:03:29 +000047
Lukacs Berkide27f9e2015-09-17 12:18:45 +000048 @Override
49 public boolean isImmutable() {
Laszlo Csomorb136315c2016-01-19 14:03:29 +000050 // TODO(laszlocsomor): set this to true. I think we could do this right now, but am not sure.
51 // Maybe we have to verify that Skylark recognizes every member's type to be recursively
52 // 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 +000053 return false;
54 }
55
Lukacs Berki6e91eb92015-09-21 09:12:37 +000056 public static List<String> makeStringList(List<Label> labels) {
57 if (labels == null) {
58 return Collections.emptyList();
59 }
60 List<String> strings = Lists.newArrayListWithCapacity(labels.size());
61 for (Label label : labels) {
62 strings.add(label.toString());
63 }
64 return strings;
65 }
66
67 public static List<?> makeList(Collection<?> list) {
68 return list == null ? Lists.newArrayList() : Lists.newArrayList(list);
69 }
70
Lukacs Berkide27f9e2015-09-17 12:18:45 +000071 @Override
vladmos46907932017-06-30 14:01:45 +020072 public void repr(SkylarkPrinter printer) {
73 printer.append("FilesetEntry(srcdir = ");
74 printer.repr(getSrcLabel().toString());
75 printer.append(", files = ");
76 printer.repr(makeStringList(getFiles()));
77 printer.append(", excludes = ");
78 printer.repr(makeList(getExcludes()));
79 printer.append(", destdir = ");
80 printer.repr(getDestDir().getPathString());
81 printer.append(", strip_prefix = ");
82 printer.repr(getStripPrefix());
83 printer.append(", symlinks = ");
84 printer.repr(getSymlinkBehavior().toString());
85 printer.append(")");
Lukacs Berkide27f9e2015-09-17 12:18:45 +000086 }
87
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010088 /** SymlinkBehavior decides what to do when a source file of a FilesetEntry is a symlink. */
Laszlo Csomorb136315c2016-01-19 14:03:29 +000089 @Immutable
90 @ThreadSafe
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091 public enum SymlinkBehavior {
92 /** Just copies the symlink as-is. May result in dangling links. */
93 COPY,
94 /** Follow the link and make the destination point to the absolute path of the final target. */
95 DEREFERENCE;
96
97 public static SymlinkBehavior parse(String value) throws IllegalArgumentException {
Jingwen Chen91b867f2018-05-11 08:56:06 -070098 return valueOf(value.toUpperCase(Locale.ENGLISH));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099 }
100
101 @Override
102 public String toString() {
103 return super.toString().toLowerCase();
104 }
105 }
106
107 private final Label srcLabel;
108 @Nullable private final ImmutableList<Label> files;
109 @Nullable private final ImmutableSet<String> excludes;
110 private final PathFragment destDir;
111 private final SymlinkBehavior symlinkBehavior;
112 private final String stripPrefix;
113
114 /**
115 * Constructs a FilesetEntry with the given values.
116 *
117 * @param srcLabel the label of the source directory. Must be non-null.
118 * @param files The explicit files to include. May be null.
119 * @param excludes The files to exclude. Man be null. May only be non-null if files is null.
120 * @param destDir The target-relative output directory.
121 * @param symlinkBehavior how to treat symlinks on the input. See
122 * {@link FilesetEntry.SymlinkBehavior}.
123 * @param stripPrefix the prefix to strip from the package-relative path. If ".", keep only the
124 * basename.
125 */
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000126 public FilesetEntry(
127 Label srcLabel,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100128 @Nullable List<Label> files,
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000129 @Nullable Collection<String> excludes,
130 @Nullable String destDir,
131 @Nullable SymlinkBehavior symlinkBehavior,
132 @Nullable String stripPrefix) {
133 this.srcLabel = Preconditions.checkNotNull(srcLabel);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 this.files = files == null ? null : ImmutableList.copyOf(files);
135 this.excludes = (excludes == null || excludes.isEmpty()) ? null : ImmutableSet.copyOf(excludes);
nharmatab4060b62017-04-04 17:11:39 +0000136 this.destDir = PathFragment.create((destDir == null) ? "" : destDir);
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000137 this.symlinkBehavior = symlinkBehavior == null ? DEFAULT_SYMLINK_BEHAVIOR : symlinkBehavior;
138 this.stripPrefix = stripPrefix == null ? DEFAULT_STRIP_PREFIX : stripPrefix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 }
140
141 /**
142 * @return the source label.
143 */
144 public Label getSrcLabel() {
145 return srcLabel;
146 }
147
148 /**
149 * @return the destDir. Non null.
150 */
151 public PathFragment getDestDir() {
152 return destDir;
153 }
154
155 /**
156 * @return how symlinks should be handled.
157 */
158 public SymlinkBehavior getSymlinkBehavior() {
159 return symlinkBehavior;
160 }
161
162 /**
kush92a225d2017-08-10 23:24:56 +0200163 * @return an immutable set of excludes. Null if none specified.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100164 */
165 @Nullable
166 public ImmutableSet<String> getExcludes() {
167 return excludes;
168 }
169
170 /**
171 * @return an immutable list of file labels. Null if none specified.
172 */
173 @Nullable
174 public ImmutableList<Label> getFiles() {
175 return files;
176 }
177
178 /**
179 * @return true if this Fileset should get files from the source directory.
180 */
181 public boolean isSourceFileset() {
182 return "BUILD".equals(srcLabel.getName());
183 }
184
185 /**
186 * @return all prerequisite labels in the FilesetEntry.
187 */
188 public Collection<Label> getLabels() {
189 Set<Label> labels = new LinkedHashSet<>();
190 if (files != null) {
191 labels.addAll(files);
192 } else {
193 labels.add(srcLabel);
194 }
195 return labels;
196 }
197
198 /**
199 * @return the prefix that should be stripped from package-relative path names.
200 */
201 public String getStripPrefix() {
202 return stripPrefix;
203 }
204
205 /**
206 * @return null if the entry is valid, and a human-readable error message otherwise.
207 */
208 @Nullable
209 public String validate() {
210 if (excludes != null && files != null) {
211 return "Cannot specify both 'files' and 'excludes' in a FilesetEntry";
212 } else if (files != null && !isSourceFileset()) {
213 return "Cannot specify files with Fileset label '" + srcLabel + "'";
214 } else if (destDir.isAbsolute()) {
215 return "Cannot specify absolute destdir '" + destDir + "'";
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000216 } else if (!stripPrefix.equals(DEFAULT_STRIP_PREFIX) && files == null) {
217 return "If the strip prefix is not \"" + DEFAULT_STRIP_PREFIX + "\", files must be specified";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +0000218 } else if (stripPrefix.startsWith("/")) {
219 return "Cannot specify absolute strip prefix; perhaps you need to use \""
220 + STRIP_PREFIX_WORKSPACE + "\"";
nharmatab4060b62017-04-04 17:11:39 +0000221 } else if (PathFragment.create(stripPrefix).containsUplevelReferences()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100222 return "Strip prefix must not contain uplevel references";
Laszlo Csomor6979c8e2016-02-04 10:43:11 +0000223 } else if (stripPrefix.startsWith("%") && !stripPrefix.startsWith(STRIP_PREFIX_WORKSPACE)) {
224 return "If the strip_prefix starts with \"%\" then it must start with \""
225 + STRIP_PREFIX_WORKSPACE + "\"";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100226 } else {
227 return null;
228 }
229 }
230
231 @Override
232 public String toString() {
Laszlo Csomorb136315c2016-01-19 14:03:29 +0000233 return String.format(
234 "FilesetEntry(srcdir=%s, destdir=%s, strip_prefix=%s, symlinks=%s, "
235 + "%d file(s) and %d excluded)",
236 srcLabel,
237 destDir,
238 stripPrefix,
239 symlinkBehavior,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100240 files != null ? files.size() : 0,
241 excludes != null ? excludes.size() : 0);
242 }
Michajlo Matijkiw586f80f2016-06-09 18:51:18 +0000243
244 @Override
245 public int hashCode() {
246 return Objects.hashCode(srcLabel, files, excludes, destDir, symlinkBehavior, stripPrefix);
247 }
248
249 @Override
250 public boolean equals(Object other) {
251 if (this == other) {
252 return true;
253 }
254
255 if (!(other instanceof FilesetEntry)) {
256 return false;
257 }
258
259 FilesetEntry that = (FilesetEntry) other;
260 return Objects.equal(srcLabel, that.srcLabel)
261 && Objects.equal(files, that.files)
262 && Objects.equal(excludes, that.excludes)
263 && Objects.equal(destDir, that.destDir)
264 && Objects.equal(symlinkBehavior, that.symlinkBehavior)
265 && Objects.equal(stripPrefix, that.stripPrefix);
266 }
267
268
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100269}