1cc13127533d23fd83fe2861503f52edc4f8cae2
[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_windows = sys.platform == 'win32'
40 is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or 
41                            ('PROCESSOR_ARCHITEW6432' in os.environ and
42                             os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64'))
43 is_linux = sys.platform == 'linux2'
44 is_mac     = sys.platform == 'darwin'
45
46 if is_windows:
47     import SCons.Tool.msvc
48 elif is_linux:
49     import SCons.Tool.gcc
50 elif is_mac:
51     import SCons.Tool.gcc
52 import SCons.Util
53 import SCons.Warnings
54
55 # Exceptions for this tool
56 class IntelCError(SCons.Errors.InternalError):
57     pass
58 class MissingRegistryError(IntelCError): # missing registry entry
59     pass
60 class MissingDirError(IntelCError):     # dir not found
61     pass
62 class NoRegistryModuleError(IntelCError): # can't read registry at all
63     pass
64
65 def uniquify(s):
66     """Return a sequence containing only one copy of each unique element from input sequence s.
67     Does not preserve order.
68     Input sequence must be hashable (i.e. must be usable as a dictionary key)."""
69     u = {}
70     for x in s:
71         u[x] = 1
72     return u.keys()
73
74 def linux_ver_normalize(vstr):
75     """Normalize a Linux compiler version number.
76     Intel changed from "80" to "9.0" in 2005, so we assume if the number
77     is greater than 60 it's an old-style number and otherwise new-style.
78     Always returns an old-style float like 80 or 90 for compatibility with Windows.
79     Shades of Y2K!"""
80     # Check for version number like 9.1.026: return 91.026
81     m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr)
82     if m:
83         vmaj,vmin,build = m.groups()
84         return float(vmaj) * 10 + float(vmin) + float(build) / 1000.;
85     else:
86         f = float(vstr)
87         if is_windows:
88             return f
89         else:
90             if f < 60: return f * 10.0
91             else: return f
92
93 def check_abi(abi):
94     """Check for valid ABI (application binary interface) name,
95     and map into canonical one"""
96     if not abi:
97         return None
98     abi = abi.lower()
99     # valid_abis maps input name to canonical name
100     if is_windows:
101         valid_abis = {'ia32'  : 'ia32',
102                       'x86'   : 'ia32',
103                       'ia64'  : 'ia64',
104                       'em64t' : 'em64t',
105                       'amd64' : 'em64t'}
106     if is_linux:
107         valid_abis = {'ia32'   : 'ia32',
108                       'x86'    : 'ia32',
109                       'x86_64' : 'x86_64',
110                       'em64t'  : 'x86_64',
111                       'amd64'  : 'x86_64'}
112     if is_mac:
113         valid_abis = {'ia32'   : 'ia32',
114                       'x86'    : 'ia32',
115                       'x86_64' : 'x86_64',
116                       'em64t'  : 'x86_64'}
117     try:
118         abi = valid_abis[abi]
119     except KeyError:
120         raise SCons.Errors.UserError, \
121               "Intel compiler: Invalid ABI %s, valid values are %s"% \
122               (abi, valid_abis.keys())
123     return abi
124
125 def vercmp(a, b):
126     """Compare strings as floats,
127     but Intel changed Linux naming convention at 9.0"""
128     return cmp(linux_ver_normalize(b), linux_ver_normalize(a))
129
130 def get_version_from_list(v, vlist):
131     """See if we can match v (string) in vlist (list of strings)
132     Linux has to match in a fuzzy way."""
133     if is_windows:
134         # Simple case, just find it in the list
135         if v in vlist: return v
136         else: return None
137     else:
138         # Fuzzy match: normalize version number first, but still return
139         # original non-normalized form.
140         fuzz = 0.001
141         for vi in vlist:
142             if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz:
143                 return vi
144         # Not found
145         return None
146
147 def get_intel_registry_value(valuename, version=None, abi=None):
148     """
149     Return a value from the Intel compiler registry tree. (Windows only)
150     """
151     # Open the key:
152     if is_win64:
153         K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper()
154     else:
155         K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper()
156     try:
157         k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K)
158     except SCons.Util.RegError:
159         raise MissingRegistryError, \
160               "%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)
161
162     # Get the value:
163     try:
164         v = SCons.Util.RegQueryValueEx(k, valuename)[0]
165         return v  # or v.encode('iso-8859-1', 'replace') to remove unicode?
166     except SCons.Util.RegError:
167         raise MissingRegistryError, \
168               "%s\\%s was not found in the registry."%(K, valuename)
169
170
171 def get_all_compiler_versions():
172     """Returns a sorted list of strings, like "70" or "80" or "9.0"
173     with most recent compiler version first.
174     """
175     versions=[]
176     if is_windows:
177         if is_win64:
178             keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++'
179         else:
180             keyname = 'Software\\Intel\\Compilers\\C++'
181         try:
182             k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
183                                         keyname)
184         except WindowsError:
185             return []
186         i = 0
187         versions = []
188         try:
189             while i < 100:
190                 subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError
191                 # Check that this refers to an existing dir.
192                 # This is not 100% perfect but should catch common
193                 # installation issues like when the compiler was installed
194                 # and then the install directory deleted or moved (rather
195                 # than uninstalling properly), so the registry values
196                 # are still there.
197                 ok = False
198                 for try_abi in ('IA32', 'IA32e',  'IA64', 'EM64T'):
199                     try:
200                         d = get_intel_registry_value('ProductDir', subkey, try_abi)
201                     except MissingRegistryError:
202                         continue  # not found in reg, keep going
203                     if os.path.exists(d): ok = True
204                 if ok:
205                     versions.append(subkey)
206                 else:
207                     try:
208                         # Registry points to nonexistent dir.  Ignore this
209                         # version.
210                         value = get_intel_registry_value('ProductDir', subkey, 'IA32')
211                     except MissingRegistryError, e:
212
213                         # Registry key is left dangling (potentially
214                         # after uninstalling).
215
216                         print \
217                             "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \
218                             "scons: *** It seems that the compiler was uninstalled and that the registry\n" \
219                             "scons: *** was not cleaned up properly.\n" % subkey
220                     else:
221                         print "scons: *** Ignoring "+str(value)
222
223                 i = i + 1
224         except EnvironmentError:
225             # no more subkeys
226             pass
227     elif is_linux:
228         for d in glob.glob('/opt/intel_cc_*'):
229             # Typical dir here is /opt/intel_cc_80.
230             m = re.search(r'cc_(.*)$', d)
231             if m:
232                 versions.append(m.group(1))
233         for d in glob.glob('/opt/intel/cc*/*'):
234             # Typical dir here is /opt/intel/cc/9.0 for IA32,
235             # /opt/intel/cce/9.0 for EMT64 (AMD64)
236             m = re.search(r'([0-9.]+)$', d)
237             if m:
238                 versions.append(m.group(1))
239     elif is_mac:
240         for d in glob.glob('/opt/intel/cc*/*'):
241             # Typical dir here is /opt/intel/cc/9.0 for IA32,
242             # /opt/intel/cce/9.0 for EMT64 (AMD64)
243             m = re.search(r'([0-9.]+)$', d)
244             if m:
245                 versions.append(m.group(1))
246     return sorted(uniquify(versions))       # remove dups
247
248 def get_intel_compiler_top(version, abi):
249     """
250     Return the main path to the top-level dir of the Intel compiler,
251     using the given version.
252     The compiler will be in <top>/bin/icl.exe (icc on linux),
253     the include dir is <top>/include, etc.
254     """
255
256     if is_windows:
257         if not SCons.Util.can_read_reg:
258             raise NoRegistryModuleError, "No Windows registry module was found"
259         top = get_intel_registry_value('ProductDir', version, abi)
260         # pre-11, icl was in Bin.  11 and later, it's in Bin/<abi> apparently.
261         if not os.path.exists(os.path.join(top, "Bin", "icl.exe")) \
262               and not os.path.exists(os.path.join(top, "Bin", abi, "icl.exe")):
263             raise MissingDirError, \
264                   "Can't find Intel compiler in %s"%(top)
265     elif is_mac or is_linux:
266         # first dir is new (>=9.0) style, second is old (8.0) style.
267         dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s')
268         if abi == 'x86_64':
269             dirs=('/opt/intel/cce/%s',)  # 'e' stands for 'em64t', aka x86_64 aka amd64
270         top=None
271         for d in dirs:
272             if os.path.exists(os.path.join(d%version, "bin", "icc")):
273                 top = d%version
274                 break
275         if not top:
276             raise MissingDirError, \
277                   "Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi)
278     return top
279
280
281 def generate(env, version=None, abi=None, topdir=None, verbose=0):
282     """Add Builders and construction variables for Intel C/C++ compiler
283     to an Environment.
284     args:
285       version: (string) compiler version to use, like "80"
286       abi:     (string) 'win32' or whatever Itanium version wants
287       topdir:  (string) compiler top dir, like
288                          "c:\Program Files\Intel\Compiler70"
289                         If topdir is used, version and abi are ignored.
290       verbose: (int)    if >0, prints compiler version used.
291     """
292     if not (is_mac or is_linux or is_windows):
293         # can't handle this platform
294         return
295
296     if is_windows:
297         SCons.Tool.msvc.generate(env)
298     elif is_linux:
299         SCons.Tool.gcc.generate(env)
300     elif is_mac:
301         SCons.Tool.gcc.generate(env)
302
303     # if version is unspecified, use latest
304     vlist = get_all_compiler_versions()
305     if not version:
306         if vlist:
307             version = vlist[0]
308     else:
309         # User may have specified '90' but we need to get actual dirname '9.0'.
310         # get_version_from_list does that mapping.
311         v = get_version_from_list(version, vlist)
312         if not v:
313             raise SCons.Errors.UserError, \
314                   "Invalid Intel compiler version %s: "%version + \
315                   "installed versions are %s"%(', '.join(vlist))
316         version = v
317
318     # if abi is unspecified, use ia32
319     # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here)
320     abi = check_abi(abi)
321     if abi is None:
322         if is_mac or is_linux:
323             # Check if we are on 64-bit linux, default to 64 then.
324             uname_m = os.uname()[4]
325             if uname_m == 'x86_64':
326                 abi = 'x86_64'
327             else:
328                 abi = 'ia32'
329         else:
330             if is_win64:
331                 abi = 'em64t'
332             else:
333                 abi = 'ia32'
334
335     if version and not topdir:
336         try:
337             topdir = get_intel_compiler_top(version, abi)
338         except (SCons.Util.RegError, IntelCError):
339             topdir = None
340
341     if not topdir:
342         # Normally this is an error, but it might not be if the compiler is
343         # on $PATH and the user is importing their env.
344         class ICLTopDirWarning(SCons.Warnings.Warning):
345             pass
346         if (is_mac or is_linux) and not env.Detect('icc') or \
347            is_windows and not env.Detect('icl'):
348
349             SCons.Warnings.enableWarningClass(ICLTopDirWarning)
350             SCons.Warnings.warn(ICLTopDirWarning,
351                                 "Failed to find Intel compiler for version='%s', abi='%s'"%
352                                 (str(version), str(abi)))
353         else:
354             # should be cleaned up to say what this other version is
355             # since in this case we have some other Intel compiler installed
356             SCons.Warnings.enableWarningClass(ICLTopDirWarning)
357             SCons.Warnings.warn(ICLTopDirWarning,
358                                 "Can't find Intel compiler top dir for version='%s', abi='%s'"%
359                                     (str(version), str(abi)))
360
361     if topdir:
362         if verbose:
363             print "Intel C compiler: using version %s (%g), abi %s, in '%s'"%\
364                   (repr(version), linux_ver_normalize(version),abi,topdir)
365             if is_linux:
366                 # Show the actual compiler version by running the compiler.
367                 os.system('%s/bin/icc --version'%topdir)
368             if is_mac:
369                 # Show the actual compiler version by running the compiler.
370                 os.system('%s/bin/icc --version'%topdir)
371
372         env['INTEL_C_COMPILER_TOP'] = topdir
373         if is_linux:
374             paths={'INCLUDE'         : 'include',
375                    'LIB'             : 'lib',
376                    'PATH'            : 'bin',
377                    'LD_LIBRARY_PATH' : 'lib'}
378             for p in paths.keys():
379                 env.PrependENVPath(p, os.path.join(topdir, paths[p]))
380         if is_mac:
381             paths={'INCLUDE'         : 'include',
382                    'LIB'             : 'lib',
383                    'PATH'            : 'bin',
384                    'LD_LIBRARY_PATH' : 'lib'}
385             for p in paths.keys():
386                 env.PrependENVPath(p, os.path.join(topdir, paths[p]))
387         if is_windows:
388             #       env key    reg valname   default subdir of top
389             paths=(('INCLUDE', 'IncludeDir', 'Include'),
390                    ('LIB'    , 'LibDir',     'Lib'),
391                    ('PATH'   , 'BinDir',     'Bin'))
392             # We are supposed to ignore version if topdir is set, so set
393             # it to the emptry string if it's not already set.
394             if version is None:
395                 version = ''
396             # Each path has a registry entry, use that or default to subdir
397             for p in paths:
398                 try:
399                     path=get_intel_registry_value(p[1], version, abi)
400                     # These paths may have $(ICInstallDir)
401                     # which needs to be substituted with the topdir.
402                     path=path.replace('$(ICInstallDir)', topdir + os.sep)
403                 except IntelCError:
404                     # Couldn't get it from registry: use default subdir of topdir
405                     env.PrependENVPath(p[0], os.path.join(topdir, p[2]))
406                 else:
407                     env.PrependENVPath(p[0], path.split(os.pathsep))
408                     # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]]))
409
410     if is_windows:
411         env['CC']        = 'icl'
412         env['CXX']       = 'icl'
413         env['LINK']      = 'xilink'
414     else:
415         env['CC']        = 'icc'
416         env['CXX']       = 'icpc'
417         # Don't reset LINK here;
418         # use smart_link which should already be here from link.py.
419         #env['LINK']      = '$CC'
420         env['AR']        = 'xiar'
421         env['LD']        = 'xild' # not used by default
422
423     # This is not the exact (detailed) compiler version,
424     # just the major version as determined above or specified
425     # by the user.  It is a float like 80 or 90, in normalized form for Linux
426     # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0)
427     if version:
428         env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version)
429
430     if is_windows:
431         # Look for license file dir
432         # in system environment, registry, and default location.
433         envlicdir = os.environ.get("INTEL_LICENSE_FILE", '')
434         K = ('SOFTWARE\Intel\Licenses')
435         try:
436             k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K)
437             reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0]
438         except (AttributeError, SCons.Util.RegError):
439             reglicdir = ""
440         defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses'
441
442         licdir = None
443         for ld in [envlicdir, reglicdir]:
444             # If the string contains an '@', then assume it's a network
445             # license (port@system) and good by definition.
446             if ld and (ld.find('@') != -1 or os.path.exists(ld)):
447                 licdir = ld
448                 break
449         if not licdir:
450             licdir = defaultlicdir
451             if not os.path.exists(licdir):
452                 class ICLLicenseDirWarning(SCons.Warnings.Warning):
453                     pass
454                 SCons.Warnings.enableWarningClass(ICLLicenseDirWarning)
455                 SCons.Warnings.warn(ICLLicenseDirWarning,
456                                     "Intel license dir was not found."
457                                     "  Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)."
458                                     "  Using the default path as a last resort."
459                                         % (envlicdir, reglicdir, defaultlicdir))
460         env['ENV']['INTEL_LICENSE_FILE'] = licdir
461
462 def exists(env):
463     if not (is_mac or is_linux or is_windows):
464         # can't handle this platform
465         return 0
466
467     try:
468         versions = get_all_compiler_versions()
469     except (SCons.Util.RegError, IntelCError):
470         versions = None
471     detected = versions is not None and len(versions) > 0
472     if not detected:
473         # try env.Detect, maybe that will work
474         if is_windows:
475             return env.Detect('icl')
476         elif is_linux:
477             return env.Detect('icc')
478         elif is_mac:
479             return env.Detect('icc')
480     return detected
481
482 # end of file
483
484 # Local Variables:
485 # tab-width:4
486 # indent-tabs-mode:nil
487 # End:
488 # vim: set expandtab tabstop=4 shiftwidth=4: