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 """Compiler version checking tool for gcc
8 Print gcc version as XY if you are running gcc X.Y.*.
9 This is used to tweak build flags for gcc 4.4.
18 compiler_version_cache
= {} # Map from (compiler, tool) -> version.
21 def Usage(program_name
):
22 print '%s MODE TOOL' % os
.path
.basename(program_name
)
23 print 'MODE: host or target.'
24 print 'TOOL: assembler or compiler or linker.'
30 raise Exception('Invalid number of arguments')
33 if mode
not in ('host', 'target'):
34 raise Exception('Invalid mode: %s' % mode
)
35 if tool
not in ('assembler', 'compiler', 'linker'):
36 raise Exception('Invalid tool: %s' % tool
)
40 def GetEnvironFallback(var_list
, default
):
41 """Look up an environment variable from a possible list of variable names."""
44 return os
.environ
[var
]
48 def GetVersion(compiler
, tool
):
49 tool_output
= tool_error
= None
50 cache_key
= (compiler
, tool
)
51 cached_version
= compiler_version_cache
.get(cache_key
)
55 # Note that compiler could be something tricky like "distcc g++".
56 if tool
== "compiler":
57 compiler
= compiler
+ " -dumpversion"
59 version_re
= re
.compile(r
"(\d+)\.(\d+)")
60 elif tool
== "assembler":
61 compiler
= compiler
+ " -Xassembler --version -x assembler -c /dev/null"
62 # Unmodified: GNU assembler (GNU Binutils) 2.24
63 # Ubuntu: GNU assembler (GNU Binutils for Ubuntu) 2.22
64 # Fedora: GNU assembler version 2.23.2
65 version_re
= re
.compile(r
"^GNU [^ ]+ .* (\d+).(\d+).*?$", re
.M
)
66 elif tool
== "linker":
67 compiler
= compiler
+ " -Xlinker --version"
69 # Unmodified: GNU ld (GNU Binutils) 2.24
70 # Ubuntu: GNU ld (GNU Binutils for Ubuntu) 2.22
71 # Fedora: GNU ld version 2.23.2
73 # Unmodified: GNU gold (GNU Binutils 2.24) 1.11
74 # Ubuntu: GNU gold (GNU Binutils for Ubuntu 2.22) 1.11
75 # Fedora: GNU gold (version 2.23.2) 1.11
76 version_re
= re
.compile(r
"^GNU [^ ]+ .* (\d+).(\d+).*?$", re
.M
)
78 raise Exception("Unknown tool %s" % tool
)
80 # Force the locale to C otherwise the version string could be localized
81 # making regex matching fail.
82 env
= os
.environ
.copy()
84 pipe
= subprocess
.Popen(compiler
, shell
=True, env
=env
,
85 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
86 tool_output
, tool_error
= pipe
.communicate()
88 raise subprocess
.CalledProcessError(pipe
.returncode
, compiler
)
90 parsed_output
= version_re
.match(tool_output
)
91 result
= parsed_output
.group(1) + parsed_output
.group(2)
92 compiler_version_cache
[cache_key
] = result
96 sys
.stderr
.write(tool_error
)
97 print >> sys
.stderr
, "compiler_version.py failed to execute:", compiler
98 print >> sys
.stderr
, e
104 (mode
, tool
) = ParseArgs(args
[1:])
106 sys
.stderr
.write(e
.message
+ '\n\n')
107 return Usage(args
[0])
109 ret_code
, result
= ExtractVersion(mode
, tool
)
116 """Hook to be called from gyp without starting a separate python
118 (mode
, tool
) = ParseArgs(args
)
119 ret_code
, result
= ExtractVersion(mode
, tool
)
122 raise Exception("Failed to extract compiler version for args: %s" % args
)
125 def ExtractVersion(mode
, tool
):
126 # Check if various CXX environment variables exist and use them if they
127 # exist. The preferences and fallback order is a close approximation of
128 # GenerateOutputForConfig() in GYP's ninja generator.
129 # The main difference being not supporting GYP's make_global_settings.
130 environments
= ['CXX_target', 'CXX']
132 environments
= ['CXX_host'] + environments
;
133 compiler
= GetEnvironFallback(environments
, 'c++')
136 compiler_version
= GetVersion(compiler
, tool
)
137 if compiler_version
!= "":
138 return (0, compiler_version
)
142 if __name__
== "__main__":
143 sys
.exit(main(sys
.argv
))