Skip to content

Commit 38f29af

Browse files
authored
Make pkg_install work without "runfiles" (Windows default) (#984)
On Windows, `pkg_install` wouldn't work out of the box without passing `--enable_runfiles` to Bazel: ```py windows> bazel run [redacted]//:install -- --destdir=d:\est [...] INFO: Running command line: [redacted]/install.exe <args omitted> Traceback (most recent call last): File "[redacted]\install_install_script.py", line 307, in <module> sys.exit(main(sys.argv)) ^^^^^^^^^^^^^^ File "[redacted]\install_install_script.py", line 299, in main installer.include_manifest_path(f) File "[redacted]\install_install_script.py", line 182, in include_manifest_path with open(path, 'r') as fh: ^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: '../[redacted]/install_install_script-install-manifest.json' ``` In order to accomodate the various use cases (`build`, `test` and `run` actions; enabled and disabled runfiles), the present change proposes to leverage `rules_python`'s "runfiles" lookup library: https://rules-python.readthedocs.io/en/latest/api/py/runfiles/runfiles.runfiles.html For good measure, the change also enables `bazel test //tests/install/...` in CI for Windows. This might close #387.
2 parents 6cdaba6 + 57f4b7d commit 38f29af

File tree

4 files changed

+20
-28
lines changed

4 files changed

+20
-28
lines changed

.bazelci/tests.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ win_tests: &win_tests
5656
- "-//tests/mappings:utf8_manifest_test"
5757
- "-//tests/mappings/filter_directory/..."
5858
- "-//tests/zip:unicode_test"
59-
# See #387
60-
- "-//tests/install/..."
6159
# rpmbuild(8) is not supported on Windows
6260
- "-//tests/rpm/..."
6361
- "-//pkg/legacy/tests/rpm/..."

pkg/install.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def pkg_install(name, srcs, destdir = None, **kwargs):
185185
name = name,
186186
srcs = [":" + name + "_install_script"],
187187
main = name + "_install_script.py",
188-
deps = [Label("//pkg/private:manifest")],
188+
deps = [Label("//pkg/private:manifest"), Label("@rules_python//python/runfiles")],
189189
srcs_version = "PY3",
190190
python_version = "PY3",
191191
**kwargs

pkg/private/install.py.tpl

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,29 @@ import argparse
2121
import logging
2222
import os
2323
import pathlib
24+
import posixpath
2425
import shutil
2526
import sys
2627
import tempfile
2728

2829
from pkg.private import manifest
30+
from python.runfiles import runfiles
2931

3032
# Globals used for runfile path manipulation.
3133
#
3234
# These are necessary because runfiles are different when used as a part of
3335
# `bazel build` and `bazel run`. # See also
3436
# https://docs.bazel.build/versions/4.1.0/skylark/rules.html#tools-with-runfiles
37+
# https://rules-python.readthedocs.io/en/latest/api/py/runfiles/runfiles.runfiles.html
38+
RUNFILES = runfiles.Create()
39+
REPOSITORY = RUNFILES.CurrentRepository() or "{WORKSPACE_NAME}" # the empty string denotes the "_main" repository
3540

36-
# Bazel's documentation claims these are set when calling `bazel run`, but not other
37-
# modes, like in `build` or `test`. We'll see.
38-
CALLED_FROM_BAZEL_RUN = bool(os.getenv("BUILD_WORKSPACE_DIRECTORY") and
39-
os.getenv("BUILD_WORKING_DIRECTORY"))
41+
def locate(short_path):
42+
"""Resolve a path relative to the current repository and return its "runfile" location.
4043
41-
WORKSPACE_NAME = "{WORKSPACE_NAME}"
42-
# This seems to be set when running in `bazel build` or `bazel test`
43-
# TODO(#382): This may not be the case in Windows.
44-
RUNFILE_PREFIX = os.path.join(os.getenv("RUNFILES_DIR"), WORKSPACE_NAME) if os.getenv("RUNFILES_DIR") else None
44+
Uses `posixpath` because runfile lookups always use forward slashes, even on Windows.
45+
"""
46+
return RUNFILES.Rlocation(posixpath.normpath(posixpath.join(REPOSITORY, short_path)))
4547

4648

4749
# This is named "NativeInstaller" because it makes use of "native" python
@@ -184,10 +186,10 @@ class NativeInstaller(object):
184186
manifest_entries = manifest.read_entries_from(manifest_fh)
185187

186188
for entry in manifest_entries:
187-
# Swap out the source with the actual "runfile" location if we're
188-
# called as a part of the build rather than "bazel run"
189-
if not CALLED_FROM_BAZEL_RUN and entry.src is not None:
190-
entry.src = os.path.join(RUNFILE_PREFIX, entry.src)
189+
# Swap out the source with the actual "runfile" location, except for
190+
# symbolic links as their targets denote installation paths
191+
if entry.type != manifest.ENTRY_IS_LINK and entry.src is not None:
192+
entry.src = locate(entry.src)
191193
# Prepend the destdir path to all installation paths, if one is
192194
# specified.
193195
if self.destdir is not None:
@@ -287,17 +289,7 @@ def main(args):
287289
wipe_destdir=args.wipe_destdir,
288290
)
289291

290-
if not CALLED_FROM_BAZEL_RUN and RUNFILE_PREFIX is None:
291-
logging.critical("RUNFILES_DIR must be set in your environment when this is run as a bazel build tool.")
292-
logging.critical("This is most likely an issue on Windows. See https://github.com/bazelbuild/rules_pkg/issues/387.")
293-
return 1
294-
295-
for f in ["{MANIFEST_INCLUSION}"]:
296-
if CALLED_FROM_BAZEL_RUN:
297-
installer.include_manifest_path(f)
298-
else:
299-
installer.include_manifest_path(os.path.join(RUNFILE_PREFIX, f))
300-
292+
installer.include_manifest_path(locate("{MANIFEST_INCLUSION}"))
301293
installer.do_the_thing()
302294

303295

tests/install/test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626

2727
class PkgInstallTestBase(unittest.TestCase):
28+
_extension = ".exe" if os.name == "nt" else ""
29+
2830
@classmethod
2931
def setUpClass(cls):
3032
cls.runfiles = runfiles.Create()
@@ -47,7 +49,7 @@ def setUpClass(cls):
4749
env = {}
4850
env.update(cls.runfiles.EnvVars())
4951
subprocess.check_call([
50-
cls.runfiles.Rlocation("rules_pkg/tests/install/test_installer"),
52+
cls.runfiles.Rlocation(f"rules_pkg/tests/install/test_installer{cls._extension}"),
5153
"--destdir", cls.installdir,
5254
"--verbose",
5355
],
@@ -215,7 +217,7 @@ def test_wipe(self):
215217
(self.installdir / "should_be_deleted.txt").touch()
216218

217219
subprocess.check_call([
218-
self.runfiles.Rlocation("rules_pkg/tests/install/test_installer"),
220+
self.runfiles.Rlocation(f"rules_pkg/tests/install/test_installer{self._extension}"),
219221
"--destdir", self.installdir,
220222
"--wipe_destdir",
221223
],

0 commit comments

Comments
 (0)