rules_foreign_cc icon indicating copy to clipboard operation
rules_foreign_cc copied to clipboard

cmake: file installed from the source directory are only symlinked

Open t-8ch opened this issue 1 year ago • 12 comments

When using cmake() together with Bazel 7.0 with exported headers the build fails.

$ cd rules_foreign_cc/examples
$ USE_BAZEL_VERSION=7.0.0rc7 bazelisk build //cmake_hello_world_lib/shared/... 
INFO: Analyzed 3 targets (0 packages loaded, 0 targets configured).
ERROR: /home/me/src/rules_foreign_cc/examples/cmake_hello_world_lib/shared/BUILD.bazel:13:6: Error while validating output TreeArtifact File:[[<execution_root>]bazel-out/k8-fastbuild/bin]cmake_hello_world_lib/shared/libhello/include : Failed to resolve relative path hello.h inside TreeArtifact /home/tweisssc/.cache/bazel/_bazel_tweisssc/691c198c0f7498b76c679347f393fea9/execroot/_main/bazel-out/k8-fastbuild/bin/cmake_hello_world_lib/shared/libhello/include. The associated file is either missing or is an invalid symlink.
ERROR: /home/me/src/rules_foreign_cc/examples/cmake_hello_world_lib/shared/BUILD.bazel:13:6: Foreign Cc - CMake: Building libhello failed: not all outputs were created or valid
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 0.428s, Critical Path: 0.36s
INFO: 2 processes: 1 internal, 1 linux-sandbox.
ERROR: Build did NOT complete successfully

And indeed the installed header file is a symlink:

$ ls -l /home/me/.cache/bazel/_bazel_me/691c198c0f7498b76c679347f393fea9/execroot/_main/bazel-out/k8-fastbuild/bin/cmake_hello_world_
lib/shared/libhello/include/hello.h
lrwxrwxrwx 1 me me 66 Dec 11 16:57 /home/me/.cache/bazel/_bazel_tweisssc/691c198c0f7498b76c679347f393fea9/execroot/_main/bazel-out/k8-fastbuild/bin/cmake_hello_world_lib/shared/libhello/include/hello.h -> /tmp/bazel-source-roots/0/cmake_hello_world_lib/shared/src/hello.h

The problem is that cmake will preserve symlinks when installing them instead of resolving them to the contents (see cmFileCopier::Install()). cmake() with filegroup() symlinks all the sources into the internal build tree, leading for example headers from the source tree to be also installed as (broken) symlink.

t-8ch avatar Dec 11 '23 17:12 t-8ch

See also https://github.com/bazelbuild/bazel/issues/10299

t-8ch avatar Dec 14 '23 12:12 t-8ch

This is likely the issue I was seeing here: https://bazelbuild.slack.com/archives/CGA9QFQ8H/p1701343519010999

cameron-martin avatar Dec 19 '23 11:12 cameron-martin

for those that get here and aren't on the bazel slack, the temporary workaround is the flag --noincompatible_sandbox_hermetic_tmp

pjjw avatar Dec 19 '23 23:12 pjjw

There are two workarounds we tested:

  1. Patch the original CMakeLists.txt to copy the source file to the build directory with ${CMAKE_COMMAND} -E copy and then install that copy, so it won't be a symlink anymore.
  2. Run a script over the install directory after the installation to replace symlinks pointing outside of the install directory with a copy of the file they are pointing to.

Solution 2. Could also be implemented natively by rules_foreign_cc in a generic way.

t-8ch avatar Dec 20 '23 09:12 t-8ch

if feedback from the peanut gallery is welcome, option 2 seems like the option least-likely to have baffling corner cases. we're already doing a bunch of backbends with rules_foreign_cc involving multiple cmake projects reading config from each other, so any straightforward patching approach probably wouldn't work for our admittedly goofy usage.

pjjw avatar Dec 20 '23 19:12 pjjw

https://github.com/bazelbuild/bazel/pull/20603 - this may fix this issue too

jsharpe avatar Dec 21 '23 00:12 jsharpe

@jsharpe It does not fix the issue for me. The same error happens and the same sandbox-relative symlink appears in the cache.

t-8ch avatar Dec 21 '23 08:12 t-8ch

for those that get here and aren't on the bazel slack, the temporary workaround is the flag --noincompatible_sandbox_hermetic_tmp

for the record, the slightly less scary --sandbox_add_mount_pair=/tmp also seems to be a workaround.

eguiraud avatar Jan 25 '24 18:01 eguiraud

that has the same effect as --noincompatible_sandbox_hermetic_tmp, it is identically scary :)

pjjw avatar Jan 25 '24 22:01 pjjw

Possibly this'll fix this issue, and is marked for 7.1: https://github.com/bazelbuild/bazel/issues/21215

cameron-martin avatar Feb 07 '24 11:02 cameron-martin

For the record,

common --noincompatible_sandbox_hermetic_tmp
common --sandbox_tmpfs_path=/tmp

worked for me on Bazel 7, while (presumably) still preserving the hermetic tmp directory which is necessary for us to address occasional hotspot crashes during Java compilations.

criemen avatar Feb 23 '24 10:02 criemen

There are two workarounds we tested:

  1. Patch the original CMakeLists.txt to copy the source file to the build directory with ${CMAKE_COMMAND} -E copy and then install that copy, so it won't be a symlink anymore.
  2. Run a script over the install directory after the installation to replace symlinks pointing outside of the install directory with a copy of the file they are pointing to.

Solution 2. Could also be implemented natively by rules_foreign_cc in a generic way.

Speaking of the second workaround, I thought I may provide the current implementation I'm using, in case someone needs it.

# Just add the 'postfix_script' to ensure that symbolic links are copied from the original files, removing the dangling problem
cmake(
    name = "external_lib",
    lib_source = ":all_srcs",
    # ...
    postfix_script = """find $INSTALLDIR -type l -exec sh -c 'for i in "$@"; do cp --preserve --remove-destination "$(readlink -f "$i")" "$i"; done' sh {} +""",
)

filegroup(
    name = "all_srcs",
    srcs = glob(["**"], exclude = ["bazel-*"]),
)

I am not an expert, and there may be some good reasons not to do so, but you may want to consider whether to add such a line directly into the official script

TendTo avatar Apr 11 '24 14:04 TendTo