Skip to content

Commit 526cd4b

Browse files
authored
Add base simulator handling logic (#112)
1 parent 5293830 commit 526cd4b

File tree

7 files changed

+177
-80
lines changed

7 files changed

+177
-80
lines changed

Example/.vscode/tasks.json

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,33 @@
22
"version": "2.0.0",
33
"tasks": [
44
{
5-
"label": "Build HelloWorld",
5+
"label": "Select Simulator for Apple Development",
66
"type": "shell",
7-
"command": "bazelisk",
8-
"args": [
9-
"build",
10-
"//HelloWorld"
11-
],
12-
"group": {
13-
"kind": "build",
14-
"isDefault": true
7+
"command": "./scripts/select_simulator.sh",
8+
"presentation": {
9+
"reveal": "always",
10+
"focus": true,
11+
"panel": "dedicated"
1512
},
16-
"problemMatcher": [
17-
{
18-
"owner": "swift",
19-
"source": "swift",
20-
"fileLocation": [
21-
"relative",
22-
"${workspaceFolder}"
23-
],
24-
"pattern": {
25-
"regexp": "^(.+?):(\\d+):(\\d+):\\s+(error|warning|note):\\s+(.*)$",
26-
"file": 1,
27-
"line": 2,
28-
"column": 3,
29-
"severity": 4,
30-
"message": 5
31-
}
32-
},
33-
],
34-
"runOptions": {
35-
"instanceLimit": 1
36-
}
13+
"problemMatcher": []
3714
},
3815
{
39-
"label": "Test HelloWorldTests",
16+
"label": "Build HelloWorld",
4017
"type": "shell",
41-
"command": "bazelisk",
42-
"args": [
43-
"test",
44-
"//HelloWorld:HelloWorldTests"
45-
],
18+
"command": "./scripts/lldb_build.sh",
19+
"options": {
20+
"env": {
21+
"BAZEL_LABEL_TO_RUN": "//HelloWorld:HelloWorld",
22+
"BAZEL_EXTRA_BUILD_FLAGS": "",
23+
}
24+
},
4625
"group": {
47-
"kind": "test",
48-
"isDefault": true
26+
"kind": "build",
4927
},
5028
"problemMatcher": [
5129
{
52-
"owner": "swift",
53-
"source": "swift",
30+
"owner": "bazelisk",
31+
"source": "bazelisk",
5432
"fileLocation": [
5533
"relative",
5634
"${workspaceFolder}"
@@ -62,12 +40,9 @@
6240
"column": 3,
6341
"severity": 4,
6442
"message": 5
65-
}
43+
},
6644
},
6745
],
68-
"runOptions": {
69-
"instanceLimit": 1
70-
}
7146
},
7247
// Hidden never-ending task that handles the launch / debugging bits for Cmd+Shift+D.
7348
// The problemMatcher field defines when the task is effectively ready to be debugged
@@ -78,7 +53,9 @@
7853
"command": "./scripts/lldb_launch_and_debug.sh",
7954
"options": {
8055
"env": {
81-
"BAZEL_LABEL_TO_RUN": "//HelloWorld:HelloWorld"
56+
"BAZEL_LABEL_TO_RUN": "//HelloWorld:HelloWorld",
57+
"BAZEL_EXTRA_BUILD_FLAGS": "",
58+
"BAZEL_LAUNCH_ARGS": ""
8259
}
8360
},
8461
"presentation": {
@@ -88,40 +65,49 @@
8865
"isBackground": true,
8966
"problemMatcher": [
9067
{
91-
"owner": "swift",
92-
"source": "swift",
68+
"owner": "bazelisk",
69+
"source": "bazelisk",
9370
"fileLocation": [
9471
"relative",
9572
"${workspaceFolder}"
9673
],
9774
"pattern": {
98-
"regexp": "^(.+?):(\\d+):(\\d+):\\s+(error|warning|note):\\s+(.*)$",
99-
"file": 1,
100-
"line": 2,
101-
"column": 3,
102-
"severity": 4,
103-
"message": 5
75+
"regexp": "launcher_error in (.*): (.*)",
76+
"kind": "file",
77+
"file": 1,
78+
"message": 2,
79+
},
80+
"background": {
81+
"activeOnStart": true,
82+
"beginsPattern": "^Starting launch task\\.\\.\\.$",
83+
"endsPattern": "^.*Launched with PID: .*"
10484
}
10585
},
10686
{
107-
"pattern": [
108-
{
109-
"regexp": "\\b\\B",
110-
"file": 1,
111-
"location": 2,
112-
"message": 3
113-
}
87+
"owner": "bazelisk",
88+
"source": "bazelisk",
89+
"fileLocation": [
90+
"relative",
91+
"${workspaceFolder}"
11492
],
93+
"pattern": {
94+
"regexp": "^(.+?):(\\d+):(\\d+):\\s+(error|warning|note):\\s+(.*)$",
95+
"file": 1,
96+
"line": 2,
97+
"column": 3,
98+
"severity": 4,
99+
"message": 5
100+
},
115101
"background": {
116102
"activeOnStart": true,
117-
"beginsPattern": "^.*Building .*",
103+
"beginsPattern": "^Starting launch task\\.\\.\\.$",
118104
"endsPattern": "^.*Launched with PID: .*"
119105
}
120-
}
106+
},
121107
],
122108
"runOptions": {
123109
"instanceLimit": 1
124110
}
125-
}
111+
},
126112
]
127113
}

Example/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# Example (Cursor/VSCode)
22

3-
This is a simple **iOS** app that lets you see sourcekit-bazel-bsp in action. This example and instructions were designed specifically for **Cursor**, but should also work for VSCode.
3+
This is a simple collection of iOS/watchOS/macOS apps that lets you see sourcekit-bazel-bsp in action. This example and instructions were designed specifically for **Cursor**, but should also work for VSCode.
44

55
## Initial Setup Instructions
66

77
- Make sure you fulfill the toolchain requirements for sourcekit-bazel-bsp, available at the main README.
8-
- Install [bazelisk](https://github.com/bazelbuild/bazelisk) if you haven't already.
9-
- On macOS: `brew install bazelisk`
8+
- Install [bazelisk](https://github.com/bazelbuild/bazelisk) if you haven't already: `brew install bazelisk`
109
- Make sure you're using **Xcode 26** as this is what this project was developed with.
10+
- Install [fzf](https://github.com/junegunn/fzf) if you haven't already: `brew install fzf`
11+
- This is used to power the simulator selection features.
1112
- On this folder, run:
1213
- `bazelisk run //HelloWorld:setup_sourcekit_bsp_example_project`
1314
- (Optional) Follow the instructions from the main README regarding configuring a custom SourceKit-LSP binary.
@@ -17,12 +18,12 @@ This is a simple **iOS** app that lets you see sourcekit-bazel-bsp in action. Th
1718

1819
After performing these steps, you should already be able to see the basic indexing features in action. It may take a minute or two the first time, but you can see the progress at the bottom of the IDE. You should also be able to see a new `SourceKit Language Server` option on the `Output` tab that shows sourcekit-lsp's internal logs, and after modifying a file for the first time an additional `SourceKit-LSP: Indexing` tab will pop up containing more detailed logs from both tools.
1920

20-
## Building and Testing
21+
## Building and Debugging
2122

22-
- To run a regular build: Cmd+Shift+B -> `Build HelloWorld`
23-
- To run a debug build, launch the simulator and attach `lldb`: Cmd+Shift+D -> `Debug HelloWorld (Example)`
24-
- This requires a **iOS 18.6 iPhone 16 Pro** simulator installed as this is what the example project is currently configured for. Make sure you have one available, as otherwise the build will fail.
25-
- To test: Cmd+Shift+P -> `Run Test Task` -> `Test HelloWorldTests`
23+
- To build the example iOS app: `Terminal` -> `Run Build Task...` -> `Build HelloWorld`
24+
- To run a debug build, launch a simulator and attach `lldb`:
25+
- First, select an appropriate iOS simulator via `Terminal` -> `Run Task...` -> `Select Simulator for Apple Development`. The minimum OS version of the example app is **iOS 17.0**.
26+
- Then: Cmd+Shift+D -> `Debug HelloWorld (Example)`. After building, the IDE will automatically launch your selected simulator and attach it to the IDE's lldb extension.
2627

2728
## Considerations
2829

Example/scripts/lldb_build.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
source "scripts/lldb_build_common.sh"
4+
run_bazel "build"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# Stores common functionality for build and launch tasks.
4+
5+
set -e
6+
7+
export WORKSPACE_ROOT
8+
WORKSPACE_ROOT=$(pwd)
9+
10+
export ADDITIONAL_FLAGS=()
11+
ADDITIONAL_FLAGS+=("--keep_going")
12+
ADDITIONAL_FLAGS+=("--color=yes")
13+
14+
if [ -n "${BAZEL_EXTRA_BUILD_FLAGS:-}" ]; then
15+
ADDITIONAL_FLAGS+=("${BAZEL_EXTRA_BUILD_FLAGS[@]}")
16+
fi
17+
18+
LAUNCH_ARGS_ARRAY=()
19+
if [ -n "${BAZEL_LAUNCH_ARGS:-}" ]; then
20+
read -ra LAUNCH_ARGS_ARRAY <<< "$BAZEL_LAUNCH_ARGS"
21+
fi
22+
23+
function run_bazel() {
24+
local command="$1"
25+
bazelisk "${command}" "${BAZEL_LABEL_TO_RUN}" "${ADDITIONAL_FLAGS[@]}" ${LAUNCH_ARGS_ARRAY[@]:+-- "${LAUNCH_ARGS_ARRAY[@]}"}
26+
}

Example/scripts/lldb_launch_and_debug.sh

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
#!/bin/bash
22

3-
set -e
3+
source "scripts/lldb_build_common.sh"
44

5-
echo "Building ${BAZEL_LABEL_TO_RUN}..."
5+
echo "Starting launch task..."
66

7-
WORKSPACE_ROOT=$(pwd)
7+
function emit_launcher_error() {
8+
echo "launcher_error in ${BASH_SOURCE[0]}: ${1}"
9+
exit 1
10+
}
11+
12+
# Read information regarding the user's selected simulator.
13+
SIMULATOR_INFO_FILE=${WORKSPACE_ROOT}/.bsp/skbsp_generated/simulator_info.txt
14+
SIMULATOR_INFO=""
15+
if [ -f "${SIMULATOR_INFO_FILE}" ]; then
16+
SIMULATOR_INFO=$(cat "${SIMULATOR_INFO_FILE}")
17+
echo "Will use simulator: ${SIMULATOR_INFO}"
18+
else
19+
emit_launcher_error "No simulator selected! You need to first run the 'Select Simulator for Apple Development' task before being able to run this script."
20+
fi
821

922
# When asking Bazel to launch a simulator, we need to intercept
1023
# the launched process' PID to be able to debug it later on.
@@ -24,20 +37,32 @@ cat > ${BAZEL_INFO_JSON} <<EOF
2437
}
2538
EOF
2639

27-
ADDITIONAL_FLAGS=()
28-
ADDITIONAL_FLAGS+=("--keep_going")
29-
ADDITIONAL_FLAGS+=("--color=yes")
3040
# We need --remote_download_regex because the files that lldb needs won't usually be downloaded by Bazel
3141
# when using flags like --remote_download_toplevel and the such.
3242
ADDITIONAL_FLAGS+=("--remote_download_regex=.*\.indexstore/.*|.*\.(a|cfg|c|C|cc|cl|cpp|cu|cxx|c++|def|h|H|hh|hpp|hxx|h++|hmap|ilc|inc|inl|ipp|tcc|tlh|tli|tpp|m|modulemap|mm|pch|swift|swiftdoc|swiftmodule|swiftsourceinfo|yaml)$")
43+
ADDITIONAL_FLAGS+=("--@build_bazel_rules_apple//apple/build_settings:ios_device=${SIMULATOR_INFO}")
3344

3445
BAZEL_APPLE_PREFER_PERSISTENT_SIMS=1 \
35-
BAZEL_APPLE_LAUNCH_INFO_PATH=${LAUNCH_INFO_JSON} \
46+
BAZEL_APPLE_LAUNCH_INFO_PATH="${LAUNCH_INFO_JSON}" \
3647
BAZEL_SIMCTL_LAUNCH_FLAGS="--wait-for-debugger --stdout=$(tty) --stderr=$(tty)" \
37-
bazelisk run "${BAZEL_LABEL_TO_RUN}" "${ADDITIONAL_FLAGS[@]}"
48+
run_bazel "run"
49+
50+
# Remove the default lldbinit file created by rules_xcodeproj if it exists.
51+
# This prevents Xcode details from leaking over to our builds.
52+
# It's safe to delete this because rules_xcodeproj creates it on every build.
53+
RULES_XCODEPROJ_LLDBINIT_FILE=${HOME}/.lldbinit-rules_xcodeproj
54+
if [ -f "${RULES_XCODEPROJ_LLDBINIT_FILE}" ]; then
55+
echo "Removing rules_xcodeproj's lldbinit file..."
56+
rm -f "${RULES_XCODEPROJ_LLDBINIT_FILE}" || true
57+
fi
3858

3959
PID=$(jq -r '.pid' "${LAUNCH_INFO_JSON}")
4060

61+
if [ -z "${PID}" ]; then
62+
emit_launcher_error "No PID found in ${LAUNCH_INFO_JSON}"
63+
fi
64+
65+
# Note: This string is hardcoded in tasks.json file to launch LLDB-DAP.
4166
echo "Launched with PID: ${PID}"
4267

4368
# Keep the terminal alive until the app is killed.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
# Script that displays all available simulators to the user
6+
# and writes the selected one's UUID to a file. This file
7+
# is then parsed by lldb_launch_and_debug.sh to pass the
8+
# appropriate device flags to Bazel.
9+
10+
WORKSPACE_ROOT=$(pwd)
11+
OUTPUT_FILE=${WORKSPACE_ROOT}/.bsp/skbsp_generated/simulator_info.txt
12+
13+
# Get list of all available simulators with their UDIDs
14+
# Format: "Device Name (platform X.Y) - UDID"
15+
SIMULATORS=$(xcrun simctl list devices available -j | jq -r '
16+
.devices | to_entries[] |
17+
.key as $runtime |
18+
.value[] |
19+
($runtime | split("SimRuntime.")[1] | split("-") | "\(.[0]) \(.[1]).\(.[2])") as $version |
20+
"\(.name) (\($version)) - \(.udid)"
21+
' | sort)
22+
23+
if [ -z "$SIMULATORS" ]; then
24+
echo "error: No available simulators found."
25+
exit 1
26+
fi
27+
28+
# Check if fzf is available
29+
if ! command -v fzf &> /dev/null; then
30+
echo "error: You need \`fzf\` to run this script. You can install it with 'brew install fzf'."
31+
exit 1
32+
fi
33+
34+
SELECTED=$(echo "$SIMULATORS" | fzf --height=20 --reverse --prompt="Select the simulator you'd like to use: ")
35+
36+
if [ -z "$SELECTED" ]; then
37+
echo "error: No simulator selected."
38+
exit 1
39+
fi
40+
41+
# Extract the UUID from the selection (split(" - ")[1])
42+
UUID=$(echo "$SELECTED" | awk -F' - ' '{print $2}')
43+
44+
mkdir -p "$(dirname "$OUTPUT_FILE")" || true
45+
echo -n "$UUID" > "$OUTPUT_FILE"
46+
47+
echo "Selected simulator: $SELECTED"
48+
echo "UUID ($UUID) successfully written to: $OUTPUT_FILE"
49+
echo "The selected simulator will now be used for your future builds."

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
- [x] All of the usual indexing features such as code completion, jump to definition, error annotations and so on, for both Swift and Obj-C (via the official [sourcekit-lsp](https://github.com/swiftlang/sourcekit-lsp))
1313
- [x] (Cursor / VSCode): Building and launching, all from within the IDE and directly via Bazel (no project generation required!)
1414
- [x] (Cursor / VSCode): Full `lldb` integration, allowing debugging from within the IDE just like Xcode (via [lldb-dap](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.lldb-dap), automatically provided by the official Swift extension)
15-
- [ ] (Cursor / VSCode): Simulator selection from within the IDE
15+
- [x] (Cursor / VSCode): Simulator selection from within the IDE (via custom IDE tasks)
1616
- [ ] (Cursor / VSCode): Automatic generation of build, launch, and debug tasks
1717
- [ ] (Cursor / VSCode): Test explorer & ability to run tests from within the IDE by clicking the tests individually, similarly to Xcode
1818
- [ ] Automatic index and build graph updates when adding / deleting files and targets (in other words, allowing the user to make heavy changes to the project without needing to restart the IDE)
@@ -76,6 +76,12 @@ The BSP by default works by attempting to build your library targets individuall
7676

7777
If this is undesirable, you can pass the `--compile-top-level` flag to make the BSP compile the target's **parent** instead, without any special flags. We recommend using this for projects that define fine-grained `*_build_test` targets and providing them as top-level targets for the BSP, as those don't suffer from this issue and thus enables maximum predictability and cacheability.
7878

79+
## Best Practices
80+
81+
- When working with large apps, consider being more explicit about the task you're going to do. This means that instead of importing the _entire app at all times_, try to import only a small group of test targets that you think will be required to perform the task. This will greatly increase the performance of the IDE.
82+
- For smaller apps, this doesn't make much difference and it should be fine to import the entire app.
83+
- Similarly, avoid wide-reaching wildcards like `//...`. Do so only at a smaller scale to avoid too many targets from being picked up.
84+
7985
## Troubleshooting
8086

8187
### Seeing sourcekit-bazel-bsp's logs

0 commit comments

Comments
 (0)