diff options
-rw-r--r-- | native_client_sdk/src/build_tools/sdk_files.list | 1 | ||||
-rwxr-xr-x | native_client_sdk/src/test_all.py | 1 | ||||
-rwxr-xr-x | native_client_sdk/src/tools/nacl_config.py | 257 | ||||
-rwxr-xr-x | native_client_sdk/src/tools/tests/nacl_config_test.py | 118 |
4 files changed, 377 insertions, 0 deletions
diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list index da1b7c4..56c5409 100644 --- a/native_client_sdk/src/build_tools/sdk_files.list +++ b/native_client_sdk/src/build_tools/sdk_files.list @@ -423,6 +423,7 @@ tools/irt_core_x86_64.nexe [win]tools/make.exe [linux,mac]tools/minidump_dump [linux,mac]tools/minidump_stackwalk +tools/nacl_config.py tools/nacl_gcc.mk [linux]tools/nacl_helper_bootstrap_x86_32 [linux]tools/nacl_helper_bootstrap_x86_64 diff --git a/native_client_sdk/src/test_all.py b/native_client_sdk/src/test_all.py index a13bf80..6e433e7 100755 --- a/native_client_sdk/src/test_all.py +++ b/native_client_sdk/src/test_all.py @@ -19,6 +19,7 @@ TEST_MODULES = [ 'fix_deps_test', 'getos_test', 'httpd_test', + 'nacl_config_test', 'oshelpers_test', 'parse_dsc_test', 'quote_test', diff --git a/native_client_sdk/src/tools/nacl_config.py b/native_client_sdk/src/tools/nacl_config.py new file mode 100755 index 0000000..1a9b469 --- /dev/null +++ b/native_client_sdk/src/tools/nacl_config.py @@ -0,0 +1,257 @@ +#!/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 optparse +import os +import posixpath +import sys + +import getos + + +if sys.version_info < (2, 6, 0): + sys.stderr.write("python 2.6 or later is required run this script\n") + sys.exit(1) + + +VALID_ARCHES = ('arm', 'x86_32', 'x86_64', 'i686') +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') +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. +PNACL_TOOLS = { + 'cc': 'clang', + 'c++': 'clang++', + 'gcc': 'clang', + 'g++': 'clang++', + 'ld': 'clang++' +} + +NACL_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(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, + '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: + ExpectArch(arch, VALID_ARCHES) + if arch == 'arm': + Expect(toolchain == '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 == 'pnacl': + assert arch is None + 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') + 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': + tool = PNACL_TOOLS.get(tool, tool) + full_tool_name = 'pnacl-%s' % tool + else: + ExpectArch(arch, VALID_ARCHES) + tool = NACL_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 GetLDFlags(): + return '-L%s' % GetSDKLibDir() + + +def main(args): + usage = 'Usage: %prog [options] <command>' + parser = optparse.OptionParser(usage=usage, description=__doc__) + parser.add_option('-t', '--toolchain', help='toolchain name. This can also ' + 'be specified with the NACL_TOOLCHAIN environment ' + 'variable.') + parser.add_option('-a', '--arch', help='architecture name. This can also be ' + 'specified with the NACL_ARCH environment variable.') + + group = optparse.OptionGroup(parser, 'Commands') + group.add_option('--tool', help='get tool path') + group.add_option('--cflags', + help='output all preprocessor and compiler flags', + action='store_true') + group.add_option('--libs', '--ldflags', help='output all linker flags', + action='store_true') + parser.add_option_group(group) + + 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.libs: + print GetLDFlags() + elif options.tool: + CheckValidToolchainArch(options.toolchain, options.arch, True) + 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) diff --git a/native_client_sdk/src/tools/tests/nacl_config_test.py b/native_client_sdk/src/tools/tests/nacl_config_test.py new file mode 100755 index 0000000..1d3489a --- /dev/null +++ b/native_client_sdk/src/tools/tests/nacl_config_test.py @@ -0,0 +1,118 @@ +#!/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. + +import os +import sys +import unittest + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +TOOLS_DIR = os.path.dirname(SCRIPT_DIR) +CHROME_SRC = os.path.dirname(os.path.dirname(os.path.dirname(TOOLS_DIR))) +MOCK_DIR = os.path.join(CHROME_SRC, "third_party", "pymock") + +# For the mock library +sys.path.append(MOCK_DIR) +import mock + +# For nacl_config, the module under test +sys.path.append(TOOLS_DIR) +import nacl_config + + +class TestNaclConfig(unittest.TestCase): + def setUp(self): + self.patches = [] + + get_sdk_path = self.AddAndStartPatch('getos.GetSDKPath') + get_sdk_path.return_value = '/sdk_root' + + get_platform = self.AddAndStartPatch('getos.GetPlatform') + get_platform.return_value = 'mac' + + def tearDown(self): + for patch in self.patches: + patch.stop() + + def AddAndStartPatch(self, name): + patch = mock.patch(name) + self.patches.append(patch) + return patch.start() + + def testCFlags(self): + cases = { + 'newlib': '-I/sdk_root/include -I/sdk_root/include/newlib', + 'glibc': '-I/sdk_root/include -I/sdk_root/include/glibc', + 'pnacl': '-I/sdk_root/include -I/sdk_root/include/pnacl', + 'win': '-I/sdk_root/include -I/sdk_root/include/win', + 'mac': '-I/sdk_root/include -I/sdk_root/include/mac', + 'linux': '-I/sdk_root/include -I/sdk_root/include/linux' + } + for toolchain, expected in cases.iteritems(): + self.assertEqual(expected, nacl_config.GetCFlags(toolchain)) + self.assertRaises(nacl_config.Error, nacl_config.GetCFlags, 'foo') + + def testLDFlags(self): + self.assertEqual('-L/sdk_root/lib', nacl_config.GetLDFlags()) + + def _TestTool(self, tool, nacl_tool=None, pnacl_tool=None): + nacl_tool = nacl_tool or tool + pnacl_tool = pnacl_tool or tool + + cases = { + ('newlib', 'x86_32'): + '/sdk_root/toolchain/mac_x86_newlib/bin/i686-nacl-%s' % nacl_tool, + ('newlib', 'x86_64'): + '/sdk_root/toolchain/mac_x86_newlib/bin/x86_64-nacl-%s' % nacl_tool, + ('newlib', 'arm'): + '/sdk_root/toolchain/mac_arm_newlib/bin/arm-nacl-%s' % nacl_tool, + + ('glibc', 'x86_32'): + '/sdk_root/toolchain/mac_x86_glibc/bin/i686-nacl-%s' % nacl_tool, + ('glibc', 'x86_64'): + '/sdk_root/toolchain/mac_x86_glibc/bin/x86_64-nacl-%s' % nacl_tool, + ('glibc', 'arm'): + '/sdk_root/toolchain/mac_arm_glibc/bin/arm-nacl-%s' % nacl_tool, + + 'pnacl': '/sdk_root/toolchain/mac_pnacl/bin/pnacl-%s' % pnacl_tool, + } + + for tc_arch, expected in cases.iteritems(): + if isinstance(tc_arch, tuple): + toolchain = tc_arch[0] + arch = tc_arch[1] + else: + toolchain = tc_arch + arch = None + self.assertEqual(expected, nacl_config.GetToolPath(toolchain, arch, tool)) + + for toolchain in ('host', 'mac', 'win', 'linux'): + self.assertRaises(nacl_config.Error, + nacl_config.GetToolPath, toolchain, None, tool) + + def testCC(self): + self._TestTool('cc', 'gcc', 'clang') + + def testCXX(self): + self._TestTool('c++', 'g++', 'clang++') + + def testLD(self): + self._TestTool('ld', 'g++', 'clang++') + + def testStandardTool(self): + for tool in ('nm', 'strip', 'ar', 'ranlib'): + self._TestTool(tool) + + def testGDB(self): + # We always use the same gdb (it supports multiple toolchains/architectures) + expected = '/sdk_root/toolchain/mac_x86_newlib/bin/x86_64-nacl-gdb' + for toolchain in ('newlib', 'glibc', 'pnacl'): + for arch in ('x86_32', 'x86_64', 'arm'): + self.assertEqual(expected, + nacl_config.GetToolPath(toolchain, arch, 'gdb')) + + + +if __name__ == '__main__': + unittest.main() |