chromium/native_client_sdk/src/tools/nacl_config.py

#!/usr/bin/env python
# Copyright 2013 The Chromium Authors
# 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', 'le32')
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 = ('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 in ['pnacl', 'le32'],
           '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)


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('glibc', '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)