create_library_to_link: add mangle_name and unique_name arguments#8888
create_library_to_link: add mangle_name and unique_name arguments#8888aherrmann wants to merge 11 commits intobazelbuild:masterfrom
Conversation
51cdeec to
6e3e4e2
Compare
2fface6 to
f85baf2
Compare
|
CI has passed and this is now ready for review. The tests test the symlinks generated in the solib directory. Since these are not generated on Windows, I have disabled the tests on Windows. |
|
This PR just exposes existing method parameters that are there in the Java API to Starlark code. It's currently a blocker for a few issues in rules_haskell. |
|
I will take a look at this on Monday next week. Even though this is in Java, it doesn't mean we want to expose it to Starlark. We are carefully reviewing every parameter we add to the API because unlike in Java fixing a mistake there is not as simple as an internal refactoring of our code. |
|
So if I understand correctly, you want more flexibility in the name used for the symlink created. I think better than the two booleans on the API, the change should add a String parameter which internally gets converted to a That should give enough flexibility to do what you want. But first I have to ask, why was it causing issues to have the default Regarding the test, I would prefer if you remove the shell test altogether and add a new test here: Thanks for this PR! |
Yes, that is correct.
Interesting, that sounds feasible, I'll look into it.
Sure, take tweag/rules_haskell@21c277b with Bazel 0.27.0 on Linux with the default GHC bindist. Then run By default Bazel produces very long directory names, and separate directories for most libraries. On macOS, the standard GHC bindist requires dynamic linking as part of compilation, each of these long paths will have to appear in the MACH-O header as a runpath or load command. This can very quickly add up to exceed the MACH-O header size limit. The header may not exceed 32k. With paths like the above we have frequently had issues with builds failing due to MACH-O header size at around 200 library dependencies. Currrently, rules_haskell employs a number of workarounds to avoid the issue. Shortening the symlink paths as with this PR would make our lives as rules_haskell maintainers easier, and also raise the upper bound for the number of possible library dependencies. For core Haskell libraries as well as and should instead look like this The library name contains the name and version of the corresponding core package and the compiler version. This makes accidental name collisions very, very unlikely. The last library comes from a local and should instead look like this The library name already contains the full package path and is thereby unique. The
Right, I'm not too familiar with the internal Java API. If I understand correctly, I'd generate the necessary files similar to how it's done in Thanks for the review @oquenchil |
Adds an overload of `getDynamicLibrarySymlink` that takes the relative path of the symlink to be created underneath the solib directory. Addresses review comment bazelbuild#8888 (comment)
By default the Starlark function `create_library_to_link` would always preserve the library name and create a subdirectory underneath `solibDir` to place the library symlink. This commit allows to override this behavior by specifying a path fragment where to place the libray symlink underneath the solib dir. Addresses review comment bazelbuild#8888 (comment)
Add tests for custom solib link paths in create_library_to_link. Addresses review comment bazelbuild#8888 (comment)
f85baf2 to
4de84d0
Compare
|
@oquenchil I've addressed your comments. |
|
Yes, that looks perfect. One little nit though. The Otherwise, I think it might cause confusion with people thinking that the path of the actual Also initially I wanted to complain about symlinks being an internal optimization detail which is platform dependent and shouldn't be leaked. But this is already leaking through the The PR looks perfect, the test is exactly how it should be and I appreciate you took good care of limiting the filetypes for these two parameters. Just renaming the two input parameters to make clear that it's for the symlink and we can merge this. |
Addresses review comment bazelbuild#8888 (comment)
|
@oquenchil Thank you, that's great to hear!
Yes, that is correct. Agreed, makes sense to be more explicit in those names. I've changed the names as suggested. PTAL |
| @Param( | ||
| name = "dynamic_library_symlink_path", | ||
| doc = | ||
| "Override the default path of the dynamic library link in the solib directory.", |
There was a problem hiding this comment.
Path is an actual Starlark type -- lets make this clear this should be a string instead of a path type? maybe "path string" ?
(Same below)
There was a problem hiding this comment.
I've amended the docstring following #8888 (comment). That should clarify this a bit more. Also, the html documentation will display the type String above that description, similar to here.
| named = true, | ||
| noneable = true, | ||
| type = String.class, | ||
| defaultValue = "None"), |
There was a problem hiding this comment.
Is there a possibility empty-string is a meaningful path here? Otherwise, consider using empty string as the default value, and making the parameter type an actual String instead of an Object.
There was a problem hiding this comment.
You're right that an empty string is not meaningful here. I was trying to follow the convention of None meaning "use default behavior". But, it looks like for strings "" is indeed used for that purpose, e.g. in repository_ctx.execute(..., working_directory = ""), so this change seems alright. The advantage is that it makes it easier to forward a default value from a rule string attribute, like here.
Addressing review comment bazelbuild#8888 (comment)
aherrmann
left a comment
There was a problem hiding this comment.
@c-parsons I've addressed your comments. PTAL
| @Param( | ||
| name = "dynamic_library_symlink_path", | ||
| doc = | ||
| "Override the default path of the dynamic library link in the solib directory.", |
There was a problem hiding this comment.
I've amended the docstring following #8888 (comment). That should clarify this a bit more. Also, the html documentation will display the type String above that description, similar to here.
| named = true, | ||
| noneable = true, | ||
| type = String.class, | ||
| defaultValue = "None"), |
There was a problem hiding this comment.
You're right that an empty string is not meaningful here. I was trying to follow the convention of None meaning "use default behavior". But, it looks like for strings "" is indeed used for that purpose, e.g. in repository_ctx.execute(..., working_directory = ""), so this change seems alright. The advantage is that it makes it easier to forward a default value from a rule string attribute, like here.
|
@c-parsons @oquenchil Can this be merged? |
c-parsons
left a comment
There was a problem hiding this comment.
Sorry this review fell off my radar In general, this looks good to me. But I'd like to get approval from oquenchil@ before proceeding, as he has more knowledge of this piece of the API.
| PathFragment dynamicLibraryPathFragment = null; | ||
| if (dynamicLibraryPath != null && !dynamicLibraryPath.isEmpty()) { | ||
| dynamicLibraryPathFragment = PathFragment.create(dynamicLibraryPath); | ||
| if (dynamicLibraryPathFragment.isEmpty() || dynamicLibraryPathFragment.isAbsolute() || dynamicLibraryPathFragment.containsUplevelReferences()) { |
There was a problem hiding this comment.
Do we need to check dynamicLibraryPathFragment.isEmpty(), given that we already checked dynamicLibraryPath.isEmpty above?
There was a problem hiding this comment.
Good question. PathFragment.create performs normalization if necessary. The docs don't seem to guarantee that normalization never returns an empty PathFragment given a non-empty String. E.g. ./ or foo/.. could potentially be normalized away. For reference, the docs say
* <p>Strings are normalized with '.' and '..' removed and resolved (if possible), any multiple
* slashes ('/') removed, and any trailing slash also removed. Windows drive letters are uppercased.
* The current implementation does not touch the incoming path string unless the string actually
* needs to be normalized.
| assertThat( | ||
| librariesToLink.toCollection(LibraryToLink.class).stream() | ||
| .filter(x -> x.getDynamicLibrary() != null) | ||
| .map(x -> x.getDynamicLibrary().getRootRelativePath().relativeTo(toolchain.getSolibDirectory()).toString()) |
There was a problem hiding this comment.
nit: can you refactor x.getDynamicLibrary().getRootRelativePath().relativeTo(toolchain.getSolibDirectory()).toString() to a helper private static method, to make this slightly more readable? (the combination of chained calls and use of stream() makes this difficult to read)
There was a problem hiding this comment.
I've factored it out as getSolibRelativePath.
Yes, still looks good to me. |
|
Im running into MACH-O limits in rules_haskell and would greatly appreciate this getting merged. Is there anything holding this up from merging? |
|
@c-parsons I think this is ready for merge. Let me know if there's something else you'd like to see changed in this PR. @mjrussell Thanks for pinging. Regarding MACH-O limits, which version of rules_haskell are you on? Version 0.11 contains some workarounds that should avoid most MACH-O header size issues. If you're still hitting these issues on 0.11, could you open an issue at https://github.com/tweag/rules_haskell with some more details? |
src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
Outdated
Show resolved
Hide resolved
src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
Outdated
Show resolved
Hide resolved
src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
Outdated
Show resolved
Hide resolved
src/main/java/com/google/devtools/build/lib/rules/cpp/SolibSymlinkAction.java
Show resolved
Hide resolved
Adds an overload of `getDynamicLibrarySymlink` that takes the relative path of the symlink to be created underneath the solib directory. Addresses review comment bazelbuild#8888 (comment)
By default the Starlark function `create_library_to_link` would always preserve the library name and create a subdirectory underneath `solibDir` to place the library symlink. This commit allows to override this behavior by specifying a path fragment where to place the libray symlink underneath the solib dir. Addresses review comment bazelbuild#8888 (comment)
Add tests for custom solib link paths in create_library_to_link. Addresses review comment bazelbuild#8888 (comment)
Addresses review comment bazelbuild#8888 (comment)
Addressing review comment bazelbuild#8888 (comment)
85c0a8e to
43dc9a7
Compare
|
@c-parsons I've addressed your comments. PTAL. |
|
|
||
| Artifact notNullArtifactForIdentifier = null; | ||
| StringBuilder extensionErrorsBuilder = new StringBuilder(); | ||
| String extensionErrorMessage = "does not have any of the allowed extensions"; |
There was a problem hiding this comment.
It's still used in the validation logic for the static and dynamic library attributes. E.g. here. Sadly, it doesn't match so well with the symlink validation and refactoring that code too much seems out of scope for this PR.
| } | ||
|
|
||
| private static void validateSymlinkPath(Location location, String attrName, PathFragment symlinkPath, FileTypeSet filetypes, StringBuilder errorsBuilder) throws EvalException { | ||
| String extensionErrorMessage = "does not have any of the allowed extensions"; |
There was a problem hiding this comment.
please just inline this string
Awesome, thank you! |
Closes #8180
mangle_nameandunique_namearguments tocreate_library_to_linkmangle_nameallows users replicate the behavior ofcc_libraryin their custom rules. The link placed under the solib directory will be mangled and no intermediate directory will be generated.unique_nameallows users to specify that the given library name is unique and no intermediate directory is required.prefixLibraryPathargument to the internalCcCommonAPI to facilitate the above changes tocreate_library_to_link.mangle_nameandunique_namearguments tocreate_library_to_link.Motivation
See #8180 tweag/rules_haskell#958
Before this PR the new Starlark Cc API's
create_library_to_linkwould always preserve the given library name and link it into the solib directory underneath an intermediate directory based on the library label and consumer label. These intermediate directories can grow very long, in particular if external workspaces are involved. In rules_haskell this causes issues on Windows and MacOS. On Windows the linker cannot look for libraries underneath search paths which exceed a certain path length. On MacOS the header size of a MACH-O binary/library is limited and the required runpaths/load commands would quickly exceed this limit due to the long paths generated by Bazel. In rules_haskell in particular these intermediate directories are unnecessary, as rules_haskell already mangles the library names itself to ensure their uniqueness. Furthermore, the Haskell compiler GHC expects library names of a particular shape, such that Bazel's builtin mangling cannot be used. This motivates the combinationmangle_name = False,unique_name = Trueenabled by this PR.cc @oquenchil @mboes @cocreature