fix: walk symlinks on Apple Silicon
We are now using a feature of the APE launcher to use indirect symlinks. This allows the APE launcher to be symlinked with an `.ape` extension. The APE launcher will remove the `.ape` extension and then launch the binary at that path. This works really well under Bazel because it can handle creating the indirect and direct symlink for a APE binary. For example: ``` $ ls -l ape-arm64.macho awk $ ape-arm64.macho awk --version awk version 20240311 $ ln -s ape-arm64.macho awk.ape $ ./awk.ape # APE launcher removes the `.ape` and executes the `awk` APE binary awk version 20240311 ``` However, Bazel requires that a rule _must_ return files that it has declared and created. A common pattern is to simply symlink to a previously generated file and return that through the `DefaultInfo`. This breaks the indirect symlink because the downstream symlink can remove the `.ape` extension. Even if the symlink includes the `.ape` extension, unless it also generates a direct symlink for the APE launcher to find, it will fail to launch. This patches the APE launcher to walk symlink chains back to the final symlink. It adds a new function to the APE launcher binary: `WalkToIndirect`. The function uses two buffers to walk the symlinks until it finds a path that is not a symlink. The previous path _must_ be the indirect symlink. It needs to use an additional buffer to read the linkname. It will return zero if the new path can be stored in the provided buffer. If not, it will return the number of bytes to allocate. We use this functionality to store the path in the same character buffer used by the system provided environment. If there is not enough space, we then allocate on the stack and store it there. An alternative option is to build a launcher binary that uses runfiles to find both the APE launcher and binary. The preference was to avoid this to be resilient against subprocess calls that do not forward on runfiles information. It would also require a programming language that is hermetic, which would likely be `rules_go`. This is a heavy dependency for users just wanting to launch APE binaries on Apple Silicon. We are _already_ requiring the download of a hermetic C toolchain on Apple Silicon so it makes sense to reuse this download to provide the functionality needed for hermetic launching on Apple Silicon. This is an inperfect solution: all other platforms assimilate a single binary that can be launched on the system. Unfortunately, due to the limitations of the APE format on Apple Silicon, the APE launcher always needs to be in the loop as native binaries are assimilated to ELF. The downstream user now needs to be aware that on Apple Silicon, there are runfiles that need to be forwarded which limits the simple message that an `ape_assimilate` generates a native binary that can be launched. If APE learns how to generate launchable binaries on Apple Silicon, we can switch to `_direct` from `_indirect` and drop the `ape-m1.patch`.
Loading