1
1
def _custom_toolchain_impl (ctx ):
2
- toolchain_dir = ctx .actions .declare_directory (ctx .attr .toolchain_name + ".xctoolchain" )
3
- toolchain_plist_file = ctx .actions .declare_file (ctx .attr .toolchain_name + "_ToolchainInfo.plist" )
2
+ toolchain_name_base = ctx .attr .toolchain_name
4
3
5
- default_toolchain_path = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain"
6
- user_toolchain_path = "$(eval echo ~)/Library/Developer/Toolchains/{}.xctoolchain" .format (ctx .attr .toolchain_name )
7
- built_toolchain_path = "$(eval pwd)/" + toolchain_dir .path
4
+ # Create a file to store Xcode version
5
+ xcode_version_file = ctx .actions .declare_file (toolchain_name_base + "_xcode_version.txt" )
8
6
9
- resolved_overrides = {}
7
+ # Run xcodebuild to get the Xcode version
8
+ ctx .actions .run_shell (
9
+ outputs = [xcode_version_file ],
10
+ command = """
11
+ # Get Xcode version and clean it for use in filenames
12
+ xcodebuild -version | head -n 1 | sed 's/Xcode //' | tr -d '.' | tr ' ' '_' > {outfile}
13
+ """ .format (outfile = xcode_version_file .path ),
14
+ mnemonic = "GetXcodeVersion" ,
15
+ execution_requirements = {"no-sandbox" : "1" },
16
+ )
17
+
18
+ # Create a file to store the default toolchain path
19
+ default_toolchain_path_file = ctx .actions .declare_file (toolchain_name_base + "_default_toolchain_path.txt" )
10
20
11
- for tool_name , label_target in ctx .attr .overrides .items ():
12
- print ("DEBUG: Processing override '{}' -> '{}'" .format (tool_name , label_target ))
21
+ # Run xcrun to get the default toolchain path
22
+ ctx .actions .run_shell (
23
+ outputs = [default_toolchain_path_file ],
24
+ command = "xcrun --find clang | sed 's|/usr/bin/clang$||' > {outfile}" .format (
25
+ outfile = default_toolchain_path_file .path
26
+ ),
27
+ mnemonic = "GetDefaultToolchainPath" ,
28
+ execution_requirements = {"no-sandbox" : "1" }, # Allow xcrun to access system paths
29
+ )
13
30
14
- # Check if the target produces valid files
15
- if hasattr (label_target , "files" ):
16
- files = label_target .files .to_list ()
17
- else :
18
- files = []
31
+ # Declare the output directory for the toolchain
32
+ toolchain_dir = ctx .actions .declare_directory (toolchain_name_base + ".xctoolchain" )
19
33
34
+ resolved_overrides = {}
35
+ override_files = [] # Collect all override files to include in inputs
36
+
37
+ for tool_target , tool_name in ctx .attr .overrides .items ():
38
+ # The key is the target (label), the value is the tool name (string)
39
+ files = tool_target .files .to_list ()
20
40
if not files :
21
- fail ("ERROR: Override '{}' does not produce any files! Ensure it is wrapped in a `filegroup`. " .format (tool_name ))
41
+ fail ("ERROR: Override for '{}' does not produce any files!" .format (tool_name ))
22
42
23
- # Extract the first file from the filegroup
24
- resolved_path = files [ 0 ]. path
25
- resolved_overrides [ tool_name ] = resolved_path
43
+ if len ( files ) > 1 :
44
+ fail ( "ERROR: Override for '{}' produces multiple files ({}). Each override must have exactly one file." . format (
45
+ tool_name , len ( files )))
26
46
27
- # Debugging: Print resolved paths
28
- print ("Resolved overrides:" , resolved_overrides )
47
+ override_file = files [0 ]
48
+ override_files .append (override_file ) # Add to list of input files
49
+ resolved_overrides [tool_name ] = override_file .path
29
50
30
51
# Generate symlink creation commands dynamically, excluding plist files
31
52
overrides_list = " " .join (["{}={}" .format (k , v ) for k , v in resolved_overrides .items ()])
32
53
33
- symlink_script = """#!/bin/bash
34
- set -e
35
-
36
- mkdir -p "{toolchain_dir}"
37
-
38
- # Process overrides manually (avoiding associative arrays)
39
- while IFS='=' read -r key value; do
40
- if [[ -n "$key" && -n "$value" ]]; then
41
- overrides="$overrides $key=$value"
42
- fi
43
- done <<< "{overrides_list}"
44
-
45
- find "{default_toolchain}" -type f -o -type l | while read file; do
46
- base_name="$(basename "$file")"
47
- rel_path="${{file#"{default_toolchain}/"}}"
48
-
49
- # Check if an override exists
50
- override_path=""
51
- for entry in $overrides; do
52
- o_key="${{entry%%=*}}"
53
- o_value="${{entry#*=}}"
54
- echo "Looking for Override: $o_key -> $o_value"
55
- if [[ "$o_key" == "$base_name" ]]; then
56
- echo "Found Override: $entry -> $o_value"
57
- override_path="$o_value"
58
- break
59
- fi
60
- done
61
-
62
- if [[ -n "$override_path" ]]; then
63
- mkdir -p "{toolchain_dir}/$(dirname "$rel_path")"
64
- cp "$override_path" "{toolchain_dir}/$rel_path"
65
- continue
66
- fi
67
-
68
- # Symlink everything else
69
- if [[ "$rel_path" != "ToolchainInfo.plist" ]]; then
70
- mkdir -p "{toolchain_dir}/$(dirname "$rel_path")"
71
- ln -s "$file" "{toolchain_dir}/$rel_path"
72
- fi
73
- done
74
-
75
- mv "{toolchain_plist}" "{toolchain_dir}/ToolchainInfo.plist"
76
-
77
- # Remove existing symlink if present and create a new one in the user directory
78
- if [ -e "{user_toolchain_path}" ]; then
79
- rm -f "{user_toolchain_path}"
80
- fi
81
- ln -s "{built_toolchain_path}" "{user_toolchain_path}"
82
- """ .format (
83
- toolchain_dir = toolchain_dir .path ,
84
- default_toolchain = default_toolchain_path ,
85
- overrides_list = overrides_list ,
86
- toolchain_plist = toolchain_plist_file .path ,
87
- user_toolchain_path = user_toolchain_path ,
88
- built_toolchain_path = built_toolchain_path
89
- )
90
-
91
- script_file = ctx .actions .declare_file (ctx .attr .toolchain_name + "_setup.sh" )
92
- ctx .actions .write (output = script_file , content = symlink_script , is_executable = True )
93
-
94
- # Generate ToolchainInfo.plist
95
- toolchain_plist_content = """<?xml version="1.0" encoding="UTF-8"?>
96
- <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
97
- <plist version="1.0">
98
- <dict>
99
- <key>Aliases</key>
100
- <array>
101
- <string>{name}</string>
102
- </array>
103
- <key>CFBundleIdentifier</key>
104
- <string>com.example.{name}</string>
105
- <key>CompatibilityVersion</key>
106
- <integer>2</integer>
107
- <key>CompatibilityVersionDisplayString</key>
108
- <string>Xcode 13.0</string>
109
- <key>DisplayName</key>
110
- <string>{name}</string>
111
- <key>ReportProblemURL</key>
112
- <string>https://github.com/MobileNativeFoundation/rules_xcodeproj</string>
113
- <key>ShortDisplayName</key>
114
- <string>{name}</string>
115
- <key>Version</key>
116
- <string>0.0.1</string>
117
- </dict>
118
- </plist>
119
- """ .format (name = ctx .attr .toolchain_name )
120
-
121
- ctx .actions .write (output = toolchain_plist_file , content = toolchain_plist_content )
54
+ script_file = ctx .actions .declare_file (toolchain_name_base + "_setup.sh" )
55
+
56
+ # Use expand_template with simplified substitutions
57
+ ctx .actions .expand_template (
58
+ template = ctx .file ._symlink_template ,
59
+ output = script_file ,
60
+ is_executable = True ,
61
+ substitutions = {
62
+ "%toolchain_name_base%" : toolchain_name_base ,
63
+ "%toolchain_dir%" : toolchain_dir .path ,
64
+ "%overrides_list%" : overrides_list ,
65
+ "%default_toolchain_path_file%" : default_toolchain_path_file .path ,
66
+ "%xcode_version_file%" : xcode_version_file .path ,
67
+ },
68
+ )
122
69
123
70
# Run the generated shell script
124
71
ctx .actions .run_shell (
125
72
outputs = [toolchain_dir ],
126
- inputs = [toolchain_plist_file ] ,
73
+ inputs = [default_toolchain_path_file , xcode_version_file ] + override_files ,
127
74
tools = [script_file ],
128
- command = script_file .path
75
+ mnemonic = "SymlinkDefaultXcodeToolchain" ,
76
+ command = script_file .path ,
77
+ execution_requirements = {"no-sandbox" : "1" },
129
78
)
130
79
131
- return [DefaultInfo (files = depset ([toolchain_dir ]))]
80
+ # Create runfiles with the override files and script file
81
+ runfiles = ctx .runfiles (files = override_files + [script_file , default_toolchain_path_file , xcode_version_file ])
82
+
83
+ return [DefaultInfo (
84
+ files = depset ([toolchain_dir ]),
85
+ runfiles = runfiles ,
86
+ )]
132
87
133
88
custom_toolchain = rule (
134
89
implementation = _custom_toolchain_impl ,
@@ -137,5 +92,10 @@ custom_toolchain = rule(
137
92
"overrides" : attr .label_keyed_string_dict (
138
93
allow_files = True , mandatory = False , default = {}
139
94
),
95
+ "_symlink_template" : attr .label (
96
+ allow_single_file = True ,
97
+ default = Label ("//xcodeproj/internal/templates:custom_toolchain_symlink.sh" ),
98
+ ),
140
99
},
141
100
)
101
+
0 commit comments