Support Intel C compiler 9.0. (Gary Oberbrunner)
[scons.git] / src / engine / SCons / Tool / intelc.py
1 """SCons.Tool.icl
2
3 Tool-specific initialization for the Intel C/C++ compiler.
4 Supports Linux and Windows compilers, v7 and up.
5
6 There normally shouldn't be any need to import this module directly.
7 It will usually be imported through the generic SCons.Tool.Tool()
8 selection method.
9
10 """
11
12 #
13 # __COPYRIGHT__
14 #
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
22 #
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
25 #
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #
34
35 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
36
37 import math, sys, os.path, glob, string, re
38
39 is_win32 = sys.platform == 'win32'
40 is_linux = sys.platform == 'linux2'
41
42 if is_win32:
43     import SCons.Tool.msvc
44 elif is_linux:
45     import SCons.Tool.gcc
46 import SCons.Util
47 import SCons.Warnings
48
49 # Exceptions for this tool
50 class IntelCError(SCons.Errors.InternalError):
51     pass
52 class MissingRegistryError(IntelCError): # missing registry entry
53     pass
54 class MissingDirError(IntelCError):     # dir not found
55     pass
56 class NoRegistryModuleError(IntelCError): # can't read registry at all
57     pass
58
59 def linux_ver_normalize(vstr):
60     """Normalize a Linux compiler version number.
61     Intel changed from "80" to "9.0" in 2005, so we assume if the number
62     is greater than 60 it's an old-style number and otherwise new-style.
63     Always returns an old-style float like 80 or 90 for compatibility with Windows.
64     Shades of Y2K!"""
65     f = float(vstr)
66     if is_win32:
67         return f
68     else:
69         if f < 60: return f * 10.0
70         else: return f
71
72 def vercmp(a, b):
73     """Compare strings as floats,
74     but Intel changed Linux naming convention at 9.0"""
75     return cmp(linux_ver_normalize(b), linux_ver_normalize(a))
76
77 def get_version_from_list(v, vlist):
78     """See if we can match v (string) in vlist (list of strings)
79     Linux has to match in a fuzzy way."""
80     if is_win32:
81         # Simple case, just find it in the list
82         if v in vlist: return v
83         else: return None
84     else:
85         # Fuzzy match: normalize version number first, but still return
86         # original non-normalized form.
87         fuzz = 0.001
88         for vi in vlist:
89             if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz:
90                 return vi
91         # Not found
92         return None
93
94 def get_intel_registry_value(valuename, version=None, abi=None):
95     """
96     Return a value from the Intel compiler registry tree. (Win32 only)
97     """
98
99     # Open the key:
100     K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper()
101     try:
102         k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K)
103     except SCons.Util.RegError:
104         raise MissingRegistryError, \
105               "%s was not found in the registry, for Intel compiler version %s"%(K, version)
106
107     # Get the value:
108     try:
109         v = SCons.Util.RegQueryValueEx(k, valuename)[0]
110         return v  # or v.encode('iso-8859-1', 'replace') to remove unicode?
111     except SCons.Util.RegError:
112         raise MissingRegistryError, \
113               "%s\\%s was not found in the registry."%(K, value)
114
115
116 def get_all_compiler_versions():
117     """Returns a sorted list of strings, like "70" or "80" or "9.0"
118     with most recent compiler version first.
119     """
120     versions=[]
121     if is_win32:
122         keyname = 'Software\\Intel\\Compilers\\C++'
123         try:
124             k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
125                                         keyname)
126         except WindowsError:
127             return []
128         i = 0
129         versions = []
130         try:
131             while i < 100:
132                 subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError
133                 # Check that this refers to an existing dir.
134                 # This is not 100% perfect but should catch common
135                 # installation issues like when the compiler was installed
136                 # and then the install directory deleted or moved (rather
137                 # than uninstalling properly), so the registry values
138                 # are still there.
139                 ok = False
140                 for check_abi in ('IA32', 'IA64'):
141                     try:
142                         d = get_intel_registry_value('ProductDir', subkey, check_abi)
143                     except MissingRegistryError:
144                         continue  # not found in reg, keep going
145                     if os.path.exists(d): ok = True
146                 if ok:
147                     versions.append(subkey)
148                 else:
149                     # Registry points to nonexistent dir.  Ignore this version.
150                     print "Ignoring "+str(get_intel_registry_value('ProductDir', subkey, 'IA32'))
151                 i = i + 1
152         except EnvironmentError:
153             # no more subkeys
154             pass
155     elif is_linux:
156         for d in glob.glob('/opt/intel_cc_*'):
157             # Typical dir here is /opt/intel_cc_80.
158             versions.append(re.search(r'cc_(.*)$', d).group(1))
159         for d in glob.glob('/opt/intel/cc/*'):
160             # Typical dir here is /opt/intel/cc/9.0.
161             versions.append(re.search(r'([0-9.]+)$', d).group(1))
162     versions.sort(vercmp)
163     return versions
164
165 def get_intel_compiler_top(version, abi):
166     """
167     Return the main path to the top-level dir of the Intel compiler,
168     using the given version.
169     The compiler will be in <top>/bin/icl.exe (icc on linux),
170     the include dir is <top>/include, etc.
171     """
172
173     if is_win32:
174         if not SCons.Util.can_read_reg:
175             raise NoRegistryModuleError, "No Windows registry module was found"
176         top = get_intel_registry_value('ProductDir', version, abi)
177
178         if not os.path.exists(os.path.join(top, "Bin", "icl.exe")):
179             raise MissingDirError, \
180                   "Can't find Intel compiler in %s"%(top)
181     elif is_linux:
182         # first dir is new (>=9.0) style, second is old (8.0) style.
183         dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s')
184         top=None
185         for d in dirs:
186             top = d%version
187             if os.path.exists(os.path.join(top, "bin", "icc")): break
188         if not top:
189             raise MissingDirError, \
190                   "Can't find version %s Intel compiler in %s"%(version,top)
191     return top
192
193
194 def generate(env, version=None, abi=None, topdir=None, verbose=0):
195     """Add Builders and construction variables for Intel C/C++ compiler
196     to an Environment.
197     args:
198       version: (string) compiler version to use, like "80"
199       abi:     (string) 'win32' or whatever Itanium version wants
200       topdir:  (string) compiler top dir, like
201                          "c:\Program Files\Intel\Compiler70"
202                         If topdir is used, version and abi are ignored.
203       verbose: (int)    if >0, prints compiler version used.
204     """
205     if not (is_linux or is_win32):
206         # can't handle this platform
207         return
208
209     if is_win32:
210         SCons.Tool.msvc.generate(env)
211     elif is_linux:
212         SCons.Tool.gcc.generate(env)
213         
214     # if version is unspecified, use latest
215     vlist = get_all_compiler_versions()
216     if not version:
217         if vlist:
218             version = vlist[0]
219     else:
220         if not get_version_from_list(version, vlist):
221             raise SCons.Errors.UserError, \
222                   "Invalid Intel compiler version %s: "%version + \
223                   "installed versions are %s"%(', '.join(vlist))
224
225     # if abi is unspecified, use ia32 (ia64 is another possibility)
226     if abi is None:
227         abi = "ia32"                    # or ia64, I believe
228
229     if topdir is None and version:
230         try:
231             topdir = get_intel_compiler_top(version, abi)
232         except (SCons.Util.RegError, IntelCError):
233             topdir = None
234
235     if topdir:
236
237         if verbose:
238             print "Intel C compiler: using version '%s' (%g), abi %s, in '%s'"%\
239                   (version, linux_ver_normalize(version),abi,topdir)
240
241         env['INTEL_C_COMPILER_TOP'] = topdir
242         if is_linux:
243             paths={'INCLUDE'         : 'include',
244                    'LIB'             : 'lib',
245                    'PATH'            : 'bin',
246                    'LD_LIBRARY_PATH' : 'lib'}
247             for p in paths:
248                 env.PrependENVPath(p, os.path.join(topdir, paths[p]))
249         if is_win32:
250             #       env key    reg valname   default subdir of top
251             paths=(('INCLUDE', 'IncludeDir', 'Include'),
252                    ('LIB'    , 'LibDir',     'Lib'),
253                    ('PATH'   , 'BinDir',     'Bin'))
254             # Each path has a registry entry, use that or default to subdir
255             for p in paths:
256                 try:
257                     path=get_intel_registry_value(p[1], version, abi)
258                     # These paths may have $(ICInstallDir)
259                     # which needs to be substituted with the topdir.
260                     path=path.replace('$(ICInstallDir)', topdir + os.sep)
261                 except IntelCError:
262                     # Couldn't get it from registry: use default subdir of topdir
263                     env.PrependENVPath(p[0], os.path.join(topdir, p[2]))
264                 else:
265                     env.PrependENVPath(p[0], string.split(path, os.pathsep))
266                     # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]]))
267
268     if is_win32:
269         env['CC']        = 'icl'
270         env['CXX']       = 'icl'
271         env['LINK']      = 'xilink'
272     else:
273         env['CC']        = 'icc'
274         env['CXX']       = 'icpc'
275         env['LINK']      = '$CC'
276     # This is not the exact (detailed) compiler version,
277     # just the major version as determined above or specified
278     # by the user.  It is a float like 80 or 90, in normalized form for Linux
279     # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0)
280     if version:
281         env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version)
282
283     if is_win32:
284         # Look for license file dir
285         # in system environment, registry, and default location.
286         envlicdir = os.environ.get("INTEL_LICENSE_FILE", '')
287         K = ('SOFTWARE\Intel\Licenses')
288         try:
289             k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K)
290             reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0]
291         except (AttributeError, SCons.Util.RegError):
292             reglicdir = ""
293         defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses'
294
295         licdir = None
296         for ld in [envlicdir, reglicdir]:
297             if ld and os.path.exists(ld):
298                 licdir = ld
299                 break
300         if not licdir:
301             licdir = defaultlicdir
302             if not os.path.exists(licdir):
303                 class ICLLicenseDirWarning(SCons.Warnings.Warning):
304                     pass
305                 SCons.Warnings.enableWarningClass(ICLLicenseDirWarning)
306                 SCons.Warnings.warn(ICLLicenseDirWarning,
307                                     "Intel license dir was not found."
308                                     "  Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)."
309                                     "  Using the default path as a last resort."
310                                         % (envlicdir, reglicdir, defaultlicdir))
311         env['ENV']['INTEL_LICENSE_FILE'] = licdir
312
313 def exists(env):
314     if not (is_linux or is_win32):
315         # can't handle this platform
316         return 0
317
318     try:
319         versions = get_all_compiler_versions()
320     except (SCons.Util.RegError, IntelCError):
321         versions = None
322     detected = versions is not None and len(versions) > 0
323     if not detected:
324         # try env.Detect, maybe that will work
325         if is_win32:
326             return env.Detect('icl')
327         elif is_linux:
328             return env.Detect('icc')
329     return detected
330
331 # end of file