2a97d19a7ebca93a9529575594470f29db677c29
[martins/samba-autobuild/.git] / third_party / waf / wafadmin / Tools / msvc.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Carlos Rafael Giani, 2006 (dv)
4 # Tamas Pal, 2007 (folti)
5 # Nicolas Mercier, 2009
6 # Microsoft Visual C++/Intel C++ compiler support - beta, needs more testing
7
8 # usage:
9 #
10 # conf.env['MSVC_VERSIONS'] = ['msvc 9.0', 'msvc 8.0', 'wsdk 7.0', 'intel 11', 'PocketPC 9.0', 'Smartphone 8.0']
11 # conf.env['MSVC_TARGETS'] = ['x64']
12 # conf.check_tool('msvc')
13 # OR conf.check_tool('msvc', funs='no_autodetect')
14 # conf.check_lib_msvc('gdi32')
15 # conf.check_libs_msvc('kernel32 user32', mandatory=true)
16 # ...
17 # obj.uselib = 'KERNEL32 USER32 GDI32'
18 #
19 # platforms and targets will be tested in the order they appear;
20 # the first good configuration will be used
21 # supported platforms :
22 # ia64, x64, x86, x86_amd64, x86_ia64
23
24 # compilers supported :
25 #  msvc       => Visual Studio, versions 7.1 (2003), 8,0 (2005), 9.0 (2008)
26 #  wsdk       => Windows SDK, versions 6.0, 6.1, 7.0
27 #  icl        => Intel compiler, versions 9,10,11
28 #  Smartphone => Compiler/SDK for Smartphone devices (armv4/v4i)
29 #  PocketPC   => Compiler/SDK for PocketPC devices (armv4/v4i)
30
31
32 import os, sys, re, string, optparse
33 import Utils, TaskGen, Runner, Configure, Task, Options
34 from Logs import debug, info, warn, error
35 from TaskGen import after, before, feature
36
37 from Configure import conftest, conf
38 import ccroot, cc, cxx, ar, winres
39 from libtool import read_la_file
40
41 try:
42         import _winreg
43 except:
44         import winreg as _winreg
45
46 pproc = Utils.pproc
47
48 # importlibs provided by MSVC/Platform SDK. Do NOT search them....
49 g_msvc_systemlibs = """
50 aclui activeds ad1 adptif adsiid advapi32 asycfilt authz bhsupp bits bufferoverflowu cabinet
51 cap certadm certidl ciuuid clusapi comctl32 comdlg32 comsupp comsuppd comsuppw comsuppwd comsvcs
52 credui crypt32 cryptnet cryptui d3d8thk daouuid dbgeng dbghelp dciman32 ddao35 ddao35d
53 ddao35u ddao35ud delayimp dhcpcsvc dhcpsapi dlcapi dnsapi dsprop dsuiext dtchelp
54 faultrep fcachdll fci fdi framedyd framedyn gdi32 gdiplus glauxglu32 gpedit gpmuuid
55 gtrts32w gtrtst32hlink htmlhelp httpapi icm32 icmui imagehlp imm32 iphlpapi iprop
56 kernel32 ksguid ksproxy ksuser libcmt libcmtd libcpmt libcpmtd loadperf lz32 mapi
57 mapi32 mgmtapi minidump mmc mobsync mpr mprapi mqoa mqrt msacm32 mscms mscoree
58 msdasc msimg32 msrating mstask msvcmrt msvcurt msvcurtd mswsock msxml2 mtx mtxdm
59 netapi32 nmapinmsupp npptools ntdsapi ntdsbcli ntmsapi ntquery odbc32 odbcbcp
60 odbccp32 oldnames ole32 oleacc oleaut32 oledb oledlgolepro32 opends60 opengl32
61 osptk parser pdh penter pgobootrun pgort powrprof psapi ptrustm ptrustmd ptrustu
62 ptrustud qosname rasapi32 rasdlg rassapi resutils riched20 rpcndr rpcns4 rpcrt4 rtm
63 rtutils runtmchk scarddlg scrnsave scrnsavw secur32 sensapi setupapi sfc shell32
64 shfolder shlwapi sisbkup snmpapi sporder srclient sti strsafe svcguid tapi32 thunk32
65 traffic unicows url urlmon user32 userenv usp10 uuid uxtheme vcomp vcompd vdmdbg
66 version vfw32 wbemuuid  webpost wiaguid wininet winmm winscard winspool winstrm
67 wintrust wldap32 wmiutils wow32 ws2_32 wsnmp32 wsock32 wst wtsapi32 xaswitch xolehlp
68 """.split()
69
70
71 all_msvc_platforms = [ ('x64', 'amd64'), ('x86', 'x86'), ('ia64', 'ia64'), ('x86_amd64', 'amd64'), ('x86_ia64', 'ia64') ]
72 all_wince_platforms = [ ('armv4', 'arm'), ('armv4i', 'arm'), ('mipsii', 'mips'), ('mipsii_fp', 'mips'), ('mipsiv', 'mips'), ('mipsiv_fp', 'mips'), ('sh4', 'sh'), ('x86', 'cex86') ]
73 all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
74
75 def setup_msvc(conf, versions):
76         platforms = Utils.to_list(conf.env['MSVC_TARGETS']) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms]
77         desired_versions = conf.env['MSVC_VERSIONS'] or [v for v,_ in versions][::-1]
78         versiondict = dict(versions)
79
80         for version in desired_versions:
81                 try:
82                         targets = dict(versiondict [version])
83                         for target in platforms:
84                                 try:
85                                         arch,(p1,p2,p3) = targets[target]
86                                         compiler,revision = version.split()
87                                         return compiler,revision,p1,p2,p3
88                                 except KeyError: continue
89                 except KeyError: continue
90         conf.fatal('msvc: Impossible to find a valid architecture for building (in setup_msvc)')
91
92 @conf
93 def get_msvc_version(conf, compiler, version, target, vcvars):
94         debug('msvc: get_msvc_version: %r %r %r', compiler, version, target)
95         batfile = os.path.join(conf.blddir, 'waf-print-msvc.bat')
96         f = open(batfile, 'w')
97         f.write("""@echo off
98 set INCLUDE=
99 set LIB=
100 call "%s" %s
101 echo PATH=%%PATH%%
102 echo INCLUDE=%%INCLUDE%%
103 echo LIB=%%LIB%%
104 """ % (vcvars,target))
105         f.close()
106         sout = Utils.cmd_output(['cmd', '/E:on', '/V:on', '/C', batfile])
107         lines = sout.splitlines()
108
109         for x in ('Setting environment', 'Setting SDK environment', 'Intel(R) C++ Compiler'):
110                 if lines[0].find(x) != -1:
111                         break
112         else:
113                 debug('msvc: get_msvc_version: %r %r %r -> not found', compiler, version, target)
114                 conf.fatal('msvc: Impossible to find a valid architecture for building (in get_msvc_version)')
115
116         for line in lines[1:]:
117                 if line.startswith('PATH='):
118                         path = line[5:]
119                         MSVC_PATH = path.split(';')
120                 elif line.startswith('INCLUDE='):
121                         MSVC_INCDIR = [i for i in line[8:].split(';') if i]
122                 elif line.startswith('LIB='):
123                         MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
124
125         # Check if the compiler is usable at all.
126         # The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
127         env = {}
128         env.update(os.environ)
129         env.update(PATH = path)
130         compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
131         cxx = conf.find_program(compiler_name, path_list=MSVC_PATH)
132         # delete CL if exists. because it could contain parameters wich can change cl's behaviour rather catastrophically.
133         if env.has_key('CL'):
134                 del(env['CL'])
135
136         try:
137                 p = pproc.Popen([cxx, '/help'], env=env, stdout=pproc.PIPE, stderr=pproc.PIPE)
138                 out, err = p.communicate()
139                 if p.returncode != 0:
140                         raise Exception('return code: %r: %r' % (p.returncode, err))
141         except Exception, e:
142                 debug('msvc: get_msvc_version: %r %r %r -> failure', compiler, version, target)
143                 debug(str(e))
144                 conf.fatal('msvc: cannot run the compiler (in get_msvc_version)')
145         else:
146                 debug('msvc: get_msvc_version: %r %r %r -> OK', compiler, version, target)
147
148         return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
149
150 @conf
151 def gather_wsdk_versions(conf, versions):
152         version_pattern = re.compile('^v..?.?\...?.?')
153         try:
154                 all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows')
155         except WindowsError:
156                 try:
157                         all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows')
158                 except WindowsError:
159                         return
160         index = 0
161         while 1:
162                 try:
163                         version = _winreg.EnumKey(all_versions, index)
164                 except WindowsError:
165                         break
166                 index = index + 1
167                 if not version_pattern.match(version):
168                         continue
169                 try:
170                         msvc_version = _winreg.OpenKey(all_versions, version)
171                         path,type = _winreg.QueryValueEx(msvc_version,'InstallationFolder')
172                 except WindowsError:
173                         continue
174                 if os.path.isfile(os.path.join(path, 'bin', 'SetEnv.cmd')):
175                         targets = []
176                         for target,arch in all_msvc_platforms:
177                                 try:
178                                         targets.append((target, (arch, conf.get_msvc_version('wsdk', version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd')))))
179                                 except Configure.ConfigurationError:
180                                         pass
181                         versions.append(('wsdk ' + version[1:], targets))
182
183 @conf
184 def gather_msvc_versions(conf, versions):
185         # checks SmartPhones SDKs
186         try:
187                 ce_sdk = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Windows CE Tools\\SDKs')
188         except WindowsError:
189                 try:
190                         ce_sdk = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows CE Tools\\SDKs')
191                 except WindowsError:
192                         ce_sdk = ''
193         if ce_sdk:
194                 supported_wince_platforms = []
195                 ce_index = 0
196                 while 1:
197                         try:
198                                 sdk_device = _winreg.EnumKey(ce_sdk, ce_index)
199                         except WindowsError:
200                                 break
201                         ce_index = ce_index + 1
202                         sdk = _winreg.OpenKey(ce_sdk, sdk_device)
203                         path,type = _winreg.QueryValueEx(sdk, 'SDKRootDir')
204                         path=str(path)
205                         path,device = os.path.split(path)
206                         if not device:
207                                 path,device = os.path.split(path)
208                         for arch,compiler in all_wince_platforms:
209                                 platforms = []
210                                 if os.path.isdir(os.path.join(path, device, 'Lib', arch)):
211                                         platforms.append((arch, compiler, os.path.join(path, device, 'Include', arch), os.path.join(path, device, 'Lib', arch)))
212                                 if platforms:
213                                         supported_wince_platforms.append((device, platforms))
214         # checks MSVC
215         version_pattern = re.compile('^..?\...?')
216         for vcver,vcvar in [('VCExpress','exp'), ('VisualStudio','')]:
217                 try:
218                         all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\'+vcver)
219                 except WindowsError:
220                         try:
221                                 all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\'+vcver)
222                         except WindowsError:
223                                 continue
224                 index = 0
225                 while 1:
226                         try:
227                                 version = _winreg.EnumKey(all_versions, index)
228                         except WindowsError:
229                                 break
230                         index = index + 1
231                         if not version_pattern.match(version):
232                                 continue
233                         try:
234                                 msvc_version = _winreg.OpenKey(all_versions, version + "\\Setup\\VS")
235                                 path,type = _winreg.QueryValueEx(msvc_version, 'ProductDir')
236                                 path=str(path)
237                                 targets = []
238                                 if ce_sdk:
239                                         for device,platforms in supported_wince_platforms:
240                                                 cetargets = []
241                                                 for platform,compiler,include,lib in platforms:
242                                                         winCEpath = os.path.join(path, 'VC', 'ce')
243                                                         if os.path.isdir(winCEpath):
244                                                                 common_bindirs,_1,_2 = conf.get_msvc_version('msvc', version, 'x86', os.path.join(path, 'Common7', 'Tools', 'vsvars32.bat'))
245                                                                 if os.path.isdir(os.path.join(winCEpath, 'lib', platform)):
246                                                                         bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)] + common_bindirs
247                                                                         incdirs = [include, os.path.join(winCEpath, 'include'), os.path.join(winCEpath, 'atlmfc', 'include')]
248                                                                         libdirs = [lib, os.path.join(winCEpath, 'lib', platform), os.path.join(winCEpath, 'atlmfc', 'lib', platform)]
249                                                                         cetargets.append((platform, (platform, (bindirs,incdirs,libdirs))))
250                                                 versions.append((device+' '+version, cetargets))
251                                 if os.path.isfile(os.path.join(path, 'VC', 'vcvarsall.bat')):
252                                         for target,realtarget in all_msvc_platforms[::-1]:
253                                                 try:
254                                                         targets.append((target, (realtarget, conf.get_msvc_version('msvc', version, target, os.path.join(path, 'VC', 'vcvarsall.bat')))))
255                                                 except:
256                                                         pass
257                                 elif os.path.isfile(os.path.join(path, 'Common7', 'Tools', 'vsvars32.bat')):
258                                         try:
259                                                 targets.append(('x86', ('x86', conf.get_msvc_version('msvc', version, 'x86', os.path.join(path, 'Common7', 'Tools', 'vsvars32.bat')))))
260                                         except Configure.ConfigurationError:
261                                                 pass
262                                 versions.append(('msvc '+version, targets))
263
264                         except WindowsError:
265                                 continue
266
267 @conf
268 def gather_icl_versions(conf, versions):
269         version_pattern = re.compile('^...?.?\....?.?')
270         try:
271                 all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\C++')
272         except WindowsError:
273                 try:
274                         all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\C++')
275                 except WindowsError:
276                         return
277         index = 0
278         while 1:
279                 try:
280                         version = _winreg.EnumKey(all_versions, index)
281                 except WindowsError:
282                         break
283                 index = index + 1
284                 if not version_pattern.match(version):
285                         continue
286                 targets = []
287                 for target,arch in all_icl_platforms:
288                         try:
289                                 icl_version = _winreg.OpenKey(all_versions, version+'\\'+target)
290                                 path,type = _winreg.QueryValueEx(icl_version,'ProductDir')
291                                 if os.path.isfile(os.path.join(path, 'bin', 'iclvars.bat')):
292                                         try:
293                                                 targets.append((target, (arch, conf.get_msvc_version('intel', version, target, os.path.join(path, 'bin', 'iclvars.bat')))))
294                                         except Configure.ConfigurationError:
295                                                 pass
296                         except WindowsError:
297                                 continue
298                 major = version[0:2]
299                 versions.append(('intel ' + major, targets))
300
301 @conf
302 def get_msvc_versions(conf):
303         if not conf.env.MSVC_INSTALLED_VERSIONS:
304                 lst = []
305                 conf.gather_msvc_versions(lst)
306                 conf.gather_wsdk_versions(lst)
307                 conf.gather_icl_versions(lst)
308                 conf.env.MSVC_INSTALLED_VERSIONS = lst
309         return conf.env.MSVC_INSTALLED_VERSIONS
310
311 @conf
312 def print_all_msvc_detected(conf):
313         for version,targets in conf.env['MSVC_INSTALLED_VERSIONS']:
314                 info(version)
315                 for target,l in targets:
316                         info("\t"+target)
317
318 def detect_msvc(conf):
319         versions = get_msvc_versions(conf)
320         return setup_msvc(conf, versions)
321
322 @conf
323 def find_lt_names_msvc(self, libname, is_static=False):
324         """
325         Win32/MSVC specific code to glean out information from libtool la files.
326         this function is not attached to the task_gen class
327         """
328         lt_names=[
329                 'lib%s.la' % libname,
330                 '%s.la' % libname,
331         ]
332
333         for path in self.env['LIBPATH']:
334                 for la in lt_names:
335                         laf=os.path.join(path,la)
336                         dll=None
337                         if os.path.exists(laf):
338                                 ltdict=read_la_file(laf)
339                                 lt_libdir=None
340                                 if ltdict.get('libdir', ''):
341                                         lt_libdir = ltdict['libdir']
342                                 if not is_static and ltdict.get('library_names', ''):
343                                         dllnames=ltdict['library_names'].split()
344                                         dll=dllnames[0].lower()
345                                         dll=re.sub('\.dll$', '', dll)
346                                         return (lt_libdir, dll, False)
347                                 elif ltdict.get('old_library', ''):
348                                         olib=ltdict['old_library']
349                                         if os.path.exists(os.path.join(path,olib)):
350                                                 return (path, olib, True)
351                                         elif lt_libdir != '' and os.path.exists(os.path.join(lt_libdir,olib)):
352                                                 return (lt_libdir, olib, True)
353                                         else:
354                                                 return (None, olib, True)
355                                 else:
356                                         raise Utils.WafError('invalid libtool object file: %s' % laf)
357         return (None, None, None)
358
359 @conf
360 def libname_msvc(self, libname, is_static=False, mandatory=False):
361         lib = libname.lower()
362         lib = re.sub('\.lib$','',lib)
363
364         if lib in g_msvc_systemlibs:
365                 return lib
366
367         lib=re.sub('^lib','',lib)
368
369         if lib == 'm':
370                 return None
371
372         (lt_path, lt_libname, lt_static) = self.find_lt_names_msvc(lib, is_static)
373
374         if lt_path != None and lt_libname != None:
375                 if lt_static == True:
376                         # file existance check has been made by find_lt_names
377                         return os.path.join(lt_path,lt_libname)
378
379         if lt_path != None:
380                 _libpaths=[lt_path] + self.env['LIBPATH']
381         else:
382                 _libpaths=self.env['LIBPATH']
383
384         static_libs=[
385                 'lib%ss.lib' % lib,
386                 'lib%s.lib' % lib,
387                 '%ss.lib' % lib,
388                 '%s.lib' %lib,
389                 ]
390
391         dynamic_libs=[
392                 'lib%s.dll.lib' % lib,
393                 'lib%s.dll.a' % lib,
394                 '%s.dll.lib' % lib,
395                 '%s.dll.a' % lib,
396                 'lib%s_d.lib' % lib,
397                 '%s_d.lib' % lib,
398                 '%s.lib' %lib,
399                 ]
400
401         libnames=static_libs
402         if not is_static:
403                 libnames=dynamic_libs + static_libs
404
405         for path in _libpaths:
406                 for libn in libnames:
407                         if os.path.exists(os.path.join(path, libn)):
408                                 debug('msvc: lib found: %s', os.path.join(path,libn))
409                                 return re.sub('\.lib$', '',libn)
410
411         #if no lib can be found, just return the libname as msvc expects it
412         if mandatory:
413                 self.fatal("The library %r could not be found" % libname)
414         return re.sub('\.lib$', '', libname)
415
416 @conf
417 def check_lib_msvc(self, libname, is_static=False, uselib_store=None, mandatory=False):
418         "This is the api to use"
419         libn = self.libname_msvc(libname, is_static, mandatory)
420
421         if not uselib_store:
422                 uselib_store = libname.upper()
423
424         # Note: ideally we should be able to place the lib in the right env var, either STATICLIB or LIB,
425         # but we don't distinguish static libs from shared libs.
426         # This is ok since msvc doesn't have any special linker flag to select static libs (no env['STATICLIB_MARKER'])
427         if False and is_static: # disabled
428                 self.env['STATICLIB_' + uselib_store] = [libn]
429         else:
430                 self.env['LIB_' + uselib_store] = [libn]
431
432 @conf
433 def check_libs_msvc(self, libnames, is_static=False, mandatory=False):
434         for libname in Utils.to_list(libnames):
435                 self.check_lib_msvc(libname, is_static, mandatory=mandatory)
436
437 @conftest
438 def no_autodetect(conf):
439         conf.eval_rules(detect.replace('autodetect', ''))
440
441
442 detect = '''
443 autodetect
444 find_msvc
445 msvc_common_flags
446 cc_load_tools
447 cxx_load_tools
448 cc_add_flags
449 cxx_add_flags
450 link_add_flags
451 '''
452
453 @conftest
454 def autodetect(conf):
455         v = conf.env
456         compiler, version, path, includes, libdirs = detect_msvc(conf)
457         v['PATH'] = path
458         v['CPPPATH'] = includes
459         v['LIBPATH'] = libdirs
460         v['MSVC_COMPILER'] = compiler
461
462 def _get_prog_names(conf, compiler):
463         if compiler=='intel':
464                 compiler_name = 'ICL'
465                 linker_name = 'XILINK'
466                 lib_name = 'XILIB'
467         else:
468                 # assumes CL.exe
469                 compiler_name = 'CL'
470                 linker_name = 'LINK'
471                 lib_name = 'LIB'
472         return compiler_name, linker_name, lib_name
473
474 @conftest
475 def find_msvc(conf):
476         # due to path format limitations, limit operation only to native Win32. Yeah it sucks.
477         if sys.platform != 'win32':
478                 conf.fatal('MSVC module only works under native Win32 Python! cygwin is not supported yet')
479
480         v = conf.env
481
482         compiler, version, path, includes, libdirs = detect_msvc(conf)
483
484         compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
485         has_msvc_manifest = (compiler == 'msvc' and float(version) >= 8) or (compiler == 'wsdk' and float(version) >= 6)        or (compiler == 'intel' and float(version) >= 11)
486
487         # compiler
488         cxx = None
489         if v.CXX: cxx = v.CXX
490         elif 'CXX' in conf.environ: cxx = conf.environ['CXX']
491         if not cxx: cxx = conf.find_program(compiler_name, var='CXX', path_list=path, mandatory=True)
492         cxx = conf.cmd_to_list(cxx)
493
494         # before setting anything, check if the compiler is really msvc
495         env = dict(conf.environ)
496         env.update(PATH = ';'.join(path))
497         if not Utils.cmd_output([cxx, '/nologo', '/?'], silent=True, env=env):
498                 conf.fatal('the msvc compiler could not be identified')
499
500         link = v.LINK_CXX
501         if not link:
502                 link = conf.find_program(linker_name, path_list=path, mandatory=True)
503         ar = v.AR
504         if not ar:
505                 ar = conf.find_program(lib_name, path_list=path, mandatory=True)
506
507         # manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
508         mt = v.MT
509         if has_msvc_manifest:
510                 mt = conf.find_program('MT', path_list=path, mandatory=True)
511
512         # no more possibility of failure means the data state will be consistent
513         # we may store the data safely now
514
515         v.MSVC_MANIFEST = has_msvc_manifest
516         v.PATH = path
517         v.CPPPATH = includes
518         v.LIBPATH = libdirs
519
520         # c/c++ compiler
521         v.CC = v.CXX = cxx
522         v.CC_NAME = v.CXX_NAME = 'msvc'
523
524         v.LINK = v.LINK_CXX = link
525         if not v.LINK_CC:
526                 v.LINK_CC = v.LINK_CXX
527
528         v.AR = ar
529         v.MT = mt
530         v.MTFLAGS = v.ARFLAGS = ['/NOLOGO']
531
532
533         conf.check_tool('winres')
534
535         if not conf.env.WINRC:
536                 warn('Resource compiler not found. Compiling resource file is disabled')
537
538         # environment flags
539         try: v.prepend_value('CPPPATH', conf.environ['INCLUDE'])
540         except KeyError: pass
541         try: v.prepend_value('LIBPATH', conf.environ['LIB'])
542         except KeyError: pass
543
544 @conftest
545 def msvc_common_flags(conf):
546         v = conf.env
547
548         v['CPPFLAGS']     = ['/W3', '/nologo']
549
550         v['CCDEFINES_ST']     = '/D%s'
551         v['CXXDEFINES_ST']    = '/D%s'
552
553         # TODO just use _WIN32, which defined by the compiler itself!
554         v['CCDEFINES']    = ['WIN32'] # avoid using this, any compiler predefines the _WIN32 marcro anyway
555         v['CXXDEFINES']   = ['WIN32'] # avoid using this, any compiler predefines the _WIN32 marcro anyway
556
557         v['_CCINCFLAGS']  = []
558         v['_CCDEFFLAGS']  = []
559         v['_CXXINCFLAGS'] = []
560         v['_CXXDEFFLAGS'] = []
561
562         v['CC_SRC_F']     = ''
563         v['CC_TGT_F']     = ['/c', '/Fo']
564         v['CXX_SRC_F']    = ''
565         v['CXX_TGT_F']    = ['/c', '/Fo']
566
567         v['CPPPATH_ST']   = '/I%s' # template for adding include paths
568
569         v['AR_TGT_F'] = v['CCLNK_TGT_F'] = v['CXXLNK_TGT_F'] = '/OUT:'
570
571         # Subsystem specific flags
572         v['CPPFLAGS_CONSOLE']   = ['/SUBSYSTEM:CONSOLE']
573         v['CPPFLAGS_NATIVE']    = ['/SUBSYSTEM:NATIVE']
574         v['CPPFLAGS_POSIX']     = ['/SUBSYSTEM:POSIX']
575         v['CPPFLAGS_WINDOWS']   = ['/SUBSYSTEM:WINDOWS']
576         v['CPPFLAGS_WINDOWSCE'] = ['/SUBSYSTEM:WINDOWSCE']
577
578         # CRT specific flags
579         v['CPPFLAGS_CRT_MULTITHREADED'] = ['/MT']
580         v['CPPFLAGS_CRT_MULTITHREADED_DLL'] = ['/MD']
581
582         # TODO these are defined by the compiler itself!
583         v['CPPDEFINES_CRT_MULTITHREADED'] = ['_MT'] # this is defined by the compiler itself!
584         v['CPPDEFINES_CRT_MULTITHREADED_DLL'] = ['_MT', '_DLL'] # these are defined by the compiler itself!
585
586         v['CPPFLAGS_CRT_MULTITHREADED_DBG'] = ['/MTd']
587         v['CPPFLAGS_CRT_MULTITHREADED_DLL_DBG'] = ['/MDd']
588
589         # TODO these are defined by the compiler itself!
590         v['CPPDEFINES_CRT_MULTITHREADED_DBG'] = ['_DEBUG', '_MT'] # these are defined by the compiler itself!
591         v['CPPDEFINES_CRT_MULTITHREADED_DLL_DBG'] = ['_DEBUG', '_MT', '_DLL'] # these are defined by the compiler itself!
592
593         # compiler debug levels
594         v['CCFLAGS']            = ['/TC']
595         v['CCFLAGS_OPTIMIZED']  = ['/O2', '/DNDEBUG']
596         v['CCFLAGS_RELEASE']    = ['/O2', '/DNDEBUG']
597         v['CCFLAGS_DEBUG']      = ['/Od', '/RTC1', '/ZI']
598         v['CCFLAGS_ULTRADEBUG'] = ['/Od', '/RTC1', '/ZI']
599
600         v['CXXFLAGS']            = ['/TP', '/EHsc']
601         v['CXXFLAGS_OPTIMIZED']  = ['/O2', '/DNDEBUG']
602         v['CXXFLAGS_RELEASE']    = ['/O2', '/DNDEBUG']
603
604         v['CXXFLAGS_DEBUG']      = ['/Od', '/RTC1', '/ZI']
605         v['CXXFLAGS_ULTRADEBUG'] = ['/Od', '/RTC1', '/ZI']
606
607         # linker
608         v['LIB']              = []
609
610         v['LIB_ST']           = '%s.lib' # template for adding libs
611         v['LIBPATH_ST']       = '/LIBPATH:%s' # template for adding libpaths
612         v['STATICLIB_ST']     = 'lib%s.lib' # Note: to be able to distinguish between a static lib and a dll import lib, it's a good pratice to name the static lib 'lib%s.lib' and the dll import lib '%s.lib'
613         v['STATICLIBPATH_ST'] = '/LIBPATH:%s'
614
615         v['LINKFLAGS'] = ['/NOLOGO']
616         if v['MSVC_MANIFEST']:
617                 v.append_value('LINKFLAGS', '/MANIFEST')
618         v['LINKFLAGS_DEBUG']      = ['/DEBUG']
619         v['LINKFLAGS_ULTRADEBUG'] = ['/DEBUG']
620
621         # shared library
622         v['shlib_CCFLAGS']  = ['']
623         v['shlib_CXXFLAGS'] = ['']
624         v['shlib_LINKFLAGS']= ['/DLL']
625         v['shlib_PATTERN']  = '%s.dll'
626         v['implib_PATTERN'] = '%s.lib'
627         v['IMPLIB_ST']      = '/IMPLIB:%s'
628
629         # static library
630         v['staticlib_LINKFLAGS'] = ['']
631         v['staticlib_PATTERN']   = 'lib%s.lib' # Note: to be able to distinguish between a static lib and a dll import lib, it's a good pratice to name the static lib 'lib%s.lib' and the dll import lib '%s.lib'
632
633         # program
634         v['program_PATTERN']     = '%s.exe'
635
636
637 #######################################################################################################
638 ##### conf above, build below
639
640 @after('apply_link')
641 @feature('cc', 'cxx')
642 def apply_flags_msvc(self):
643         if self.env.CC_NAME != 'msvc' or not self.link_task:
644                 return
645
646         subsystem = getattr(self, 'subsystem', '')
647         if subsystem:
648                 subsystem = '/subsystem:%s' % subsystem
649                 flags = 'cstaticlib' in self.features and 'ARFLAGS' or 'LINKFLAGS'
650                 self.env.append_value(flags, subsystem)
651
652         if getattr(self, 'link_task', None) and not 'cstaticlib' in self.features:
653                 for f in self.env.LINKFLAGS:
654                         d = f.lower()
655                         if d[1:] == 'debug':
656                                 pdbnode = self.link_task.outputs[0].change_ext('.pdb')
657                                 pdbfile = pdbnode.bldpath(self.env)
658                                 self.link_task.outputs.append(pdbnode)
659                                 self.bld.install_files(self.install_path, [pdbnode], env=self.env)
660                                 break
661
662 @feature('cprogram', 'cshlib', 'cstaticlib')
663 @after('apply_lib_vars')
664 @before('apply_obj_vars')
665 def apply_obj_vars_msvc(self):
666         if self.env['CC_NAME'] != 'msvc':
667                 return
668
669         try:
670                 self.meths.remove('apply_obj_vars')
671         except ValueError:
672                 pass
673
674         libpaths = getattr(self, 'libpaths', [])
675         if not libpaths: self.libpaths = libpaths
676
677         env = self.env
678         app = env.append_unique
679
680         cpppath_st       = env['CPPPATH_ST']
681         lib_st           = env['LIB_ST']
682         staticlib_st     = env['STATICLIB_ST']
683         libpath_st       = env['LIBPATH_ST']
684         staticlibpath_st = env['STATICLIBPATH_ST']
685
686         for i in env['LIBPATH']:
687                 app('LINKFLAGS', libpath_st % i)
688                 if not libpaths.count(i):
689                         libpaths.append(i)
690
691         for i in env['LIBPATH']:
692                 app('LINKFLAGS', staticlibpath_st % i)
693                 if not libpaths.count(i):
694                         libpaths.append(i)
695
696         # i doubt that anyone will make a fully static binary anyway
697         if not env['FULLSTATIC']:
698                 if env['STATICLIB'] or env['LIB']:
699                         app('LINKFLAGS', env['SHLIB_MARKER']) # TODO does SHLIB_MARKER work?
700
701         for i in env['STATICLIB']:
702                 app('LINKFLAGS', staticlib_st % i)
703
704         for i in env['LIB']:
705                 app('LINKFLAGS', lib_st % i)
706
707 # split the manifest file processing from the link task, like for the rc processing
708
709 @feature('cprogram', 'cshlib')
710 @after('apply_link')
711 def apply_manifest(self):
712         """Special linker for MSVC with support for embedding manifests into DLL's
713         and executables compiled by Visual Studio 2005 or probably later. Without
714         the manifest file, the binaries are unusable.
715         See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx"""
716
717         if self.env.CC_NAME == 'msvc' and self.env.MSVC_MANIFEST:
718                 out_node = self.link_task.outputs[0]
719                 man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
720                 self.link_task.outputs.append(man_node)
721                 self.link_task.do_manifest = True
722
723 def exec_mf(self):
724         env = self.env
725         mtool = env['MT']
726         if not mtool:
727                 return 0
728
729         self.do_manifest = False
730
731         outfile = self.outputs[0].bldpath(env)
732
733         manifest = None
734         for out_node in self.outputs:
735                 if out_node.name.endswith('.manifest'):
736                         manifest = out_node.bldpath(env)
737                         break
738         if manifest is None:
739                 # Should never get here.  If we do, it means the manifest file was
740                 # never added to the outputs list, thus we don't have a manifest file
741                 # to embed, so we just return.
742                 return 0
743
744         # embedding mode. Different for EXE's and DLL's.
745         # see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx
746         mode = ''
747         if 'cprogram' in self.generator.features:
748                 mode = '1'
749         elif 'cshlib' in self.generator.features:
750                 mode = '2'
751
752         debug('msvc: embedding manifest')
753         #flags = ' '.join(env['MTFLAGS'] or [])
754
755         lst = []
756         lst.extend([env['MT']])
757         lst.extend(Utils.to_list(env['MTFLAGS']))
758         lst.extend(Utils.to_list("-manifest"))
759         lst.extend(Utils.to_list(manifest))
760         lst.extend(Utils.to_list("-outputresource:%s;%s" % (outfile, mode)))
761
762         #cmd='%s %s -manifest "%s" -outputresource:"%s";#%s' % (mtool, flags,
763         #       manifest, outfile, mode)
764         lst = [lst]
765         return self.exec_command(*lst)
766
767 ########## stupid evil command modification: concatenate the tokens /Fx, /doc, and /x: with the next token
768
769 def exec_command_msvc(self, *k, **kw):
770         "instead of quoting all the paths and keep using the shell, we can just join the options msvc is interested in"
771         if self.env['CC_NAME'] == 'msvc':
772                 if isinstance(k[0], list):
773                         lst = []
774                         carry = ''
775                         for a in k[0]:
776                                 if len(a) == 3 and a.startswith('/F') or a == '/doc' or a[-1] == ':':
777                                         carry = a
778                                 else:
779                                         lst.append(carry + a)
780                                         carry = ''
781                         k = [lst]
782
783                 env = dict(os.environ)
784                 env.update(PATH = ';'.join(self.env['PATH']))
785                 kw['env'] = env
786
787         ret = self.generator.bld.exec_command(*k, **kw)
788         if ret: return ret
789         if getattr(self, 'do_manifest', None):
790                 ret = exec_mf(self)
791         return ret
792
793 for k in 'cc cxx winrc cc_link cxx_link static_link qxx'.split():
794         cls = Task.TaskBase.classes.get(k, None)
795         if cls:
796                 cls.exec_command = exec_command_msvc