OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 | |
7 from optparse import OptionParser | |
8 import os | |
9 import subprocess | |
10 import sys | |
11 | |
12 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | |
13 import pynacl.platform | |
14 | |
15 NATIVE_CLIENT_DIR = os.path.dirname(os.path.dirname(__file__)) | |
16 TOOLCHAIN_DIR = os.path.join(NATIVE_CLIENT_DIR, 'toolchain') | |
17 | |
18 """Building verification script | |
19 | |
20 This module will take the output directory path, base nexe name and | |
21 architecture and combine them to generate a sel_ldr execution command | |
22 line using irt_core of the appropriate architecture. | |
23 """ | |
24 | |
25 | |
26 def RelativePath(filepath): | |
27 """Return a POSIX style relative path.""" | |
28 cwdpath = os.path.abspath(os.getcwd()) | |
29 cwdparts = cwdpath.split(os.path.sep) | |
30 | |
31 filepath = os.path.abspath(filepath) | |
32 | |
33 # For Windows, convert to POSIX style path | |
34 if sys.platform == 'win32': | |
35 filepath = filepath.replace('\\', '/') | |
36 fileparts = filepath.split('/') | |
37 for index in range(len(fileparts)): | |
38 if index >= len(cwdparts): | |
39 break | |
40 if cwdparts[index] != fileparts[index]: | |
41 break | |
42 | |
43 # Add 'cd ..' the rest of cwd that doesn't match | |
44 out = '../' * (len(cwdparts) - index) | |
45 out += '/'.join(fileparts[index:]) | |
46 return out | |
47 | |
48 | |
49 def UseWin64(): | |
50 """Check if we are on 64 bit windows.""" | |
51 if sys.platform != 'win32': | |
52 return False | |
53 arch32 = os.environ.get('PROCESSOR_ARCHITECTURE', 'unk') | |
54 arch64 = os.environ.get('PROCESSOR_ARCHITEW6432', 'unk') | |
55 | |
56 if arch32 == 'AMD64' or arch64 == 'AMD64': | |
57 return True | |
58 return False | |
59 | |
60 | |
61 def ErrOut(text): | |
62 """ErrOut prints an error message and the command-line that caused it. | |
63 | |
64 Prints to standard err, both the command-line normally, and separated by | |
65 >>...<< to make it easier to copy and paste the command, or to | |
66 find command formating issues. | |
67 """ | |
68 sys.stderr.write('\n\n') | |
69 sys.stderr.write( '>>>' + '>> <<'.join(sys.argv) + '<<\n\n') | |
70 sys.stderr.write(' '.join(sys.argv) + '<<\n\n') | |
71 sys.stderr.write(text + '\n') | |
72 sys.exit(1) | |
73 | |
74 def Run(cmd_line): | |
75 """Run the provided command-line returning the error code. | |
76 | |
77 Uses supbrocess to execute the command line, printing debugging information | |
78 as well as error messages as appropriate. | |
79 """ | |
80 sys.stdout.write('Run: %s\n' % ' '.join(cmd_line)) | |
81 sys.stdout.flush() | |
82 try: | |
83 ecode = subprocess.call(cmd_line) | |
84 except Exception, err: | |
85 ErrOut('\nFAILED: %s\n' % str(err)) | |
86 | |
87 if ecode: | |
88 ErrOut('FAILED with return value: %d\n' % ecode) | |
89 else: | |
90 sys.stdout.write('Success\n') | |
91 sys.stdout.flush() | |
92 return ecode | |
93 | |
94 | |
95 def GetToolchainPath(tool): | |
96 """Given the requested tool, compute the path to that toolchain.""" | |
97 os_name = pynacl.platform.GetOS() | |
98 | |
99 if tool == 'newlib': | |
100 lib_name = 'newlib' | |
101 else: | |
102 lib_name = 'glibc' | |
103 | |
104 return os.path.join( | |
105 TOOLCHAIN_DIR, | |
106 '%s_x86' % (os_name), | |
107 'nacl_x86_%s_raw' % lib_name | |
108 ) | |
109 | |
110 | |
111 def GetLibPath(tool, arch): | |
112 """For a given tool and architecture, determine the library path.""" | |
113 arch_map = { | |
114 'ia32': 'lib32', | |
115 'x64': 'lib', | |
116 } | |
117 lib = arch_map[arch] | |
118 | |
119 # For 64bit win, the arch is incorrectly reported as 32bit, so override it. | |
120 if UseWin64(): | |
121 lib = 'lib' | |
122 return os.path.join(GetToolchainPath(tool), 'x86_64-nacl', lib) | |
123 | |
124 | |
125 def GetNexe(path, name, arch, tool): | |
126 """For a given nexe name, architecture, and tool generate a path to it.""" | |
127 arch_map = { | |
128 'ia32': 'x32', | |
129 'x64': 'x64', | |
130 } | |
131 # For 64bit win, the arch is incorrectly reported as 32bit, so override it. | |
132 if UseWin64(): | |
133 arch = 'x64' | |
134 return os.path.join(path, '%s_%s_%s.nexe' % (name, tool, arch_map[arch])) | |
135 | |
136 | |
137 def GetLdrIrtNexe(path, nexe, arch, tool): | |
138 sel_ldr = os.path.join(path, 'sel_ldr') | |
139 # If incorrectly reported use sel_ldr64 otherwise assume sel_ldr is already | |
140 # the correct architecture. | |
141 if UseWin64() and arch == 'ia32': | |
142 sel_ldr = os.path.join(path, 'sel_ldr64') | |
143 | |
144 # Get IRT_CORE which is built with newlib | |
145 irt_core = GetNexe(path, 'irt_core', arch, 'newlib') | |
146 nexe = GetNexe(path, nexe, arch, tool) | |
147 return (sel_ldr, irt_core, nexe) | |
148 | |
149 | |
150 def BuildCmdLine(path, nexe, arch, tool): | |
151 (sel_ldr, irt_core, nexe) = GetLdrIrtNexe(path, nexe, arch, tool) | |
152 if tool in ('newlib', 'pnacl_newlib'): | |
153 return [sel_ldr, '-B', irt_core, '--', nexe] | |
154 | |
155 # For GLIBC we need to add 'runnable-ld.so' and allow sel_ldr access to | |
156 # the file system. | |
157 libpath = GetLibPath(tool, arch) | |
158 ldso = os.path.join(libpath, 'runnable-ld.so') | |
159 # Force POSIX style paths required by runnable-ld | |
160 nexe = nexe.replace('\\', '/') | |
161 return [sel_ldr, '-a', '-B', irt_core, '--', ldso, '--library-path', | |
162 libpath, nexe] | |
163 | |
164 | |
165 def RunLoader(path, nexe, arch, tools, out): | |
166 fail = 0 | |
167 for tool in tools: | |
168 print '\n\nRunning: %s %s %s' % (nexe, arch, tool) | |
169 cmd_line = BuildCmdLine(path, nexe, arch, tool) | |
170 err = Run(cmd_line) | |
171 if err: | |
172 fail = fail + 1 | |
173 if out: | |
174 out.write('%s on %s built with %s, failed returning %s.\n' % | |
175 (nexe, arch, tool, err)) | |
176 else: | |
177 if out: | |
178 out.write('SUCCESS: %s on %s built with %s.\n' % (nexe, arch, tool)) | |
179 return fail | |
180 | |
181 | |
182 def LogLoader(path, nexe, arch, tools): | |
183 for tool in tools: | |
184 cmd_line = BuildCmdLine(path, nexe, arch, tool) | |
185 print ' '.join(cmd_line) | |
186 return 0 | |
187 | |
188 | |
189 def LogDeps(path, nexe, arch, tools): | |
190 out_set = set() | |
191 out_set.add(RelativePath(__file__)) | |
192 | |
193 for tool in tools: | |
194 out_set |= set(GetLdrIrtNexe(path, nexe, arch, tool)) | |
195 for out in out_set: | |
196 # Emit forward slashes here as that's what gyp expects. | |
197 print out.replace('\\', '/') | |
198 | |
199 | |
200 def Main(argv): | |
201 parser = OptionParser() | |
202 | |
203 # Test build modes | |
204 parser.add_option('-i', '--input', help='Generate input list.', | |
205 action='store_const', const='i', dest='mode', default='') | |
206 parser.add_option('-r', '--run', help='Run the nexe.', | |
207 action='store_const', const='r', dest='mode', default='') | |
208 parser.add_option('-l', '--log', help='Log the command-lines to run.', | |
209 action='store_const', const='l', dest='mode', default='') | |
210 | |
211 # Options | |
212 parser.add_option('-v', '--verbose', dest='verbose', default=False, | |
213 help='Enable verbosity', action='store_true') | |
214 parser.add_option('-o', '--output', dest='output', default='', | |
215 help='Set output file name.', action='store') | |
216 parser.add_option('-p', '--path', dest='path', default='.', | |
217 help='Set path to sel_ldr and NEXEs.', action='store') | |
218 parser.add_option('-n', '--name', dest='name', action='store', default='', | |
219 help='Base name of the NEXE.') | |
220 parser.add_option('-x', '--arch', dest='arch', action='store', default='', | |
221 help='Run architecture.') | |
222 parser.add_option('-t', '--tools', dest='tools', action='append', default=[], | |
223 help='Select which toolchains versions to run.') | |
224 parser.add_option('-s', '--stamp', dest='stamp', | |
225 help='Select path to write a stamp file to on success.') | |
226 | |
227 options, nexe_args = parser.parse_args(argv[1:]) | |
228 if not options.name: | |
229 parser.error('Missing NEXE name.') | |
230 if not options.mode or options.mode not in ['i', 'r', 'l']: | |
231 parser.error('Missing run mode.') | |
232 if not options.arch: | |
233 parser.error('Missing run architecture.') | |
234 if not options.tools: | |
235 parser.error('Missing toolchains.') | |
236 | |
237 # TODO(bradnelson): Hack to prevent gyp generated path ending with \" from | |
238 # being interpreted as escaped quote. Here we strip the extra | |
239 # '/hack' we added in the gyp file. | |
240 # http://code.google.com/p/chromium/issues/detail?id=141463 | |
241 if options.mode is 'r': | |
242 options.path = os.path.dirname(options.path) | |
243 | |
244 if options.output: | |
245 try: | |
246 out = open(options.output, 'w') | |
247 except EnvironmentError: | |
248 print 'Failed to open %s.\n' % options.output | |
249 return 1 | |
250 else: | |
251 out = None | |
252 | |
253 tools = ','.join(options.tools) | |
254 tools = tools.split(',') | |
255 fail = 0 | |
256 for tool in tools: | |
257 if tool not in ['newlib', 'glibc', 'pnacl_newlib']: | |
258 parser.error('Unknown tool: %s\n' % tool) | |
259 | |
260 path = options.path | |
261 nexe = options.name | |
262 arch = options.arch | |
263 | |
264 if options.mode == 'r': | |
265 fail = RunLoader(path, nexe, arch, tools, out) | |
266 if options.mode == 'l': | |
267 fail = LogLoader(path, nexe, arch, tools) | |
268 if options.mode == 'i': | |
269 fail = LogDeps('<(PRODUCT_DIR)', nexe, arch, tools) | |
270 | |
271 if out: | |
272 out.close() | |
273 | |
274 if fail == 0 and options.stamp is not None: | |
275 with open(options.stamp, 'w') as fh: | |
276 fh.write('ok') | |
277 | |
278 return fail | |
279 | |
280 | |
281 if __name__ == '__main__': | |
282 sys.exit(Main(sys.argv)) | |
OLD | NEW |