OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 | |
3 # Copyright 2016 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 import os | |
8 import os.path | |
9 import shutil | |
10 import subprocess | |
11 import sys | |
12 | |
13 # The linker_driver.py is responsible for forwarding a linker invocation to | |
14 # the compiler driver, while processing special arguments itself. | |
15 # | |
16 # On Mac, the logical step of linking is handled by three discrete tools to | |
17 # perform the image link, debug info link, and strip. The linker_driver.py | |
18 # combines these three steps into a single tool. | |
19 # | |
20 # The command passed to the linker_driver.py is first invoked unaltered (except | |
21 # for the removal of the special driver arguments, described below). Then the | |
22 # driver performs additional actions, based on these arguments: | |
Mark Mentovai
2016/05/20 19:41:11
Specify (perhaps by implication if you provide a s
Robert Sesek
2016/05/24 15:05:33
Done.
| |
23 # | |
24 # -Wcrl,dsym,<dsym_path_prefix> | |
25 # After invoking the linker, this will run `dsymutil` on the linker's | |
26 # output, producing a dSYM bundle, stored at dsym_path_prefix. As an | |
27 # example, if the linker driver were invoked with: | |
28 # "... -o out/gn/obj/foo/libbar.dylib ... -Wcrl,dsym,out/gn/ ..." | |
Mark Mentovai
2016/05/20 19:41:11
If the trailing / isn’t necessary, don’t include i
Robert Sesek
2016/05/24 15:05:33
Done.
| |
29 # The resulting dSYM would be out/gn/libbar.dylib.dSYM/. | |
30 # | |
31 # -Wcrl,strip,<strip_arguments> | |
32 # After invoking the linker, and optionally dsymutil, this will run | |
33 # the strip command on the linker's output. strip_arguments are | |
34 # comma-separated arguments to be passed to the strip command. | |
35 | |
36 def Main(args): | |
37 """Main function for the linker driver. Separates out the arguments for | |
38 the main compiler driver and the linker driver, then invokes all the | |
39 required tools. | |
40 | |
41 Args: | |
42 args: list of string, Arguments to the script. | |
43 """ | |
44 | |
45 if len(args) < 2: | |
46 raise RuntimeError("Usage: linker_driver.py [linker-invocation]") | |
47 | |
48 # Collect arguments to the linker driver (this script) and remove them from | |
49 # the arguments being passed to the compiler driver. | |
50 linker_driver_actions = {} | |
51 compiler_driver_args = [] | |
52 for arg in args[1:]: | |
53 if arg.startswith(_LINKER_DRIVER_ARG_PREFIX): | |
54 # Convert driver actions into a map of name => lambda to invoke. | |
55 driver_action = ProcessLinkerDriverArg(arg) | |
56 linker_driver_actions[driver_action[0]] = driver_action[1] | |
Mark Mentovai
2016/05/20 19:41:11
assert (or raise if not) driver_action[0] in linke
Robert Sesek
2016/05/24 15:05:33
Done.
| |
57 else: | |
58 compiler_driver_args.append(arg) | |
59 | |
60 linker_driver_outputs = [_FindLinkerOutput(compiler_driver_args)] | |
61 | |
62 try: | |
63 # Run the linker by invoking the compiler driver. | |
64 subprocess.check_call(compiler_driver_args) | |
65 | |
66 # Run the linker driver actions, in the order specified by the actions list. | |
67 for action in _LINKER_DRIVER_ACTIONS: | |
68 name = action[0] | |
69 if name in linker_driver_actions: | |
70 linker_driver_outputs += linker_driver_actions[name](args) | |
Mark Mentovai
2016/05/20 19:41:11
I’m not going to force you to fix this because it’
Robert Sesek
2016/05/24 15:05:33
Acknowledged.
| |
71 except: | |
72 # If a linker driver action failed, remove all the outputs to make the | |
73 # build step atomic. | |
74 map(_RemovePath, linker_driver_outputs) | |
75 | |
76 # Re-report the original failure. | |
77 raise | |
78 | |
79 | |
80 def ProcessLinkerDriverArg(arg): | |
81 """Processes a linker driver argument and returns a tuple containing the | |
82 name and unary lambda to invoke for that linker driver action. | |
83 | |
84 Args: | |
85 arg: string, The linker driver argument. | |
86 | |
87 Returns: | |
88 A 2-tuple: | |
89 0: The driver action name, as in _LINKER_DRIVER_ACTIONS. | |
90 1: An 1-ary lambda that takes the full list of arguments passed to | |
91 Main(). The lambda should call the linker driver action that | |
92 corresponds to the argument. | |
Mark Mentovai
2016/05/20 19:41:11
and return blah blah blah
Robert Sesek
2016/05/24 15:05:33
Done.
| |
93 """ | |
94 if not arg.startswith(_LINKER_DRIVER_ARG_PREFIX): | |
95 raise ValueError('%s is not a linker driver argument' % (arg,)) | |
96 | |
97 sub_arg = arg[len(_LINKER_DRIVER_ARG_PREFIX):] | |
98 | |
99 for driver_action in _LINKER_DRIVER_ACTIONS: | |
100 (name, action) = driver_action | |
101 if sub_arg.startswith(name): | |
102 return (name, | |
103 lambda full_args: action(sub_arg[len(name):], full_args)) | |
104 | |
105 raise ValueError('Unknown linker driver argument: %s' % (arg,)) | |
106 | |
107 | |
108 def RunDsymUtil(dsym_path_prefix, full_args): | |
109 """Linker driver action for -Wcrl,dsym,<dsym-path-prefix>. Invokes dsymutil | |
110 on the linker's output and produces a dsym file at |dsym_file| path. | |
111 | |
112 Args: | |
113 dsym_path_prefix: string, The path at which the dsymutil output should be | |
114 located. | |
115 full_args: list of string, Full argument list for the linker driver. | |
116 | |
117 Returns: | |
118 list of string, Build step outputs. | |
119 """ | |
120 if not len(dsym_path_prefix): | |
121 raise ValueError('Unspecified dSYM output file') | |
122 | |
123 linker_out = _FindLinkerOutput(full_args) | |
124 (head, tail) = os.path.split(linker_out) | |
125 dsym_out = os.path.join(dsym_path_prefix, tail + '.dSYM') | |
126 | |
127 # Remove old dSYMs before invoking dsymutil. | |
128 _RemovePath(dsym_out) | |
129 subprocess.check_call(['xcrun', 'dsymutil', '-o', dsym_out, linker_out]) | |
130 return [dsym_out] | |
Mark Mentovai
2016/05/20 19:41:11
This and RunStrip can return a (tuple) instead of
Robert Sesek
2016/05/24 15:05:33
Returning tuple means having a (turd,). These seem
| |
131 | |
132 | |
133 def RunStrip(strip_args_string, full_args): | |
134 """Linker driver action for -Wcrl,strip,<strip_arguments>. | |
135 | |
136 Args: | |
137 strip_args_string: string, Comma-separated arguments for `strip`. | |
138 full_args: list of string, Full arguments for the linker driver. | |
139 | |
140 Returns: | |
141 list of string, Build step outputs. | |
142 """ | |
143 strip_args_list = strip_args_string.split(',') | |
Mark Mentovai
2016/05/20 19:41:11
If you get “-Wcrl,strip,”, then you want to run “s
Robert Sesek
2016/05/24 15:05:33
Done.
| |
144 strip_command = ['xcrun', 'strip'] + strip_args_list | |
145 strip_command.append(_FindLinkerOutput(full_args)) | |
146 subprocess.check_call(strip_command) | |
Mark Mentovai
2016/05/20 19:41:11
TODO(maybe?): swallow that bogus strip deprecation
Robert Sesek
2016/05/24 15:05:33
Gonna skip this for now.
| |
147 return [] | |
148 | |
149 | |
150 def _FindLinkerOutput(full_args): | |
151 """Finds the output of the linker by looking for the output flag in its | |
152 argument list. As this is a required linker argument, raises an error if it | |
153 cannot be found. | |
154 """ | |
155 for (i, arg) in enumerate(full_args): | |
Mark Mentovai
2016/05/20 19:41:11
Why not just this:
return full_args[full_args.ind
Robert Sesek
2016/05/24 15:05:33
Done.
| |
156 if arg == '-o': | |
157 return full_args[i+1] | |
158 raise RuntimeError('Unable to locate linker output file (no -o flag)') | |
159 | |
160 | |
161 def _RemovePath(path): | |
162 """Removes the file or directory at |path| if it exists.""" | |
163 if os.path.exists(path): | |
164 if os.path.isdir(path): | |
165 shutil.rmtree(path) | |
166 else: | |
167 os.unlink(path) | |
168 | |
169 | |
170 _LINKER_DRIVER_ARG_PREFIX = '-Wcrl,' | |
171 | |
172 """List of linker driver actions. The sort order of this list affects the | |
173 order in which the actions are invoked. The first item in the tuple is the | |
174 argument's -Wcrl,<sub_argument> and the second is the function to invoke. | |
175 """ | |
176 _LINKER_DRIVER_ACTIONS = [ | |
177 ('dsym,', RunDsymUtil), | |
178 ('strip,', RunStrip), | |
179 ] | |
180 | |
181 | |
182 if __name__ == '__main__': | |
183 Main(sys.argv) | |
184 sys.exit(0) | |
OLD | NEW |