Skip to content

Commit 1bc8f27

Browse files
cerisierbrentleyjonesluispadronfmeum
authored
Introduce a pure Swift runfiles library (#1310)
Fixes #890 # Main implementation I followed guidance from @fmeum and based this implementation on the runfiles library of `rules_python` where applicable since this was pointed as the reference implementation. In addition to `rules_python` implementation, this implementation uses a similar mechanism as the C++ implementation for deducing the `RUNFILES_DIR` and `RUNFILES_MANIFEST_FILE` location based on `argv0`. --------- Co-authored-by: Brentley Jones <[email protected]> Co-authored-by: Luis Padron <[email protected]> Co-authored-by: Fabian Meumertzheim <[email protected]>
1 parent aae0cab commit 1bc8f27

File tree

9 files changed

+979
-0
lines changed

9 files changed

+979
-0
lines changed

examples/runfiles/BUILD

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
load("//swift:swift.bzl", "swift_binary")
2+
3+
swift_binary(
4+
name = "runfiles_example",
5+
srcs = ["main.swift"],
6+
data = [
7+
"data/sample.txt",
8+
],
9+
visibility = ["//visibility:public"],
10+
deps = [
11+
"//swift/runfiles",
12+
],
13+
)

examples/runfiles/data/sample.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello runfiles

examples/runfiles/main.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import BazelRunfiles
2+
3+
do {
4+
let runfiles = try Runfiles.create()
5+
// Runfiles lookup paths have the form `my_workspace/package/file`.
6+
// Runfiles path lookup may throw.
7+
let fileURL = try runfiles.rlocation("build_bazel_rules_swift/examples/runfiles/data/sample.txt")
8+
print("file: \(fileURL)")
9+
10+
// Runfiles path lookup may return a non-existent path.
11+
let content = try String(contentsOf: fileURL, encoding: .utf8)
12+
13+
assert(content == "Hello runfiles")
14+
print(content)
15+
} catch {
16+
print("runfiles error: \(error)")
17+
}

swift/internal/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ bzl_library(
214214
],
215215
)
216216

217+
bzl_library(
218+
name = "runfiles",
219+
srcs = ["runfiles.bzl"],
220+
visibility = ["//swift:__subpackages__"],
221+
)
222+
217223
bzl_library(
218224
name = "swift_autoconfiguration",
219225
srcs = ["swift_autoconfiguration.bzl"],

swift/runfiles/BUILD

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
load("//swift:swift_library.bzl", "swift_library")
2+
3+
swift_library(
4+
name = "runfiles",
5+
srcs = [
6+
"Runfiles.swift",
7+
],
8+
module_name = "BazelRunfiles",
9+
visibility = ["//visibility:public"],
10+
)

swift/runfiles/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Swift BazelRunfiles library
2+
3+
This is a Bazel Runfiles lookup library for Bazel-built Swift binaries and tests.
4+
5+
Learn about runfiles: read [Runfiles guide](https://bazel.build/extending/rules#runfiles)
6+
or watch [Fabian's BazelCon talk](https://www.youtube.com/watch?v=5NbgUMH1OGo).
7+
8+
## Usage
9+
10+
1. Depend on this runfiles library from your build rule:
11+
12+
```python
13+
swift_binary(
14+
name = "my_binary",
15+
...
16+
data = ["//path/to/my/data.txt"],
17+
deps = ["@build_bazel_rules_swift//swift/runfiles"],
18+
)
19+
```
20+
21+
2. Include the runfiles library:
22+
23+
```swift
24+
import BazelRunfiles
25+
```
26+
27+
3. Create a Runfiles instance and use `rlocation` to look up runfile urls:
28+
29+
```swift
30+
import BazelRunfiles
31+
32+
do {
33+
let runfiles = try Runfiles.create()
34+
let fileURL = try runfiles.rlocation("my_workspace/path/to/my/data.txt")
35+
print("file: \(fileURL)")
36+
} catch {
37+
print("runfiles error: \(error)")
38+
}
39+
```
40+
41+
The code above:
42+
43+
- Creates a manifest- or directory-based implementation based on
44+
the environment variables in `Process.processInfo.environment`.
45+
See `Runfiles.create()` for more info.
46+
- The `Runfiles.create` function uses the runfiles manifest and the runfiles
47+
directory from the `RUNFILES_MANIFEST_FILE` and `RUNFILES_DIR` environment
48+
variables. If not present, the function looks for the manifest and directory
49+
near `CommandLine.arguments.first` (e.g. `argv[0]` the path of the main program).
50+
51+
If you want to start subprocesses, the runfiles library helps you set the required environment variables for them to find their runfiles:
52+
53+
```swift
54+
import BazelRunfiles
55+
import Foundation
56+
57+
do {
58+
59+
let runfiles = try Runfiles.create()
60+
let executableURL = try runfiles.rlocation("my_workspace/path/to/binary")
61+
62+
let process = Process()
63+
process.executableURL = executableURL
64+
process.environment = runfiles.envVars()
65+
66+
do {
67+
// Launch the process
68+
try process.run()
69+
process.waitUntilExit()
70+
} catch {
71+
// ...
72+
}
73+
} catch {
74+
fatalError("runfiles error: \(error)")
75+
}
76+
```

0 commit comments

Comments
 (0)