From a092da65b0e69b11e8ac549a868f940957c83847 Mon Sep 17 00:00:00 2001 From: Joseph Gette Date: Thu, 13 Aug 2026 13:48:45 +0200 Subject: [PATCH 1/2] fix(install): follow Windows runfiles junctions when copying files --enable_runfiles on Windows creates directory junctions for file runfiles. Junctions cannot point at files, so shutil.copyfile fails with Permission denied. Resolve the reparse point before copying. --- pkg/private/install.py.tpl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/private/install.py.tpl b/pkg/private/install.py.tpl index 5242b18a..66e775dc 100644 --- a/pkg/private/install.py.tpl +++ b/pkg/private/install.py.tpl @@ -81,6 +81,16 @@ class NativeInstaller(object): def _do_file_copy(self, src, dest): logging.debug("COPY %s <- %s", dest, src) + # Windows --enable_runfiles uses directory junctions for every runfile. + # Junctions cannot point at files, so open() fails with Permission denied. + # Follow the reparse point to the real bazel-out file. + if sys.platform == "win32": + try: + target = os.readlink(src) + except OSError: + target = None + if target: + src = target # Copy to a temporary directory and then move it to the destination. # This ensures code-signed executables on certain platforms # behave correctly. From 2bd3f8c1ce8abaa3eb145a8d4146f3178aa0cda7 Mon Sep 17 00:00:00 2001 From: Joseph Gette Date: Mon, 17 Aug 2026 08:50:42 +0200 Subject: [PATCH 2/2] Use os.path.realpath instead of platform-specific junction resolution Suggested by @alopezz: realpath resolves reparse points on Windows and symlinks on Unix, removing the need for sys.platform conditional and os.readlink try/except. --- pkg/private/install.py.tpl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/private/install.py.tpl b/pkg/private/install.py.tpl index 66e775dc..4c66caa4 100644 --- a/pkg/private/install.py.tpl +++ b/pkg/private/install.py.tpl @@ -84,13 +84,7 @@ class NativeInstaller(object): # Windows --enable_runfiles uses directory junctions for every runfile. # Junctions cannot point at files, so open() fails with Permission denied. # Follow the reparse point to the real bazel-out file. - if sys.platform == "win32": - try: - target = os.readlink(src) - except OSError: - target = None - if target: - src = target + src = os.path.realpath(src) # Copy to a temporary directory and then move it to the destination. # This ensures code-signed executables on certain platforms # behave correctly.