#!/usr/bin/env python # Copyright 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A helper script to print paths of NaCl binaries, includes, libs, etc. It is similar in behavior to pkg-config or sdl-config. """ import argparse import os import posixpath import sys import getos if sys.version_info < (2, 7, 0): sys.stderr.write("python 2.7 or later is required run this script\n") sys.exit(1) VALID_ARCHES = ('arm', 'x86_32', 'x86_64', 'i686') VALID_PNACL_ARCHES = (None, 'pnacl') ARCH_NAME = { 'arm': 'arm', 'x86_32': 'i686', 'i686': 'i686', 'x86_64': 'x86_64' } ARCH_ALT_NAME = { 'arm': 'arm', 'x86_32': 'x86_32', 'i686': 'x86_32', 'x86_64': 'x86_64' } ARCH_BASE_NAME = { 'arm': 'arm', 'x86_32': 'x86', 'i686': 'x86', 'x86_64': 'x86' } NACL_TOOLCHAINS = ('newlib', 'glibc', 'pnacl', 'bionic', 'clang-newlib') HOST_TOOLCHAINS = ('linux', 'mac', 'win') VALID_TOOLCHAINS = list(HOST_TOOLCHAINS) + list(NACL_TOOLCHAINS) + ['host'] # This is not an exhaustive list of tools, just the ones that need to be # special-cased. # e.g. For PNaCL cc => pnacl-clang # For NaCl cc => pnacl-gcc # # Most tools will be passed through directly. # e.g. For PNaCl foo => pnacl-foo # For NaCl foo => x86_64-nacl-foo. CLANG_TOOLS = { 'cc': 'clang', 'c++': 'clang++', 'gcc': 'clang', 'g++': 'clang++', 'ld': 'clang++' } GCC_TOOLS = { 'cc': 'gcc', 'c++': 'g++', 'gcc': 'gcc', 'g++': 'g++', 'ld': 'g++' } class Error(Exception): pass def Expect(condition, message): if not condition: raise Error(message) def ExpectToolchain(toolchain, expected_toolchains): Expect(toolchain in expected_toolchains, 'Expected toolchain to be one of [%s], not %s.' % ( ', '.join(expected_toolchains), toolchain)) def ExpectArch(arch, expected_arches): Expect(arch in expected_arches, 'Expected arch to be one of [%s], not %s.' % ( ', '.join(map(str, expected_arches)), arch)) def CheckValidToolchainArch(toolchain, arch, arch_required=False): if toolchain or arch or arch_required: ExpectToolchain(toolchain, VALID_TOOLCHAINS) if toolchain in HOST_TOOLCHAINS: Expect(arch is None, 'Expected no arch for host toolchain %r. Got %r.' % ( toolchain, arch)) elif toolchain == 'pnacl': Expect(arch is None or arch == 'pnacl', 'Expected no arch for toolchain %r. Got %r.' % (toolchain, arch)) elif arch_required: Expect(arch is not None, 'Expected arch to be one of [%s] for toolchain %r.\n' 'Use the -a or --arch flags to specify one.\n' % ( ', '.join(VALID_ARCHES), toolchain)) if arch: if toolchain == 'pnacl': ExpectArch(arch, VALID_PNACL_ARCHES) else: ExpectArch(arch, VALID_ARCHES) if arch == 'arm': Expect(toolchain in ['newlib', 'bionic', 'clang-newlib'], 'The arm arch only supports newlib.') def GetArchName(arch): return ARCH_NAME.get(arch) def GetArchAltName(arch): return ARCH_ALT_NAME.get(arch) def GetArchBaseName(arch): return ARCH_BASE_NAME.get(arch) def CanonicalizeToolchain(toolchain): if toolchain == 'host': return getos.GetPlatform() return toolchain def GetPosixSDKPath(): sdk_path = getos.GetSDKPath() if getos.GetPlatform() == 'win': return sdk_path.replace('\\', '/') else: return sdk_path def GetToolchainDir(toolchain, arch=None): ExpectToolchain(toolchain, NACL_TOOLCHAINS) root = GetPosixSDKPath() platform = getos.GetPlatform() if toolchain in ('pnacl', 'clang-newlib'): subdir = '%s_pnacl' % platform else: assert arch is not None subdir = '%s_%s_%s' % (platform, GetArchBaseName(arch), toolchain) return posixpath.join(root, 'toolchain', subdir) def GetToolchainArchDir(toolchain, arch): ExpectToolchain(toolchain, NACL_TOOLCHAINS) assert arch is not None toolchain_dir = GetToolchainDir(toolchain, arch) arch_dir = '%s-nacl' % GetArchName(arch) return posixpath.join(toolchain_dir, arch_dir) def GetToolchainBinDir(toolchain, arch=None): ExpectToolchain(toolchain, NACL_TOOLCHAINS) return posixpath.join(GetToolchainDir(toolchain, arch), 'bin') def GetSDKIncludeDirs(toolchain): root = GetPosixSDKPath() base_include = posixpath.join(root, 'include') if toolchain == 'clang-newlib': toolchain = 'newlib' return [base_include, posixpath.join(base_include, toolchain)] def GetSDKLibDir(): return posixpath.join(GetPosixSDKPath(), 'lib') # Commands def GetToolPath(toolchain, arch, tool): if tool == 'gdb': # Always use the same gdb; it supports multiple toolchains/architectures. # NOTE: this is always a i686 executable. i686-nacl-gdb is a symlink to # x86_64-nacl-gdb. return posixpath.join(GetToolchainBinDir('newlib', 'x86_64'), 'x86_64-nacl-gdb') if toolchain == 'pnacl': CheckValidToolchainArch(toolchain, arch) tool = CLANG_TOOLS.get(tool, tool) full_tool_name = 'pnacl-%s' % tool else: CheckValidToolchainArch(toolchain, arch, arch_required=True) ExpectArch(arch, VALID_ARCHES) if toolchain == 'clang-newlib': tool = CLANG_TOOLS.get(tool, tool) else: tool = GCC_TOOLS.get(tool, tool) full_tool_name = '%s-nacl-%s' % (GetArchName(arch), tool) return posixpath.join(GetToolchainBinDir(toolchain, arch), full_tool_name) def GetCFlags(toolchain): ExpectToolchain(toolchain, VALID_TOOLCHAINS) return ' '.join('-I%s' % dirname for dirname in GetSDKIncludeDirs(toolchain)) def GetIncludeDirs(toolchain): ExpectToolchain(toolchain, VALID_TOOLCHAINS) return ' '.join(GetSDKIncludeDirs(toolchain)) def GetLDFlags(): return '-L%s' % GetSDKLibDir() def main(args): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-t', '--toolchain', help='toolchain name. This can also ' 'be specified with the NACL_TOOLCHAIN environment ' 'variable.') parser.add_argument('-a', '--arch', help='architecture name. This can also ' 'be specified with the NACL_ARCH environment variable.') group = parser.add_argument_group('Commands') group.add_argument('--tool', help='get tool path') group.add_argument('--cflags', help='output all preprocessor and compiler flags', action='store_true') group.add_argument('--libs', '--ldflags', help='output all linker flags', action='store_true') group.add_argument('--include-dirs', help='output include dirs, separated by spaces', action='store_true') options = parser.parse_args(args) # Get toolchain/arch from environment, if not specified on commandline options.toolchain = options.toolchain or os.getenv('NACL_TOOLCHAIN') options.arch = options.arch or os.getenv('NACL_ARCH') options.toolchain = CanonicalizeToolchain(options.toolchain) CheckValidToolchainArch(options.toolchain, options.arch) if options.cflags: print GetCFlags(options.toolchain) elif options.include_dirs: print GetIncludeDirs(options.toolchain) elif options.libs: print GetLDFlags() elif options.tool: print GetToolPath(options.toolchain, options.arch, options.tool) else: parser.error('Expected a command. Run with --help for more information.') return 0 if __name__ == '__main__': try: sys.exit(main(sys.argv[1:])) except Error as e: sys.stderr.write(str(e) + '\n') sys.exit(1)