Skip to content

Commit 854e37e

Browse files
author
Karim Alweheshy
committed
make rule incremental to avoid expensive calculation
1 parent 2d78cae commit 854e37e

File tree

3 files changed

+146
-59
lines changed

3 files changed

+146
-59
lines changed

xcodeproj/internal/custom_toolchain.bzl

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ def _get_xcode_product_version(*, xcode_config):
1010
""")
1111

1212
version_components = raw_version.split(".")
13-
if len(version_components) < 4:
14-
# This will result in analysis cache misses, but it's better than
15-
# failing
16-
return raw_version
13+
if len(version_components) != 4:
14+
fail("""\
15+
`xcode_config.xcode_version` returned an unexpected number of components: {}
16+
""".format(len(version_components)))
1717

1818
return version_components[3]
1919

@@ -25,7 +25,10 @@ def _custom_toolchain_impl(ctx):
2525
toolchain_name_base = ctx.attr.toolchain_name
2626
toolchain_id = "com.rules_xcodeproj.{}.{}".format(toolchain_name_base, xcode_version)
2727
full_toolchain_name = "{}{}".format(toolchain_name_base, xcode_version)
28-
toolchain_dir = ctx.actions.declare_directory(full_toolchain_name + ".xctoolchain")
28+
29+
# Create two directories - one for symlinks, one for the final overridden toolchain
30+
symlink_toolchain_dir = ctx.actions.declare_directory(full_toolchain_name + ".symlink.xctoolchain")
31+
final_toolchain_dir = ctx.actions.declare_directory(full_toolchain_name + ".xctoolchain")
2932

3033
resolved_overrides = {}
3134
override_files = []
@@ -53,29 +56,36 @@ def _custom_toolchain_impl(ctx):
5356
if tool_name: # Skip empty names
5457
resolved_overrides[tool_name] = stub_file.path
5558

59+
# Instead of passing the full map of overrides, just pass the tool names
60+
# This way, changes to the stubs don't trigger a rebuild
61+
tool_names_list = " ".join(resolved_overrides.keys())
62+
5663
overrides_list = " ".join(["{}={}".format(k, v) for k, v in resolved_overrides.items()])
5764

58-
script_file = ctx.actions.declare_file(full_toolchain_name + "_setup.sh")
65+
symlink_script_file = ctx.actions.declare_file(full_toolchain_name + "_symlink.sh")
66+
override_script_file = ctx.actions.declare_file(full_toolchain_name + "_override.sh")
67+
override_marker = ctx.actions.declare_file(full_toolchain_name + ".override.marker")
5968

69+
# Create symlink script
6070
ctx.actions.expand_template(
6171
template = ctx.file._symlink_template,
62-
output = script_file,
72+
output = symlink_script_file,
6373
is_executable = True,
6474
substitutions = {
65-
"%overrides_list%": overrides_list,
66-
"%toolchain_dir%": toolchain_dir.path,
75+
"%tool_names_list%": tool_names_list,
76+
"%toolchain_dir%": symlink_toolchain_dir.path,
6777
"%toolchain_id%": toolchain_id,
6878
"%toolchain_name_base%": full_toolchain_name,
6979
"%xcode_version%": xcode_version,
7080
},
7181
)
7282

83+
# First run the symlinking script to set up the toolchain
7384
ctx.actions.run_shell(
74-
outputs = [toolchain_dir],
75-
inputs = override_files,
76-
tools = [script_file],
77-
mnemonic = "CreateCustomToolchain",
78-
command = script_file.path,
85+
outputs = [symlink_toolchain_dir],
86+
tools = [symlink_script_file],
87+
mnemonic = "CreateSymlinkToolchain",
88+
command = symlink_script_file.path,
7989
execution_requirements = {
8090
"local": "1",
8191
"no-cache": "1",
@@ -85,7 +95,36 @@ def _custom_toolchain_impl(ctx):
8595
use_default_shell_env = True,
8696
)
8797

88-
runfiles = ctx.runfiles(files = override_files + [script_file])
98+
if override_files:
99+
ctx.actions.expand_template(
100+
template = ctx.file._override_template,
101+
output = override_script_file,
102+
is_executable = True,
103+
substitutions = {
104+
"%final_toolchain_dir%": final_toolchain_dir.path,
105+
"%marker_file%": override_marker.path,
106+
"%overrides_list%": overrides_list,
107+
"%symlink_toolchain_dir%": symlink_toolchain_dir.path,
108+
"%tool_names_list%": tool_names_list,
109+
},
110+
)
111+
112+
ctx.actions.run_shell(
113+
inputs = override_files + [symlink_toolchain_dir],
114+
outputs = [final_toolchain_dir, override_marker],
115+
tools = [override_script_file],
116+
mnemonic = "ApplyCustomToolchainOverrides",
117+
command = override_script_file.path,
118+
execution_requirements = {
119+
"local": "1",
120+
"no-cache": "1",
121+
"no-sandbox": "1",
122+
"requires-darwin": "1",
123+
},
124+
use_default_shell_env = True,
125+
)
126+
127+
runfiles = ctx.runfiles(files = override_files + [symlink_script_file, override_script_file, override_marker])
89128

90129
toolchain_provider = ToolchainInfo(
91130
name = full_toolchain_name,
@@ -94,7 +133,7 @@ def _custom_toolchain_impl(ctx):
94133

95134
return [
96135
DefaultInfo(
97-
files = depset([toolchain_dir]),
136+
files = depset([final_toolchain_dir if override_files else symlink_toolchain_dir]),
98137
runfiles = runfiles,
99138
),
100139
toolchain_provider,
@@ -109,6 +148,10 @@ custom_toolchain = rule(
109148
doc = "Map from stub target to comma-separated list of tool names that should use that stub",
110149
),
111150
"toolchain_name": attr.string(mandatory = True),
151+
"_override_template": attr.label(
152+
allow_single_file = True,
153+
default = Label("//xcodeproj/internal/templates:custom_toolchain_override.sh"),
154+
),
112155
"_symlink_template": attr.label(
113156
allow_single_file = True,
114157
default = Label("//xcodeproj/internal/templates:custom_toolchain_symlink.sh"),
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
SYMLINK_TOOLCHAIN_DIR="%symlink_toolchain_dir%"
5+
FINAL_TOOLCHAIN_DIR="%final_toolchain_dir%"
6+
MARKER_FILE="%marker_file%"
7+
8+
# Get the default toolchain path
9+
DEFAULT_TOOLCHAIN=$(xcrun --find clang | sed 's|/usr/bin/clang$||')
10+
11+
OVERRIDES_FILE=$(mktemp)
12+
echo "%overrides_list%" > "$OVERRIDES_FILE"
13+
14+
TOOL_NAMES_FILE=$(mktemp)
15+
echo "%tool_names_list%" > "$TOOL_NAMES_FILE"
16+
17+
for tool_name in $(cat "$TOOL_NAMES_FILE"); do
18+
VALUE=""
19+
for override in $(cat "$OVERRIDES_FILE"); do
20+
KEY="${override%%=*}"
21+
if [[ "$KEY" == "$tool_name" ]]; then
22+
VALUE="${override#*=}"
23+
break
24+
fi
25+
done
26+
27+
if [[ -z "$VALUE" ]]; then
28+
echo "Error: No override found for tool: $tool_name"
29+
echo "ERROR: No override found for tool: $tool_name" >> "$MARKER_FILE"
30+
continue
31+
fi
32+
33+
find "$DEFAULT_TOOLCHAIN" -name "$tool_name" | while read -r default_tool_path; do
34+
rel_path="${default_tool_path#"$DEFAULT_TOOLCHAIN/"}"
35+
target_file="$FINAL_TOOLCHAIN_DIR/$rel_path"
36+
37+
mkdir -p "$(dirname "$target_file")"
38+
39+
override_path="$PWD/$VALUE"
40+
cp "$override_path" "$target_file"
41+
42+
echo "Copied $override_path to $target_file (rel_path: $rel_path)" >> "$MARKER_FILE"
43+
done
44+
done
45+
46+
# Clean up temporary files
47+
rm -f "$OVERRIDES_FILE"
48+
rm -f "$TOOL_NAMES_FILE"
49+
50+
# Copy the symlink toolchain to the final toolchain directory
51+
mkdir -p "$FINAL_TOOLCHAIN_DIR"
52+
cp -RP "$SYMLINK_TOOLCHAIN_DIR/"* "$FINAL_TOOLCHAIN_DIR/"
53+
54+
# Create a symlink to the toolchain in the user's Library directory
55+
HOME_TOOLCHAIN_NAME=$(basename "$FINAL_TOOLCHAIN_DIR")
56+
USER_TOOLCHAIN_PATH="/Users/$(id -un)/Library/Developer/Toolchains/$HOME_TOOLCHAIN_NAME"
57+
mkdir -p "$(dirname "$USER_TOOLCHAIN_PATH")"
58+
if [[ -e "$USER_TOOLCHAIN_PATH" || -L "$USER_TOOLCHAIN_PATH" ]]; then
59+
rm -rf "$USER_TOOLCHAIN_PATH"
60+
fi
61+
ln -sf "$PWD/$FINAL_TOOLCHAIN_DIR" "$USER_TOOLCHAIN_PATH"
62+
echo "Created symlink: $USER_TOOLCHAIN_PATH -> $PWD/$FINAL_TOOLCHAIN_DIR" >> "$MARKER_FILE"
63+
64+

xcodeproj/internal/templates/custom_toolchain_symlink.sh

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,67 +6,50 @@ TOOLCHAIN_NAME_BASE="%toolchain_name_base%"
66
TOOLCHAIN_DIR="%toolchain_dir%"
77
XCODE_VERSION="%xcode_version%"
88

9+
# Store the list of tools that will be overridden
10+
TOOL_NAMES_FILE=$(mktemp)
11+
echo "%tool_names_list%" > "$TOOL_NAMES_FILE"
12+
913
# Get Xcode version and default toolchain path
1014
DEFAULT_TOOLCHAIN=$(xcrun --find clang | sed 's|/usr/bin/clang$||')
1115
XCODE_RAW_VERSION=$(xcodebuild -version | head -n 1)
1216

13-
# Define toolchain names
17+
# Define toolchain names for reference only
1418
HOME_TOOLCHAIN_NAME="BazelRulesXcodeProj${XCODE_VERSION}"
15-
USER_TOOLCHAIN_PATH="/Users/$(id -un)/Library/Developer/Toolchains/${HOME_TOOLCHAIN_NAME}.xctoolchain"
16-
BUILT_TOOLCHAIN_PATH="$PWD/$TOOLCHAIN_DIR"
1719

1820
mkdir -p "$TOOLCHAIN_DIR"
1921

20-
# Parse overrides into a file for safer processing
21-
OVERRIDES_FILE=$(mktemp)
22-
echo "%overrides_list%" > "$OVERRIDES_FILE"
23-
2422
# Process all files from the default toolchain
2523
find "$DEFAULT_TOOLCHAIN" -type f -o -type l | while read -r file; do
26-
base_name="$(basename "$file")"
2724
rel_path="${file#"$DEFAULT_TOOLCHAIN/"}"
25+
base_name=$(basename "$rel_path")
2826

2927
# Skip ToolchainInfo.plist as we'll create our own
3028
if [[ "$rel_path" == "ToolchainInfo.plist" ]]; then
3129
continue
3230
fi
3331

34-
# Ensure parent directory exists
35-
mkdir -p "$TOOLCHAIN_DIR/$(dirname "$rel_path")"
36-
37-
# Check if this file has an override
38-
override_found=false
39-
override_value=""
40-
41-
for override in $(cat "$OVERRIDES_FILE"); do
42-
KEY="${override%%=*}"
43-
VALUE="${override#*=}"
44-
45-
if [[ "$KEY" == "$base_name" ]]; then
46-
override_value="$VALUE"
47-
override_found=true
32+
# Check if this file is in the list of tools to be overridden
33+
should_skip=0
34+
for tool_name in $(cat "$TOOL_NAMES_FILE"); do
35+
if [[ "$base_name" == "$tool_name" ]]; then
36+
# Skip creating a symlink for overridden tools
37+
echo "Skipping symlink for tool to be overridden: $base_name"
38+
should_skip=1
4839
break
4940
fi
5041
done
5142

52-
# Apply the override or create symlink
53-
if [[ "$override_found" == "true" ]]; then
54-
# Make path absolute
55-
override_path="$PWD/$override_value"
56-
cp "$override_path" "$TOOLCHAIN_DIR/$rel_path"
57-
58-
# Make executable if original is executable
59-
if [[ -x "$file" ]]; then
60-
chmod +x "$TOOLCHAIN_DIR/$rel_path"
61-
fi
62-
else
63-
# If no override found, symlink the original
64-
ln -sf "$file" "$TOOLCHAIN_DIR/$rel_path"
43+
if [[ $should_skip -eq 1 ]]; then
44+
continue
6545
fi
66-
done
6746

68-
# Clean up
69-
rm -f "$OVERRIDES_FILE"
47+
# Ensure parent directory exists
48+
mkdir -p "$TOOLCHAIN_DIR/$(dirname "$rel_path")"
49+
50+
# Create symlink to the original file
51+
ln -sf "$file" "$TOOLCHAIN_DIR/$rel_path"
52+
done
7053

7154
# Generate the ToolchainInfo.plist directly with Xcode version information
7255
cat > "$TOOLCHAIN_DIR/ToolchainInfo.plist" << EOF
@@ -96,8 +79,5 @@ cat > "$TOOLCHAIN_DIR/ToolchainInfo.plist" << EOF
9679
</plist>
9780
EOF
9881

99-
mkdir -p "$(dirname "$USER_TOOLCHAIN_PATH")"
100-
if [[ -e "$USER_TOOLCHAIN_PATH" || -L "$USER_TOOLCHAIN_PATH" ]]; then
101-
rm -rf "$USER_TOOLCHAIN_PATH"
102-
fi
103-
ln -sf "$BUILT_TOOLCHAIN_PATH" "$USER_TOOLCHAIN_PATH"
82+
# Clean up
83+
rm -f "$TOOL_NAMES_FILE"

0 commit comments

Comments
 (0)