| 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 |