13
13
// limitations under the License.
14
14
package com .google .devtools .build .lib .rules .cpp ;
15
15
16
+ import static com .google .common .collect .ImmutableList .toImmutableList ;
17
+
16
18
import com .google .common .base .Preconditions ;
17
19
import com .google .common .base .Strings ;
18
20
import com .google .common .collect .ImmutableList ;
@@ -40,7 +42,6 @@ public class LibrariesToLinkCollector {
40
42
private final PathFragment toolchainLibrariesSolibDir ;
41
43
private final CppConfiguration cppConfiguration ;
42
44
private final CcToolchainProvider ccToolchainProvider ;
43
- private final Artifact outputArtifact ;
44
45
private final boolean isLtoIndexing ;
45
46
private final PathFragment solibDir ;
46
47
private final Iterable <? extends LinkerInput > linkerInputs ;
@@ -49,7 +50,8 @@ public class LibrariesToLinkCollector {
49
50
private final Artifact thinltoParamFile ;
50
51
private final FeatureConfiguration featureConfiguration ;
51
52
private final boolean needWholeArchive ;
52
- private final String rpathRoot ;
53
+ private final ImmutableList <String > potentialExecRoots ;
54
+ private final ImmutableList <String > rpathRoots ;
53
55
private final boolean needToolchainLibrariesRpath ;
54
56
private final Map <Artifact , Artifact > ltoMap ;
55
57
private final RuleErrorConsumer ruleErrorConsumer ;
@@ -76,7 +78,6 @@ public LibrariesToLinkCollector(
76
78
this .cppConfiguration = cppConfiguration ;
77
79
this .ccToolchainProvider = toolchain ;
78
80
this .toolchainLibrariesSolibDir = toolchainLibrariesSolibDir ;
79
- this .outputArtifact = output ;
80
81
this .solibDir = solibDir ;
81
82
this .isLtoIndexing = isLtoIndexing ;
82
83
this .allLtoArtifacts = allLtoArtifacts ;
@@ -106,22 +107,83 @@ public LibrariesToLinkCollector(
106
107
// and the second could use $ORIGIN/../_solib_[arch]. But since this is a shared
107
108
// artifact, both are symlinks to the same place, so
108
109
// there's no *one* RPATH setting that fits all targets involved in the sharing.
109
- rpathRoot = ccToolchainProvider .getSolibDirectory () + "/" ;
110
+ potentialExecRoots = ImmutableList .of ();
111
+ rpathRoots = ImmutableList .of (ccToolchainProvider .getSolibDirectory () + "/" );
110
112
} else {
111
- // When executed from within a runfiles directory, the binary lies under a path such as
112
- // target.runfiles/some_repo/pkg/file, whereas the solib directory is located under
113
- // target.runfiles/main_repo.
114
- PathFragment runfilesPath = outputArtifact .getRunfilesPath ();
115
- String runfilesExecRoot ;
116
- if (runfilesPath .startsWith (LabelConstants .EXTERNAL_RUNFILES_PATH_PREFIX )) {
117
- // runfilesPath is of the form ../some_repo/pkg/file, walk back some_repo/pkg and then
118
- // descend into the main workspace.
119
- runfilesExecRoot = Strings .repeat ("../" , runfilesPath .segmentCount () - 2 ) + workspaceName + "/" ;
120
- } else {
121
- // runfilesPath is of the form pkg/file, walk back pkg to reach the main workspace.
122
- runfilesExecRoot = Strings .repeat ("../" , runfilesPath .segmentCount () - 1 );
113
+ // The runtime location of the solib directory relative to the binary depends on four factors:
114
+ //
115
+ // * whether the binary is contained in the main repository or an external repository;
116
+ // * whether the binary is executed directly or from a runfiles tree;
117
+ // * whether the binary is staged as a symlink (sandboxed execution; local execution if the
118
+ // binary is in the runfiles of another target) or a regular file (remote execution) - the
119
+ // dynamic linker follows sandbox and runfiles symlinks into its location under the
120
+ // unsandboxed execroot, which thus becomes the effective $ORIGIN;
121
+ // * whether --experimental_sibling_repository_layout is enabled or not.
122
+ //
123
+ // The rpaths emitted into the binary thus have to cover the following cases (assuming that
124
+ // the binary target is located in the pkg `pkg` and has name `file`) for the directory used
125
+ // as $ORIGIN by the dynamic linker and the directory containing the solib directories:
126
+ //
127
+ // 1. main, direct, symlink:
128
+ // $ORIGIN: $EXECROOT/pkg
129
+ // solib root: $EXECROOT
130
+ // 2. main, direct, regular file:
131
+ // $ORIGIN: $EXECROOT/pkg
132
+ // solib root: $EXECROOT/pkg/file.runfiles/main_repo
133
+ // 3. main, runfiles, symlink:
134
+ // $ORIGIN: $EXECROOT/pkg
135
+ // solib root: $EXECROOT
136
+ // 4. main, runfiles, regular file:
137
+ // $ORIGIN: other_target.runfiles/main_repo/pkg
138
+ // solib root: other_target.runfiles/main_repo
139
+ // 5a. external, direct, symlink:
140
+ // $ORIGIN: $EXECROOT/external/other_repo/pkg
141
+ // solib root: $EXECROOT
142
+ // 5b. external, direct, symlink, with --experimental_sibling_repository_layout:
143
+ // $ORIGIN: $EXECROOT/../other_repo/pkg
144
+ // solib root: $EXECROOT/../other_repo
145
+ // 6a. external, direct, regular file:
146
+ // $ORIGIN: $EXECROOT/external/other_repo/pkg
147
+ // solib root: $EXECROOT/external/other_repo/pkg/file.runfiles/main_repo
148
+ // 6b. external, direct, regular file, with --experimental_sibling_repository_layout:
149
+ // $ORIGIN: $EXECROOT/../other_repo/pkg
150
+ // solib root: $EXECROOT/../other_repo/pkg/file.runfiles/other_repo
151
+ // 7a. external, runfiles, symlink:
152
+ // $ORIGIN: $EXECROOT/external/other_repo/pkg
153
+ // solib root: $EXECROOT
154
+ // 7b. external, runfiles, symlink, with --experimental_sibling_repository_layout:
155
+ // $ORIGIN: $EXECROOT/../other_repo/pkg
156
+ // solib root: $EXECROOT/../other_repo
157
+ // 8a. external, runfiles, regular file:
158
+ // $ORIGIN: other_target.runfiles/some_repo/pkg
159
+ // solib root: other_target.runfiles/main_repo
160
+ // 8b. external, runfiles, regular file, with --experimental_sibling_repository_layout:
161
+ // $ORIGIN: other_target.runfiles/some_repo/pkg
162
+ // solib root: other_target.runfiles/some_repo
163
+ //
164
+ // Cases 1, 3, 4, 5, 7, and 8b are covered by an rpath that walks up the root relative path.
165
+ // Case 8a is covered by walking up some_repo/pkg and then into main_repo.
166
+ // Cases 2 and 6 are currently not covered as they would require an rpath containing the
167
+ // binary filename, which may contain commas that would clash with the `-Wl` argument used to
168
+ // pass the rpath to the linker.
169
+ // TODO(#14600): Fix this by using `-Xlinker` instead of `-Wl`.
170
+ ImmutableList .Builder <String > execRoots = ImmutableList .builder ();
171
+ // Handles cases 1, 3, 4, 5, and 7.
172
+ execRoots .add (Strings .repeat ("../" , output .getRootRelativePath ().segmentCount () - 1 ));
173
+ if (output .getRunfilesPath ().startsWith (LabelConstants .EXTERNAL_RUNFILES_PATH_PREFIX )
174
+ && output .getRoot ().isLegacy ()) {
175
+ // Handles case 8a. The runfiles path is of the form ../some_repo/pkg/file and we need to
176
+ // walk up some_repo/pkg and then down into main_repo.
177
+ execRoots .add (
178
+ Strings .repeat ("../" , output .getRunfilesPath ().segmentCount () - 2 ) + workspaceName
179
+ + "/" );
123
180
}
124
- rpathRoot = runfilesExecRoot + ccToolchainProvider .getSolibDirectory () + "/" ;
181
+
182
+ potentialExecRoots = execRoots .build ();
183
+ rpathRoots =
184
+ potentialExecRoots .stream ()
185
+ .map ((execRoot ) -> execRoot + ccToolchainProvider .getSolibDirectory () + "/" )
186
+ .collect (toImmutableList ());
125
187
}
126
188
127
189
ltoMap = generateLtoMap ();
@@ -196,10 +258,10 @@ public CollectedLibrariesToLink collectLibrariesToLink() {
196
258
// directory. In other words, given blaze-bin/my/package/binary, rpathRoot would be
197
259
// "../../_solib_[arch]".
198
260
if (needToolchainLibrariesRpath ) {
199
- runtimeLibrarySearchDirectories . add (
200
- Strings . repeat ( "../" , outputArtifact . getRootRelativePath (). segmentCount () - 1 )
201
- + toolchainLibrariesSolibName
202
- + "/" );
261
+ for ( String potentialExecRoot : potentialExecRoots ) {
262
+ runtimeLibrarySearchDirectories . add (
263
+ potentialExecRoot + toolchainLibrariesSolibName + "/" );
264
+ }
203
265
}
204
266
if (isNativeDeps ) {
205
267
// We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to
@@ -231,7 +293,9 @@ public CollectedLibrariesToLink collectLibrariesToLink() {
231
293
NestedSetBuilder <String > allRuntimeLibrarySearchDirectories = NestedSetBuilder .linkOrder ();
232
294
// rpath ordering matters for performance; first add the one where most libraries are found.
233
295
if (includeSolibDir ) {
234
- allRuntimeLibrarySearchDirectories .add (rpathRoot );
296
+ for (String rpathRoot : rpathRoots ) {
297
+ allRuntimeLibrarySearchDirectories .add (rpathRoot );
298
+ }
235
299
}
236
300
allRuntimeLibrarySearchDirectories .addAll (rpathRootsForExplicitSoDeps .build ());
237
301
if (includeToolchainLibrariesSolibDir ) {
@@ -346,17 +410,21 @@ private void addDynamicInputLinkOptions(
346
410
// When all dynamic deps are built in transitioned configurations, the default solib dir is
347
411
// not created. While resolving paths, the dynamic linker stops at the first directory that
348
412
// does not exist, even when followed by "../". We thus have to normalize the relative path.
349
- String relativePathToRoot =
350
- rpathRoot + dotdots + libDir .relativeTo (commonParent ).getPathString ();
351
- String normalizedPathToRoot = PathFragment .create (relativePathToRoot ).getPathString ();
352
- rpathRootsForExplicitSoDeps .add (normalizedPathToRoot );
413
+ for (String rpathRoot : rpathRoots ) {
414
+ String relativePathToRoot =
415
+ rpathRoot + dotdots + libDir .relativeTo (commonParent ).getPathString ();
416
+ String normalizedPathToRoot = PathFragment .create (relativePathToRoot ).getPathString ();
417
+ rpathRootsForExplicitSoDeps .add (normalizedPathToRoot );
418
+ }
353
419
354
420
// Unless running locally, libraries will be available under the root relative path, so we
355
421
// should add that to the rpath as well.
356
422
if (inputArtifact .getRootRelativePathString ().startsWith ("_solib_" )) {
357
423
PathFragment artifactPathUnderSolib = inputArtifact .getRootRelativePath ().subFragment (1 );
358
- rpathRootsForExplicitSoDeps .add (
359
- rpathRoot + artifactPathUnderSolib .getParentDirectory ().getPathString ());
424
+ for (String rpathRoot : rpathRoots ) {
425
+ rpathRootsForExplicitSoDeps .add (
426
+ rpathRoot + artifactPathUnderSolib .getParentDirectory ().getPathString ());
427
+ }
360
428
}
361
429
}
362
430
0 commit comments