Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / tools / create_nmf.py
bloba97fd92f992f4e45383dabcea2ab9521898d5d3f
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium 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.
6 """Tool for automatically creating .nmf files from .nexe/.pexe/.bc executables.
8 As well as creating the nmf file this tool can also find and stage
9 any shared libraries dependencies that the executables might have.
10 """
12 import argparse
13 import errno
14 import json
15 import os
16 import posixpath
17 import shutil
18 import sys
20 import getos
22 if sys.version_info < (2, 7, 0):
23 sys.stderr.write("python 2.7 or later is required run this script\n")
24 sys.exit(1)
26 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
27 LIB_DIR = os.path.join(SCRIPT_DIR, 'lib')
29 sys.path.append(LIB_DIR)
31 import elf
32 import get_shared_deps
33 import quote
36 ARCH_LOCATION = {
37 'x86-32': 'lib32',
38 'x86-64': 'lib64',
39 'arm': 'lib',
43 # These constants are used within nmf files.
44 RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
45 MAIN_NEXE = 'main.nexe' # Name of entry point for execution
46 PROGRAM_KEY = 'program' # Key of the program section in an nmf file
47 URL_KEY = 'url' # Key of the url field for a particular file in an nmf file
48 FILES_KEY = 'files' # Key of the files section in an nmf file
49 PNACL_OPTLEVEL_KEY = 'optlevel' # key for PNaCl optimization level
50 PORTABLE_KEY = 'portable' # key for portable section of manifest
51 TRANSLATE_KEY = 'pnacl-translate' # key for translatable objects
52 TRANSLATE_DEBUG_KEY = 'pnacl-debug' # key for translatable debug objects
55 def DebugPrint(message):
56 if DebugPrint.debug_mode:
57 sys.stderr.write('%s\n' % message)
60 DebugPrint.debug_mode = False # Set to True to enable extra debug prints
63 def SplitPath(path):
64 """Returns all components of a path as a list.
66 e.g.
67 'foo/bar/baz.blah' => ['foo', 'bar', 'baz.blah']
68 """
69 result = []
70 while path:
71 path, part = os.path.split(path)
72 result.append(part)
73 return result[::-1] # Reverse.
76 def MakePosixPath(path):
77 """Converts from the native format to posixpath format.
79 e.g. on Windows, "foo\\bar\\baz.blah" => "foo/bar/baz.blah"
80 on Mac/Linux this is a no-op.
81 """
82 if os.path == posixpath:
83 return path
84 return posixpath.join(*SplitPath(path))
87 def PosixRelPath(path, start):
88 """Takes two paths in native format, and produces a relative path in posix
89 format.
91 e.g.
92 For Windows: "foo\\bar\\baz.blah", "foo" => "bar/baz.blah"
93 For Mac/Linux: "foo/bar/baz.blah", "foo" => "bar/baz.blah"
95 NOTE: This function uses os.path.realpath to create a canonical path for
96 |path| and |start|.
97 """
98 real_path = os.path.realpath(path)
99 real_start = os.path.realpath(start)
100 return MakePosixPath(os.path.relpath(real_path, real_start))
103 def DirectoryTreeContainsFile(dirname, filename):
104 """Returns True if a file is in a directory, or any of that directory's
105 subdirectories recursively.
107 e.g.
108 DirectoryTreeContainsFile("foo", "foo/quux.txt") => True
109 DirectoryTreeContainsFile("foo", "foo/bar/baz/blah.txt") => True
110 DirectoryTreeContainsFile("foo", "bar/blah.txt") => False
112 real_dirname = os.path.realpath(dirname)
113 real_filename = os.path.realpath(filename)
114 return real_filename.startswith(real_dirname)
117 def MakeDir(dirname):
118 """Just like os.makedirs but doesn't generate errors when dirname
119 already exists.
121 if os.path.isdir(dirname):
122 return
124 Trace("mkdir: %s" % dirname)
125 try:
126 os.makedirs(dirname)
127 except OSError as exception_info:
128 if exception_info.errno != errno.EEXIST:
129 raise
132 def ParseElfHeader(path):
133 """Wrap elf.ParseElfHeader to return raise this module's Error on failure."""
134 try:
135 return elf.ParseElfHeader(path)
136 except elf.Error, e:
137 raise Error(str(e))
140 class Error(Exception):
141 """Local Error class for this file."""
142 pass
145 class ArchFile(object):
146 """Simple structure containing information about an architecture-specific
147 file.
149 Attributes:
150 name: Name of this file
151 path: Full path to this file on the build system
152 arch: Architecture of this file (e.g., x86-32)
153 url: Relative path to file in the staged web directory.
154 Used for specifying the "url" attribute in the nmf file."""
156 def __init__(self, name, path, url=None, arch=None):
157 self.name = name
158 self.path = path
159 self.url = url
160 self.arch = arch
161 if arch is None:
162 self.arch = ParseElfHeader(path)[0]
164 def __repr__(self):
165 return '<ArchFile %s>' % self.path
167 def __str__(self):
168 """Return the file path when invoked with the str() function"""
169 return self.path
172 class NmfUtils(object):
173 """Helper class for creating and managing nmf files"""
175 def __init__(self, main_files=None, objdump=None,
176 lib_path=None, extra_files=None, lib_prefix=None,
177 nexe_prefix=None, no_arch_prefix=None, remap=None,
178 pnacl_optlevel=None, pnacl_debug_optlevel=None,
179 nmf_root=None):
180 """Constructor
182 Args:
183 main_files: List of main entry program files. These will be named
184 files->main.nexe for dynamic nexes, and program for static nexes
185 objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
186 lib_path: List of paths to library directories
187 extra_files: List of extra files to include in the nmf
188 lib_prefix: A path prefix to prepend to the library paths, both for
189 staging the libraries and for inclusion into the nmf file.
190 Example: '../lib_dir'
191 nexe_prefix: Like lib_prefix, but is prepended to the nexes instead.
192 no_arch_prefix: Don't prefix shared libraries by lib32/lib64.
193 remap: Remaps the library name in the manifest.
194 pnacl_optlevel: Optimization level for PNaCl translation.
195 pnacl_debug_optlevel: Optimization level for debug PNaCl translation.
196 nmf_root: Directory of the NMF. All urls are relative to this directory.
198 assert len(main_files) > 0
199 self.objdump = objdump
200 self.main_files = main_files
201 self.extra_files = extra_files or []
202 self.lib_path = lib_path or []
203 self.manifest = None
204 self.needed = None
205 self.lib_prefix = lib_prefix or ''
206 self.nexe_prefix = nexe_prefix or ''
207 self.no_arch_prefix = no_arch_prefix
208 self.remap = remap or {}
209 self.pnacl = main_files[0].endswith(('.pexe', '.bc'))
210 self.pnacl_optlevel = pnacl_optlevel
211 self.pnacl_debug_optlevel = pnacl_debug_optlevel
212 if nmf_root is not None:
213 self.nmf_root = nmf_root
214 else:
215 # To match old behavior, if there is no nmf_root, use the directory of
216 # the first nexe found in main_files.
217 self.nmf_root = os.path.dirname(main_files[0])
219 for filename in self.main_files:
220 if not os.path.exists(filename):
221 raise Error('Input file not found: %s' % filename)
222 if not os.path.isfile(filename):
223 raise Error('Input is not a file: %s' % filename)
225 def GetNeeded(self):
226 """Collect the list of dependencies for the main_files
228 Returns:
229 A dict with key=filename and value=ArchFile of input files.
230 Includes the input files as well, with arch filled in if absent.
231 Example: { '/path/to/my.nexe': ArchFile(my.nexe),
232 '/path/to/libfoo.so': ArchFile(libfoo.so) }"""
234 if self.needed:
235 return self.needed
237 DebugPrint('GetNeeded(%s)' % self.main_files)
239 if not self.objdump:
240 self.objdump = FindObjdumpExecutable()
242 try:
243 all_files = get_shared_deps.GetNeeded(self.main_files, self.objdump,
244 self.lib_path)
245 except get_shared_deps.NoObjdumpError:
246 raise Error('No objdump executable found (see --help for more info)')
247 except get_shared_deps.Error, e:
248 raise Error(str(e))
250 self.needed = {}
252 # all_files is a dictionary mapping filename to architecture. self.needed
253 # should be a dictionary of filename to ArchFile.
254 for filename, arch in all_files.iteritems():
255 name = os.path.basename(filename)
256 self.needed[filename] = ArchFile(name=name, path=filename, arch=arch)
258 self._SetArchFileUrls()
260 return self.needed
262 def _SetArchFileUrls(self):
263 """Fill in the url member of all ArchFiles in self.needed.
265 All urls are relative to the nmf_root. In addition, architecture-specific
266 files are relative to the .nexe with the matching architecture. This is
267 useful when making a multi-platform packaged app, so each architecture's
268 files are in a different directory.
270 # self.GetNeeded() should have already been called.
271 assert self.needed is not None
273 main_nexes = [f for f in self.main_files if f.endswith('.nexe')]
275 # map from each arch to its corresponding main nexe.
276 arch_to_main_dir = {}
277 for main_file in main_nexes:
278 arch, _ = ParseElfHeader(main_file)
279 main_dir = os.path.dirname(main_file)
280 main_dir = PosixRelPath(main_dir, self.nmf_root)
281 if main_dir == '.':
282 main_dir = ''
283 arch_to_main_dir[arch] = main_dir
285 for arch_file in self.needed.itervalues():
286 prefix = ''
287 if DirectoryTreeContainsFile(self.nmf_root, arch_file.path):
288 # This file is already in the nmf_root tree, so it does not need to be
289 # staged. Just make the URL relative to the .nmf.
290 url = PosixRelPath(arch_file.path, self.nmf_root)
291 else:
292 # This file is outside of the nmf_root subtree, so it needs to be
293 # staged. Its path should be relative to the main .nexe with the same
294 # architecture.
295 prefix = arch_to_main_dir[arch_file.arch]
296 url = os.path.basename(arch_file.path)
298 if arch_file.name.endswith('.nexe'):
299 prefix = posixpath.join(prefix, self.nexe_prefix)
300 elif self.no_arch_prefix:
301 prefix = posixpath.join(prefix, self.lib_prefix)
302 else:
303 prefix = posixpath.join(
304 prefix, self.lib_prefix, ARCH_LOCATION[arch_file.arch])
305 arch_file.url = posixpath.join(prefix, url)
307 def StageDependencies(self, destination_dir):
308 """Copies over the dependencies into a given destination directory
310 Each library will be put into a subdirectory that corresponds to the arch.
312 Args:
313 destination_dir: The destination directory for staging the dependencies
315 assert self.needed is not None
316 for arch_file in self.needed.itervalues():
317 source = arch_file.path
318 destination = os.path.join(destination_dir, arch_file.url)
320 if (os.path.normcase(os.path.realpath(source)) ==
321 os.path.normcase(os.path.realpath(destination))):
322 continue
324 # make sure target dir exists
325 MakeDir(os.path.dirname(destination))
327 Trace('copy: %s -> %s' % (source, destination))
328 shutil.copy2(source, destination)
330 def _GeneratePNaClManifest(self):
331 manifest = {}
332 manifest[PROGRAM_KEY] = {}
333 manifest[PROGRAM_KEY][PORTABLE_KEY] = {}
334 portable = manifest[PROGRAM_KEY][PORTABLE_KEY]
335 for filename in self.main_files:
336 translate_dict = {
337 'url': os.path.basename(filename),
339 if filename.endswith('.pexe'):
340 if self.pnacl_optlevel is not None:
341 translate_dict[PNACL_OPTLEVEL_KEY] = self.pnacl_optlevel
342 if TRANSLATE_KEY in portable:
343 raise Error('Multiple .pexe files')
344 portable[TRANSLATE_KEY] = translate_dict
345 elif filename.endswith('.bc'):
346 if self.pnacl_debug_optlevel is not None:
347 translate_dict[PNACL_OPTLEVEL_KEY] = self.pnacl_debug_optlevel
348 if TRANSLATE_DEBUG_KEY in portable:
349 raise Error('Multiple .bc files')
350 portable[TRANSLATE_DEBUG_KEY] = translate_dict
351 else:
352 raise Error('Unexpected executable type: %s' % filename)
353 self.manifest = manifest
355 def _GenerateManifest(self):
356 """Create a JSON formatted dict containing the files
358 NaCl will map url requests based on architecture. The startup NEXE
359 can always be found under the top key PROGRAM. Additional files are under
360 the FILES key further mapped by file name. In the case of 'runnable' the
361 PROGRAM key is populated with urls pointing the runnable-ld.so which acts
362 as the startup nexe. The application itself is then placed under the
363 FILES key mapped as 'main.exe' instead of the original name so that the
364 loader can find it.
366 manifest = { FILES_KEY: {}, PROGRAM_KEY: {} }
368 needed = self.GetNeeded()
370 runnable = any(n.endswith(RUNNABLE_LD) for n in needed)
372 extra_files_kv = [(key, ArchFile(name=key,
373 arch=arch,
374 path=url,
375 url=url))
376 for key, arch, url in self.extra_files]
378 for need, archinfo in needed.items() + extra_files_kv:
379 urlinfo = { URL_KEY: archinfo.url }
380 name = archinfo.name
382 # If starting with runnable-ld.so, make that the main executable.
383 if runnable:
384 if need.endswith(RUNNABLE_LD):
385 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
386 continue
388 if need in self.main_files:
389 if need.endswith(".nexe"):
390 # Place it under program if we aren't using the runnable-ld.so.
391 if not runnable:
392 manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
393 continue
394 # Otherwise, treat it like another another file named main.nexe.
395 name = MAIN_NEXE
397 name = self.remap.get(name, name)
398 fileinfo = manifest[FILES_KEY].get(name, {})
399 fileinfo[archinfo.arch] = urlinfo
400 manifest[FILES_KEY][name] = fileinfo
401 self.manifest = manifest
403 def GetManifest(self):
404 """Returns a JSON-formatted dict containing the NaCl dependencies"""
405 if self.manifest is None:
406 if self.pnacl:
407 self._GeneratePNaClManifest()
408 else:
409 self._GenerateManifest()
410 return self.manifest
412 def GetJson(self):
413 """Returns the Manifest as a JSON-formatted string"""
414 pretty_string = json.dumps(self.GetManifest(), indent=2)
415 # json.dumps sometimes returns trailing whitespace and does not put
416 # a newline at the end. This code fixes these problems.
417 pretty_lines = pretty_string.split('\n')
418 return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
421 def Trace(msg):
422 if Trace.verbose:
423 sys.stderr.write(str(msg) + '\n')
425 Trace.verbose = False
428 def ParseExtraFiles(encoded_list, err):
429 """Parse the extra-files list and return a canonicalized list of
430 [key, arch, url] triples. The |encoded_list| should be a list of
431 strings of the form 'key:url' or 'key:arch:url', where an omitted
432 'arch' is taken to mean 'portable'.
434 All entries in |encoded_list| are checked for syntax errors before
435 returning. Error messages are written to |err| (typically
436 sys.stderr) so that the user has actionable feedback for fixing all
437 errors, rather than one at a time. If there are any errors, None is
438 returned instead of a list, since an empty list is a valid return
439 value.
441 seen_error = False
442 canonicalized = []
443 for ix in range(len(encoded_list)):
444 kv = encoded_list[ix]
445 unquoted = quote.unquote(kv, ':')
446 if len(unquoted) == 3:
447 if unquoted[1] != ':':
448 err.write('Syntax error for key:value tuple ' +
449 'for --extra-files argument: ' + kv + '\n')
450 seen_error = True
451 else:
452 canonicalized.append([unquoted[0], 'portable', unquoted[2]])
453 elif len(unquoted) == 5:
454 if unquoted[1] != ':' or unquoted[3] != ':':
455 err.write('Syntax error for key:arch:url tuple ' +
456 'for --extra-files argument: ' +
457 kv + '\n')
458 seen_error = True
459 else:
460 canonicalized.append([unquoted[0], unquoted[2], unquoted[4]])
461 else:
462 err.write('Bad key:arch:url tuple for --extra-files: ' + kv + '\n')
463 if seen_error:
464 return None
465 return canonicalized
468 def GetSDKRoot():
469 """Returns the root directory of the NaCl SDK.
471 # This script should be installed in NACL_SDK_ROOT/tools. Assert that
472 # the 'toolchain' folder exists within this directory in case, for
473 # example, this script is moved to a different location.
474 # During the Chrome build this script is sometimes run outside of
475 # of an SDK but in these cases it should always be run with --objdump=
476 # and --no-default-libpath which avoids the need to call this function.
477 sdk_root = os.path.dirname(SCRIPT_DIR)
478 assert(os.path.exists(os.path.join(sdk_root, 'toolchain')))
479 return sdk_root
482 def FindObjdumpExecutable():
483 """Derive path to objdump executable to use for determining shared
484 object dependencies.
486 osname = getos.GetPlatform()
487 toolchain = os.path.join(GetSDKRoot(), 'toolchain', '%s_x86_glibc' % osname)
488 objdump = os.path.join(toolchain, 'bin', 'x86_64-nacl-objdump')
489 if osname == 'win':
490 objdump += '.exe'
492 if not os.path.exists(objdump):
493 sys.stderr.write('WARNING: failed to find objdump in default '
494 'location: %s' % objdump)
495 return None
497 return objdump
500 def GetDefaultLibPath(config):
501 """Derive default library path to use when searching for shared
502 objects. This currently include the toolchain library folders
503 as well as the top level SDK lib folder and the naclports lib
504 folder. We include both 32-bit and 64-bit library paths.
506 sdk_root = GetSDKRoot()
508 osname = getos.GetPlatform()
509 libpath = [
510 # Core toolchain libraries
511 'toolchain/%s_x86_glibc/x86_64-nacl/lib' % osname,
512 'toolchain/%s_x86_glibc/x86_64-nacl/lib32' % osname,
513 'toolchain/%s_arm_glibc/arm-nacl/lib' % osname,
514 # naclports installed libraries
515 'toolchain/%s_x86_glibc/x86_64-nacl/usr/lib' % osname,
516 'toolchain/%s_x86_glibc/i686-nacl/usr/lib' % osname,
517 'toolchain/%s_arm_glibc/arm-nacl/usr/lib' % osname,
518 # SDK bundle libraries
519 'lib/glibc_x86_32/%s' % config,
520 'lib/glibc_x86_64/%s' % config,
521 'lib/glibc_arm/%s' % config,
522 # naclports bundle libraries
523 'ports/lib/glibc_x86_32/%s' % config,
524 'ports/lib/glibc_x86_64/%s' % config,
525 'ports/lib/glibc_arm/%s' % config,
528 # In some cases (e.g. ASAN, TSAN, STANDALONE) the name of the configuration
529 # can be different to simply Debug or Release. For example 'msan_Release'.
530 # In this case we search for libraries first in this directory and then
531 # fall back to 'Release'.
532 if config not in ['Debug', 'Release']:
533 config_fallback = 'Release'
534 if 'Debug' in config:
535 config_fallback = 'Debug'
537 libpath += [
538 'lib/glibc_x86_32/%s' % config_fallback,
539 'lib/glibc_x86_64/%s' % config_fallback,
540 'lib/glibc_arm/%s' % config_fallback,
541 'ports/lib/glibc_x86_32/%s' % config_fallback,
542 'ports/lib/glibc_x86_64/%s' % config_fallback,
543 'ports/lib/glibc_arm/%s' % config_fallback,
546 bionic_dir = 'toolchain/%s_arm_bionic' % osname
547 if os.path.isdir(os.path.join(sdk_root, bionic_dir)):
548 libpath += [
549 '%s/arm-nacl/lib' % bionic_dir,
550 '%s/arm-nacl/usr/lib' % bionic_dir,
551 'lib/bionic_arm/%s' % config,
553 libpath = [os.path.normpath(p) for p in libpath]
554 libpath = [os.path.join(sdk_root, p) for p in libpath]
555 return libpath
558 def main(args):
559 parser = argparse.ArgumentParser(description=__doc__)
560 parser.add_argument('-o', '--output', dest='output',
561 help='Write manifest file to FILE (default is stdout)',
562 metavar='FILE')
563 parser.add_argument('-D', '--objdump', dest='objdump',
564 help='Override the default "objdump" tool used to find '
565 'shared object dependencies',
566 metavar='TOOL')
567 parser.add_argument('--no-default-libpath', action='store_true',
568 help="Don't include the SDK default library paths")
569 parser.add_argument('--debug-libs', action='store_true',
570 help='Legacy option, do not use')
571 parser.add_argument('--config', default='Release',
572 help='Use a particular library configuration (normally '
573 'Debug or Release)')
574 parser.add_argument('-L', '--library-path', dest='lib_path',
575 action='append', default=[],
576 help='Add DIRECTORY to library search path',
577 metavar='DIRECTORY')
578 parser.add_argument('-P', '--path-prefix', dest='path_prefix', default='',
579 help='Deprecated. An alias for --lib-prefix.',
580 metavar='DIRECTORY')
581 parser.add_argument('-p', '--lib-prefix', dest='lib_prefix', default='',
582 help='A path to prepend to shared libraries in the .nmf',
583 metavar='DIRECTORY')
584 parser.add_argument('-N', '--nexe-prefix', dest='nexe_prefix', default='',
585 help='A path to prepend to nexes in the .nmf',
586 metavar='DIRECTORY')
587 parser.add_argument('-s', '--stage-dependencies', dest='stage_dependencies',
588 help='Destination directory for staging libraries',
589 metavar='DIRECTORY')
590 parser.add_argument('--no-arch-prefix', action='store_true',
591 help='Don\'t put shared libraries in the lib32/lib64 '
592 'directories. Instead, they will be put in the same '
593 'directory as the .nexe that matches its architecture.')
594 parser.add_argument('-t', '--toolchain', help='Legacy option, do not use')
595 parser.add_argument('-n', '--name', dest='name',
596 help='Rename FOO as BAR',
597 action='append', default=[], metavar='FOO,BAR')
598 parser.add_argument('-x', '--extra-files',
599 help='Add extra key:file tuple to the "files"'
600 ' section of the .nmf',
601 action='append', default=[], metavar='FILE')
602 parser.add_argument('-O', '--pnacl-optlevel',
603 help='Set the optimization level to N in PNaCl manifests',
604 metavar='N')
605 parser.add_argument('--pnacl-debug-optlevel',
606 help='Set the optimization level to N for debugging '
607 'sections in PNaCl manifests',
608 metavar='N')
609 parser.add_argument('-v', '--verbose',
610 help='Verbose output', action='store_true')
611 parser.add_argument('-d', '--debug-mode',
612 help='Debug mode', action='store_true')
613 parser.add_argument('executables', metavar='EXECUTABLE', nargs='+')
615 # To enable bash completion for this command first install optcomplete
616 # and then add this line to your .bashrc:
617 # complete -F _optcomplete create_nmf.py
618 try:
619 import optcomplete
620 optcomplete.autocomplete(parser)
621 except ImportError:
622 pass
624 options = parser.parse_args(args)
625 if options.verbose:
626 Trace.verbose = True
627 if options.debug_mode:
628 DebugPrint.debug_mode = True
630 if options.toolchain is not None:
631 sys.stderr.write('warning: option -t/--toolchain is deprecated.\n')
632 if options.debug_libs:
633 sys.stderr.write('warning: --debug-libs is deprecated (use --config).\n')
634 # Implement legacy behavior
635 options.config = 'Debug'
637 canonicalized = ParseExtraFiles(options.extra_files, sys.stderr)
638 if canonicalized is None:
639 parser.error('Bad --extra-files (-x) argument syntax')
641 remap = {}
642 for ren in options.name:
643 parts = ren.split(',')
644 if len(parts) != 2:
645 parser.error('Expecting --name=<orig_arch.so>,<new_name.so>')
646 remap[parts[0]] = parts[1]
648 if options.path_prefix:
649 options.lib_prefix = options.path_prefix
651 for libpath in options.lib_path:
652 if not os.path.exists(libpath):
653 sys.stderr.write('Specified library path does not exist: %s\n' % libpath)
654 elif not os.path.isdir(libpath):
655 sys.stderr.write('Specified library is not a directory: %s\n' % libpath)
657 if not options.no_default_libpath:
658 # Add default libraries paths to the end of the search path.
659 options.lib_path += GetDefaultLibPath(options.config)
660 for path in options.lib_path:
661 Trace('libpath: %s' % path)
663 pnacl_optlevel = None
664 if options.pnacl_optlevel is not None:
665 pnacl_optlevel = int(options.pnacl_optlevel)
666 if pnacl_optlevel < 0 or pnacl_optlevel > 3:
667 sys.stderr.write(
668 'warning: PNaCl optlevel %d is unsupported (< 0 or > 3)\n' %
669 pnacl_optlevel)
670 if options.pnacl_debug_optlevel is not None:
671 pnacl_debug_optlevel = int(options.pnacl_debug_optlevel)
672 else:
673 pnacl_debug_optlevel = pnacl_optlevel
675 nmf_root = None
676 if options.output:
677 nmf_root = os.path.dirname(options.output)
679 nmf = NmfUtils(objdump=options.objdump,
680 main_files=options.executables,
681 lib_path=options.lib_path,
682 extra_files=canonicalized,
683 lib_prefix=options.lib_prefix,
684 nexe_prefix=options.nexe_prefix,
685 no_arch_prefix=options.no_arch_prefix,
686 remap=remap,
687 pnacl_optlevel=pnacl_optlevel,
688 pnacl_debug_optlevel=pnacl_debug_optlevel,
689 nmf_root=nmf_root)
691 if options.output is None:
692 sys.stdout.write(nmf.GetJson())
693 else:
694 with open(options.output, 'w') as output:
695 output.write(nmf.GetJson())
697 if options.stage_dependencies and not nmf.pnacl:
698 Trace('Staging dependencies...')
699 nmf.StageDependencies(options.stage_dependencies)
701 return 0
704 if __name__ == '__main__':
705 try:
706 rtn = main(sys.argv[1:])
707 except Error, e:
708 sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e))
709 rtn = 1
710 except KeyboardInterrupt:
711 sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
712 rtn = 1
713 sys.exit(rtn)