blob: 4db5bf699ae5dd74723f45caa8c40360034a6000 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 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
Kristina Chodorow73fa2032015-08-28 17:57:46 +000015package com.google.devtools.build.lib.cmdline;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010016
tomlua155b532017-11-08 20:12:47 +010017import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ComparisonChain;
twerthc3661662018-07-04 05:56:27 -070019import com.google.common.collect.ImmutableMap;
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +000020import com.google.common.collect.Interner;
Nathan Harmataa16d9f12016-11-23 20:58:07 +000021import com.google.devtools.build.lib.concurrent.BlazeInterners;
shahan20f35b42018-02-28 15:57:33 -080022import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
vladmosf07773b2017-07-11 16:31:32 +020023import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
24import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import java.io.Serializable;
Shreya Bhattaraifed931d2016-05-26 13:46:39 +000027import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import javax.annotation.concurrent.Immutable;
29
30/**
31 * Uniquely identifies a package, given a repository name and a package's path fragment.
32 *
vladmosf07773b2017-07-11 16:31:32 +020033 * <p>The repository the build is happening in is the <i>default workspace</i>, and is identified by
34 * the workspace name "". Other repositories can be named in the WORKSPACE file. These workspaces
35 * are prefixed by {@literal @}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036 */
shahan20f35b42018-02-28 15:57:33 -080037@AutoCodec
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038@Immutable
vladmosf07773b2017-07-11 16:31:32 +020039public final class PackageIdentifier
40 implements Comparable<PackageIdentifier>, Serializable, SkylarkValue {
Nathan Harmataa16d9f12016-11-23 20:58:07 +000041 private static final Interner<PackageIdentifier> INTERNER = BlazeInterners.newWeakInterner();
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +000042
43 public static PackageIdentifier create(String repository, PathFragment pkgName)
44 throws LabelSyntaxException {
45 return create(RepositoryName.create(repository), pkgName);
46 }
47
shahan20f35b42018-02-28 15:57:33 -080048 @AutoCodec.Instantiator
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +000049 public static PackageIdentifier create(RepositoryName repository, PathFragment pkgName) {
shreyax43a1dc72018-06-02 15:08:51 -070050 // Note: We rely on these being interned to fast-path Label#equals.
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +000051 return INTERNER.intern(new PackageIdentifier(repository, pkgName));
52 }
53
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +000054 public static final PackageIdentifier EMPTY_PACKAGE_ID = createInMainRepo(
55 PathFragment.EMPTY_FRAGMENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056
Brian Silvermand7d6d622016-03-17 09:53:39 +000057 public static PackageIdentifier createInMainRepo(String name) {
nharmatab4060b62017-04-04 17:11:39 +000058 return createInMainRepo(PathFragment.create(name));
Brian Silvermand7d6d622016-03-17 09:53:39 +000059 }
60
61 public static PackageIdentifier createInMainRepo(PathFragment name) {
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +000062 return create(RepositoryName.MAIN, name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010063 }
64
65 /**
Ulf Adamsa28b5402017-02-24 09:28:44 +000066 * Tries to infer the package identifier from the given exec path. This method does not perform
67 * any I/O, but looks solely at the structure of the exec path. The resulting identifier may
68 * actually be a subdirectory of a package rather than a package, e.g.:
jcater7b00f142017-08-30 23:10:11 +020069 *
Ulf Adamsa28b5402017-02-24 09:28:44 +000070 * <pre><code>
71 * + WORKSPACE
72 * + foo/BUILD
73 * + foo/bar/bar.java
74 * </code></pre>
75 *
76 * In this case, this method returns a package identifier for foo/bar, even though that is not a
77 * package. Callers need to look up the actual package if needed.
78 *
jcater7b00f142017-08-30 23:10:11 +020079 * @throws LabelSyntaxException if the exec path seems to be for an external repository that does
80 * not have a valid repository name (see {@link RepositoryName#create})
Ulf Adamsa28b5402017-02-24 09:28:44 +000081 */
82 public static PackageIdentifier discoverFromExecPath(PathFragment execPath, boolean forFiles)
83 throws LabelSyntaxException {
84 Preconditions.checkArgument(!execPath.isAbsolute(), execPath);
85 PathFragment tofind = forFiles
86 ? Preconditions.checkNotNull(
87 execPath.getParentDirectory(), "Must pass in files, not root directory")
88 : execPath;
dannarkbe3cefc2018-12-13 11:52:45 -080089 if (tofind.startsWith(LabelConstants.EXTERNAL_PATH_PREFIX)) {
Ulf Adamsa28b5402017-02-24 09:28:44 +000090 // TODO(ulfjack): Remove this when kchodorow@'s exec root rearrangement has been rolled out.
91 RepositoryName repository = RepositoryName.create("@" + tofind.getSegment(1));
tomlue7552c52018-01-19 10:25:19 -080092 return PackageIdentifier.create(repository, tofind.subFragment(2));
tomlua729b9b2018-02-08 15:32:00 -080093 } else if (tofind.containsUplevelReferences()) {
Ulf Adamsa28b5402017-02-24 09:28:44 +000094 RepositoryName repository = RepositoryName.create("@" + tofind.getSegment(1));
tomlue7552c52018-01-19 10:25:19 -080095 return PackageIdentifier.create(repository, tofind.subFragment(2));
Ulf Adamsa28b5402017-02-24 09:28:44 +000096 } else {
97 return PackageIdentifier.createInMainRepo(tofind);
98 }
99 }
100
101 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102 * The identifier for this repository. This is either "" or prefixed with an "@",
103 * e.g., "@myrepo".
104 */
105 private final RepositoryName repository;
106
brandjonf76ad072017-04-12 16:32:05 +0000107 /** The name of the package. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108 private final PathFragment pkgName;
109
brandjonf76ad072017-04-12 16:32:05 +0000110 /**
111 * Precomputed hash code. Hash/equality is based on repository and pkgName. Note that due to
112 * interning, x.equals(y) <=> x==y.
113 **/
Shreya Bhattaraifed931d2016-05-26 13:46:39 +0000114 private final int hashCode;
115
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +0000116 private PackageIdentifier(RepositoryName repository, PathFragment pkgName) {
Eric Fellheimer913d4582016-01-14 17:53:54 +0000117 this.repository = Preconditions.checkNotNull(repository);
shreyaxa1f90ce2018-02-15 12:05:13 -0800118 this.pkgName = Preconditions.checkNotNull(pkgName);
Shreya Bhattaraifed931d2016-05-26 13:46:39 +0000119 this.hashCode = Objects.hash(repository, pkgName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 }
121
Lukacs Berkia6434362015-09-15 13:56:14 +0000122 public static PackageIdentifier parse(String input) throws LabelSyntaxException {
twerthc3661662018-07-04 05:56:27 -0700123 return parse(input, /* repo= */ null, /* repositoryMapping= */ null);
124 }
125
126 public static PackageIdentifier parse(
127 String input, String repo, ImmutableMap<RepositoryName, RepositoryName> repositoryMapping)
128 throws LabelSyntaxException {
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000129 String packageName;
130 int packageStartPos = input.indexOf("//");
twerthc3661662018-07-04 05:56:27 -0700131 if (repo != null) {
132 packageName = input;
133 } else if (input.startsWith("@") && packageStartPos > 0) {
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000134 repo = input.substring(0, packageStartPos);
135 packageName = input.substring(packageStartPos + 2);
Lukacs Berkia6434362015-09-15 13:56:14 +0000136 } else if (input.startsWith("@")) {
Lukacs Berki485eb962016-01-13 10:47:29 +0000137 throw new LabelSyntaxException("starts with a '@' but does not contain '//'");
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000138 } else if (packageStartPos == 0) {
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000139 repo = RepositoryName.DEFAULT_REPOSITORY;
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000140 packageName = input.substring(2);
141 } else {
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000142 repo = RepositoryName.DEFAULT_REPOSITORY;
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000143 packageName = input;
144 }
145
146 String error = RepositoryName.validate(repo);
147 if (error != null) {
Lukacs Berkia6434362015-09-15 13:56:14 +0000148 throw new LabelSyntaxException(error);
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000149 }
150
151 error = LabelValidator.validatePackageName(packageName);
152 if (error != null) {
Lukacs Berkia6434362015-09-15 13:56:14 +0000153 throw new LabelSyntaxException(error);
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000154 }
155
twerthc3661662018-07-04 05:56:27 -0700156 if (repositoryMapping != null) {
157 RepositoryName repositoryName = RepositoryName.create(repo);
158 repositoryName = repositoryMapping.getOrDefault(repositoryName, repositoryName);
159 return create(repositoryName, PathFragment.create(packageName));
160 } else {
161 return create(repo, PathFragment.create(packageName));
162 }
Lukacs Berki33aa1e12015-07-08 08:11:30 +0000163 }
164
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100165 public RepositoryName getRepository() {
166 return repository;
167 }
168
169 public PathFragment getPackageFragment() {
170 return pkgName;
171 }
172
173 /**
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000174 * Returns a relative path to the source code for this package. Returns pkgName if this is in the
175 * main repository or external/[repository name]/[pkgName] if not.
Kristina Chodorow9bf65cb2015-03-18 18:42:35 +0000176 */
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000177 public PathFragment getSourceRoot() {
178 return repository.getSourceRoot().getRelative(pkgName);
Kristina Chodorow9bf65cb2015-03-18 18:42:35 +0000179 }
180
Kristina Chodorowbd016c92016-09-09 14:10:44 +0000181 public PathFragment getPathUnderExecRoot() {
Kristina Chodorowf57730b2017-01-05 19:43:03 +0000182 return repository.getPathUnderExecRoot().getRelative(pkgName);
Laszlo Csomor8539a122016-09-20 15:40:42 +0000183 }
184
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000185 /**
186 * Returns the runfiles/execRoot path for this repository (relative to the x.runfiles/main-repo/
187 * directory).
188 */
189 public PathFragment getRunfilesPath() {
190 return repository.getRunfilesPath().getRelative(pkgName);
191 }
192
Brian Silvermand7d6d622016-03-17 09:53:39 +0000193 public PackageIdentifier makeAbsolute() {
194 if (!repository.isDefault()) {
195 return this;
196 }
197
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000198 return create(RepositoryName.MAIN, pkgName);
Brian Silvermand7d6d622016-03-17 09:53:39 +0000199 }
200
Kristina Chodorow9bf65cb2015-03-18 18:42:35 +0000201 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100202 * Returns the name of this package.
203 *
204 * <p>There are certain places that expect the path fragment as the package name ('foo/bar') as a
205 * package identifier. This isn't specific enough for packages in other repositories, so their
206 * stringified version is '@baz//foo/bar'.</p>
207 */
208 @Override
209 public String toString() {
Brian Silvermand7d6d622016-03-17 09:53:39 +0000210 return (repository.isDefault() || repository.isMain() ? "" : repository + "//") + pkgName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 }
212
213 @Override
214 public boolean equals(Object object) {
215 if (this == object) {
216 return true;
217 }
Ulf Adamsc5aeaa32015-02-06 14:05:30 +0000218 if (!(object instanceof PackageIdentifier)) {
219 return false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100220 }
Ulf Adamsc5aeaa32015-02-06 14:05:30 +0000221 PackageIdentifier that = (PackageIdentifier) object;
Shreya Bhattaraifed931d2016-05-26 13:46:39 +0000222 return this.hashCode == that.hashCode && pkgName.equals(that.pkgName)
223 && repository.equals(that.repository);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100224 }
225
226 @Override
227 public int hashCode() {
Shreya Bhattaraifed931d2016-05-26 13:46:39 +0000228 return this.hashCode;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100229 }
230
231 @Override
232 public int compareTo(PackageIdentifier that) {
233 return ComparisonChain.start()
234 .compare(repository.toString(), that.repository.toString())
235 .compare(pkgName, that.pkgName)
236 .result();
237 }
vladmosf07773b2017-07-11 16:31:32 +0200238
239 @Override
240 public void repr(SkylarkPrinter printer) {
241 printer.repr(toString());
242 }
Janak Ramakrishnana71d4702015-05-06 16:55:00 +0000243}