1342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch#!/usr/bin/env python
2342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# Use of this source code is governed by a BSD-style license that can be
4342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# found in the LICENSE file.
5342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
6342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch"""Compiler version checking tool for gcc
7342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
8342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen MurdochPrint gcc version as XY if you are running gcc X.Y.*.
9342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen MurdochThis is used to tweak build flags for gcc 4.4.
10342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch"""
11342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
12342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport os
13342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport re
14342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport subprocess
15342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport sys
16342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
17342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
18342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochcompiler_version_cache = {}  # Map from (compiler, tool) -> version.
19342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
20342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
21342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef Usage(program_name):
22342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  print '%s MODE TOOL' % os.path.basename(program_name)
23342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  print 'MODE: host or target.'
24342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  print 'TOOL: assembler or compiler or linker.'
25342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return 1
26342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
27342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
28342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef ParseArgs(args):
29342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if len(args) != 2:
30342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    raise Exception('Invalid number of arguments')
31342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  mode = args[0]
32342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  tool = args[1]
33342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if mode not in ('host', 'target'):
34342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    raise Exception('Invalid mode: %s' % mode)
35342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if tool not in ('assembler',):
36342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    raise Exception('Invalid tool: %s' % tool)
37342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return mode, tool
38342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
39342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
40342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef GetEnvironFallback(var_list, default):
41342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  """Look up an environment variable from a possible list of variable names."""
42342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  for var in var_list:
43342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    if var in os.environ:
44342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      return os.environ[var]
45342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return default
46342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
47342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
48342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef GetVersion(compiler, tool):
49342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  tool_output = tool_error = None
50342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  cache_key = (compiler, tool)
51342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  cached_version = compiler_version_cache.get(cache_key)
52342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if cached_version:
53342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return cached_version
54342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  try:
55342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    # Note that compiler could be something tricky like "distcc g++".
56342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    if tool == "assembler":
57342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      compiler = compiler + " -Xassembler --version -x assembler -c /dev/null"
58342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      # Unmodified: GNU assembler (GNU Binutils) 2.24
59342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      # Ubuntu: GNU assembler (GNU Binutils for Ubuntu) 2.22
60342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      # Fedora: GNU assembler version 2.23.2
61342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      version_re = re.compile(r"^GNU [^ ]+ .* (\d+).(\d+).*?$", re.M)
62342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    else:
63342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      raise Exception("Unknown tool %s" % tool)
64342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
65342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    # Force the locale to C otherwise the version string could be localized
66342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    # making regex matching fail.
67342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    env = os.environ.copy()
68342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    env["LC_ALL"] = "C"
69342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    pipe = subprocess.Popen(compiler, shell=True, env=env,
70342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
71342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    tool_output, tool_error = pipe.communicate()
72342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    if pipe.returncode:
73342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      raise subprocess.CalledProcessError(pipe.returncode, compiler)
74342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
75342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    parsed_output = version_re.match(tool_output)
76342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    result = parsed_output.group(1) + parsed_output.group(2)
77342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    compiler_version_cache[cache_key] = result
78342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return result
79342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  except Exception, e:
80342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    if tool_error:
81342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      sys.stderr.write(tool_error)
82342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    print >> sys.stderr, "compiler_version.py failed to execute:", compiler
83342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    print >> sys.stderr, e
84342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return ""
85342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
86342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
87342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef main(args):
88342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  try:
89342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    (mode, tool) = ParseArgs(args[1:])
90342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  except Exception, e:
91342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    sys.stderr.write(e.message + '\n\n')
92342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return Usage(args[0])
93342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
94342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  ret_code, result = ExtractVersion(mode, tool)
95342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if ret_code == 0:
96342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    print result
97342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return ret_code
98342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
99342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
100342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef DoMain(args):
101342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  """Hook to be called from gyp without starting a separate python
102342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  interpreter."""
103342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  (mode, tool) = ParseArgs(args)
104342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  ret_code, result = ExtractVersion(mode, tool)
105342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if ret_code == 0:
106342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return result
107342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  raise Exception("Failed to extract compiler version for args: %s" % args)
108342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
109342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
110342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef ExtractVersion(mode, tool):
111342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # Check if various CXX environment variables exist and use them if they
112342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # exist. The preferences and fallback order is a close approximation of
113342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # GenerateOutputForConfig() in GYP's ninja generator.
114342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # The main difference being not supporting GYP's make_global_settings.
115342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  environments = ['CXX_target', 'CXX']
116342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if mode == 'host':
117342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    environments = ['CXX_host'] + environments;
118342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  compiler = GetEnvironFallback(environments, 'c++')
119342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
120342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if compiler:
121342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    compiler_version = GetVersion(compiler, tool)
122342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    if compiler_version != "":
123342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      return (0, compiler_version)
124342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return (1, None)
125342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
126342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
127342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochif __name__ == "__main__":
128342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  sys.exit(main(sys.argv))
129