Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a runfiles library #1310

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/runfiles/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
load("//swift:swift.bzl", "swift_binary")

swift_binary(
name = "binary",
srcs = ["main.swift"],
data = [
"data/sample.txt",
],
visibility = ["//visibility:public"],
deps = [
"//swift/runfiles",
],
)
1 change: 1 addition & 0 deletions examples/runfiles/data/sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello runfiles
17 changes: 17 additions & 0 deletions examples/runfiles/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import BazelRunfiles

let runfiles = try Runfiles.create(sourceRepository: BazelRunfilesConstants.currentRepository)

// Runfiles lookup paths have the form `my_workspace/package/file`.
// Runfiles path lookup may return nil.
guard let runFile = runfiles.rlocation("build_bazel_rules_swift/examples/runfiles/data/sample.txt") else {
fatalError("couldn't resolve runfile")
}

print(runFile)

// Runfiles path lookup may return a non-existent path.
let content = try String(contentsOf: runFile, encoding: .utf8)

assert(content == "Hello runfiles")
print(content)
3 changes: 3 additions & 0 deletions swift/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ bzl_library(
"//swift/internal:compiling",
"//swift/internal:feature_names",
"//swift/internal:linking",
"//swift/internal:runfiles",
"//swift/internal:toolchain_utils",
"//swift/internal:utils",
"@bazel_skylib//lib:paths",
Expand Down Expand Up @@ -189,6 +190,7 @@ bzl_library(
"//swift/internal:feature_names",
"//swift/internal:linking",
"//swift/internal:output_groups",
"//swift/internal:runfiles",
"//swift/internal:toolchain_utils",
"//swift/internal:utils",
"@bazel_skylib//lib:dicts",
Expand Down Expand Up @@ -252,6 +254,7 @@ bzl_library(
"//swift/internal:feature_names",
"//swift/internal:linking",
"//swift/internal:output_groups",
"//swift/internal:runfiles",
"//swift/internal:swift_symbol_graph_aspect",
"//swift/internal:toolchain_utils",
"//swift/internal:utils",
Expand Down
6 changes: 6 additions & 0 deletions swift/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ bzl_library(
],
)

bzl_library(
name = "runfiles",
srcs = ["runfiles.bzl"],
visibility = ["//swift:__subpackages__"],
)

bzl_library(
name = "swift_autoconfiguration",
srcs = ["swift_autoconfiguration.bzl"],
Expand Down
41 changes: 41 additions & 0 deletions swift/internal/runfiles.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2024 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Internal API to generate constants needed by the runfiles library"""

def include_runfiles_constants(label, actions, all_deps):
"""TODO: Do this the right way.

Args:
label: The label of the target for which the Swift files are being generated.
actions: The actions object used to declare the files to be generated and the actions that generate them.
all_deps: The list of public dependencies of the target.

Returns:
A list containing the runfiles constant declared file if applicable;
otherwise an empty list.
"""
matches = [dep for dep in all_deps if dep.label == Label("@build_bazel_rules_swift//swift/runfiles:runfiles")]
if len(matches) > 0:
repo_name_file = actions.declare_file("Runfiles+Constants.swift")
actions.write(
output = repo_name_file,
content = """
internal enum BazelRunfilesConstants {{
static let currentRepository = "{}"
}}
""".format(label.workspace_name),
)
return [repo_name_file]
return []
10 changes: 10 additions & 0 deletions swift/runfiles/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("//swift:swift_library.bzl", "swift_library")

swift_library(
name = "runfiles",
srcs = [
"Runfiles.swift",
],
module_name = "BazelRunfiles",
visibility = ["//visibility:public"],
)
74 changes: 74 additions & 0 deletions swift/runfiles/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Swift BazelRunfiles library

This is a Bazel Runfiles lookup library for Bazel-built Swift binaries and tests.

Learn about runfiles: read [Runfiles guide](https://bazel.build/extending/rules#runfiles)
or watch [Fabian's BazelCon talk](https://www.youtube.com/watch?v=5NbgUMH1OGo).

## Usage

1. Depend on this runfiles library from your build rule:

```python
swift_binary(
name = "my_binary",
...
data = ["//path/to/my/data.txt"],
deps = ["@bazel_swift//swift/runfiles"],
)
```

2. Include the runfiles library:

```swift
import BazelRunfiles
```

3. Create a Runfiles instance and use `rlocation` to look up runfile urls:

```swift
import BazelRunfiles

let runfiles = try? Runfiles.create(sourceRepository: BazelRunfilesConstants.currentRepository)

let fileURL = runfiles?.rlocation("my_workspace/path/to/my/data.txt")
```

> The code above creates a manifest- or directory-based implementation based on
the environment variables in `Process.processInfo.environment`.
See `Runfiles.create()` for more info.

> The Runfiles.create function uses the runfiles manifest and the runfiles
directory from the RUNFILES_MANIFEST_FILE and RUNFILES_DIR environment
variables. If not present, the function looks for the manifest and directory
near CommandLine.arguments.first (argv[0]), the path of the main program.

> The BazelRunfilesConstants.currentRepository symbol is available in every
target that depends on the runfiles library.

If you want to start subprocesses, and the subprocess can't automatically
cerisier marked this conversation as resolved.
Show resolved Hide resolved
find the correct runfiles directory, you can explicitly set the right
environment variables for them:

```swift
import Foundation
import BazelRunfiles

let runfiles = try? Runfiles.create(sourceRepository: BazelRunfilesConstant.currentRepository)

guard let executableURL = runfiles.rlocation("my_workspace/path/to/binary") else {
return
}

let process = Process()
process.executableURL = executableURL
process.environment = runfiles.envVars()

do {
// Launch the process
try process.run()
process.waitUntilExit()
} catch {
// ...
}
```
Loading