third_party: Update waf to verison 2.0.23
[Samba.git] / third_party / waf / waflib / extras / msvs.py
blob03b739f849c034ac52ae5a2405d4ace6e42ec702
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # Avalanche Studios 2009-2011
4 # Thomas Nagy 2011
6 """
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright
15 notice, this list of conditions and the following disclaimer in the
16 documentation and/or other materials provided with the distribution.
18 3. The name of the author may not be used to endorse or promote products
19 derived from this software without specific prior written permission.
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY OF SUCH DAMAGE.
32 """
34 """
35 To add this tool to your project:
36 def options(conf):
37 opt.load('msvs')
39 It can be a good idea to add the sync_exec tool too.
41 To generate solution files:
42 $ waf configure msvs
44 To customize the outputs, provide subclasses in your wscript files::
46 from waflib.extras import msvs
47 class vsnode_target(msvs.vsnode_target):
48 def get_build_command(self, props):
49 # likely to be required
50 return "waf.bat build"
51 def collect_source(self):
52 # likely to be required
53 ...
54 class msvs_bar(msvs.msvs_generator):
55 def init(self):
56 msvs.msvs_generator.init(self)
57 self.vsnode_target = vsnode_target
59 The msvs class re-uses the same build() function for reading the targets (task generators),
60 you may therefore specify msvs settings on the context object::
62 def build(bld):
63 bld.solution_name = 'foo.sln'
64 bld.waf_command = 'waf.bat'
65 bld.projects_dir = bld.srcnode.make_node('.depproj')
66 bld.projects_dir.mkdir()
68 For visual studio 2008, the command is called 'msvs2008', and the classes
69 such as vsnode_target are wrapped by a decorator class 'wrap_2008' to
70 provide special functionality.
72 To customize platform toolsets, pass additional parameters, for example::
74 class msvs_2013(msvs.msvs_generator):
75 cmd = 'msvs2013'
76 numver = '13.00'
77 vsver = '2013'
78 platform_toolset_ver = 'v120'
80 ASSUMPTIONS:
81 * a project can be either a directory or a target, vcxproj files are written only for targets that have source files
82 * each project is a vcxproj file, therefore the project uuid needs only to be a hash of the absolute path
83 """
85 import os, re, sys
86 import uuid # requires python 2.5
87 from waflib.Build import BuildContext
88 from waflib import Utils, TaskGen, Logs, Task, Context, Node, Options
90 HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)'
92 PROJECT_TEMPLATE = r'''<?xml version="1.0" encoding="UTF-8"?>
93 <Project DefaultTargets="Build" ToolsVersion="4.0"
94 xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
96 <ItemGroup Label="ProjectConfigurations">
97 ${for b in project.build_properties}
98 <ProjectConfiguration Include="${b.configuration}|${b.platform}">
99 <Configuration>${b.configuration}</Configuration>
100 <Platform>${b.platform}</Platform>
101 </ProjectConfiguration>
102 ${endfor}
103 </ItemGroup>
105 <PropertyGroup Label="Globals">
106 <ProjectGuid>{${project.uuid}}</ProjectGuid>
107 <Keyword>MakeFileProj</Keyword>
108 <ProjectName>${project.name}</ProjectName>
109 </PropertyGroup>
110 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
112 ${for b in project.build_properties}
113 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'" Label="Configuration">
114 <ConfigurationType>Makefile</ConfigurationType>
115 <OutDir>${b.outdir}</OutDir>
116 <PlatformToolset>${project.platform_toolset_ver}</PlatformToolset>
117 </PropertyGroup>
118 ${endfor}
120 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
121 <ImportGroup Label="ExtensionSettings">
122 </ImportGroup>
124 ${for b in project.build_properties}
125 <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
126 <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
127 </ImportGroup>
128 ${endfor}
130 ${for b in project.build_properties}
131 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
132 <NMakeBuildCommandLine>${xml:project.get_build_command(b)}</NMakeBuildCommandLine>
133 <NMakeReBuildCommandLine>${xml:project.get_rebuild_command(b)}</NMakeReBuildCommandLine>
134 <NMakeCleanCommandLine>${xml:project.get_clean_command(b)}</NMakeCleanCommandLine>
135 <NMakeIncludeSearchPath>${xml:b.includes_search_path}</NMakeIncludeSearchPath>
136 <NMakePreprocessorDefinitions>${xml:b.preprocessor_definitions};$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
137 <IncludePath>${xml:b.includes_search_path}</IncludePath>
138 <ExecutablePath>$(ExecutablePath)</ExecutablePath>
140 ${if getattr(b, 'output_file', None)}
141 <NMakeOutput>${xml:b.output_file}</NMakeOutput>
142 ${endif}
143 ${if getattr(b, 'deploy_dir', None)}
144 <RemoteRoot>${xml:b.deploy_dir}</RemoteRoot>
145 ${endif}
146 </PropertyGroup>
147 ${endfor}
149 ${for b in project.build_properties}
150 ${if getattr(b, 'deploy_dir', None)}
151 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
152 <Deploy>
153 <DeploymentType>CopyToHardDrive</DeploymentType>
154 </Deploy>
155 </ItemDefinitionGroup>
156 ${endif}
157 ${endfor}
159 <ItemGroup>
160 ${for x in project.source}
161 <${project.get_key(x)} Include='${x.win32path()}' />
162 ${endfor}
163 </ItemGroup>
164 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
165 <ImportGroup Label="ExtensionTargets">
166 </ImportGroup>
167 </Project>
170 FILTER_TEMPLATE = '''<?xml version="1.0" encoding="UTF-8"?>
171 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
172 <ItemGroup>
173 ${for x in project.source}
174 <${project.get_key(x)} Include="${x.win32path()}">
175 <Filter>${project.get_filter_name(x.parent)}</Filter>
176 </${project.get_key(x)}>
177 ${endfor}
178 </ItemGroup>
179 <ItemGroup>
180 ${for x in project.dirs()}
181 <Filter Include="${project.get_filter_name(x)}">
182 <UniqueIdentifier>{${project.make_uuid(x.win32path())}}</UniqueIdentifier>
183 </Filter>
184 ${endfor}
185 </ItemGroup>
186 </Project>
189 PROJECT_2008_TEMPLATE = r'''<?xml version="1.0" encoding="UTF-8"?>
190 <VisualStudioProject ProjectType="Visual C++" Version="9,00"
191 Name="${xml: project.name}" ProjectGUID="{${project.uuid}}"
192 Keyword="MakeFileProj"
193 TargetFrameworkVersion="196613">
194 <Platforms>
195 ${if project.build_properties}
196 ${for b in project.build_properties}
197 <Platform Name="${xml: b.platform}" />
198 ${endfor}
199 ${else}
200 <Platform Name="Win32" />
201 ${endif}
202 </Platforms>
203 <ToolFiles>
204 </ToolFiles>
205 <Configurations>
206 ${if project.build_properties}
207 ${for b in project.build_properties}
208 <Configuration
209 Name="${xml: b.configuration}|${xml: b.platform}"
210 IntermediateDirectory="$ConfigurationName"
211 OutputDirectory="${xml: b.outdir}"
212 ConfigurationType="0">
213 <Tool
214 Name="VCNMakeTool"
215 BuildCommandLine="${xml: project.get_build_command(b)}"
216 ReBuildCommandLine="${xml: project.get_rebuild_command(b)}"
217 CleanCommandLine="${xml: project.get_clean_command(b)}"
218 ${if getattr(b, 'output_file', None)}
219 Output="${xml: b.output_file}"
220 ${endif}
221 PreprocessorDefinitions="${xml: b.preprocessor_definitions}"
222 IncludeSearchPath="${xml: b.includes_search_path}"
223 ForcedIncludes=""
224 ForcedUsingAssemblies=""
225 AssemblySearchPath=""
226 CompileAsManaged=""
228 </Configuration>
229 ${endfor}
230 ${else}
231 <Configuration Name="Release|Win32" >
232 </Configuration>
233 ${endif}
234 </Configurations>
235 <References>
236 </References>
237 <Files>
238 ${project.display_filter()}
239 </Files>
240 </VisualStudioProject>
243 SOLUTION_TEMPLATE = '''Microsoft Visual Studio Solution File, Format Version ${project.numver}
244 # Visual Studio ${project.vsver}
245 ${for p in project.all_projects}
246 Project("{${p.ptype()}}") = "${p.name}", "${p.title}", "{${p.uuid}}"
247 EndProject${endfor}
248 Global
249 GlobalSection(SolutionConfigurationPlatforms) = preSolution
250 ${if project.all_projects}
251 ${for (configuration, platform) in project.all_projects[0].ctx.project_configurations()}
252 ${configuration}|${platform} = ${configuration}|${platform}
253 ${endfor}
254 ${endif}
255 EndGlobalSection
256 GlobalSection(ProjectConfigurationPlatforms) = postSolution
257 ${for p in project.all_projects}
258 ${if hasattr(p, 'source')}
259 ${for b in p.build_properties}
260 {${p.uuid}}.${b.configuration}|${b.platform}.ActiveCfg = ${b.configuration}|${b.platform}
261 ${if getattr(p, 'is_active', None)}
262 {${p.uuid}}.${b.configuration}|${b.platform}.Build.0 = ${b.configuration}|${b.platform}
263 ${endif}
264 ${if getattr(p, 'is_deploy', None)}
265 {${p.uuid}}.${b.configuration}|${b.platform}.Deploy.0 = ${b.configuration}|${b.platform}
266 ${endif}
267 ${endfor}
268 ${endif}
269 ${endfor}
270 EndGlobalSection
271 GlobalSection(SolutionProperties) = preSolution
272 HideSolutionNode = FALSE
273 EndGlobalSection
274 GlobalSection(NestedProjects) = preSolution
275 ${for p in project.all_projects}
276 ${if p.parent}
277 {${p.uuid}} = {${p.parent.uuid}}
278 ${endif}
279 ${endfor}
280 EndGlobalSection
281 EndGlobal
284 COMPILE_TEMPLATE = '''def f(project):
285 lst = []
286 def xml_escape(value):
287 return value.replace("&", "&amp;").replace('"', "&quot;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;")
291 #f = open('cmd.txt', 'w')
292 #f.write(str(lst))
293 #f.close()
294 return ''.join(lst)
296 reg_act = re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<code>[^}]*?)\})", re.M)
297 def compile_template(line):
299 Compile a template expression into a python function (like jsps, but way shorter)
301 extr = []
302 def repl(match):
303 g = match.group
304 if g('dollar'):
305 return "$"
306 elif g('backslash'):
307 return "\\"
308 elif g('subst'):
309 extr.append(g('code'))
310 return "<<|@|>>"
311 return None
313 line2 = reg_act.sub(repl, line)
314 params = line2.split('<<|@|>>')
315 assert(extr)
318 indent = 0
319 buf = []
320 app = buf.append
322 def app(txt):
323 buf.append(indent * '\t' + txt)
325 for x in range(len(extr)):
326 if params[x]:
327 app("lst.append(%r)" % params[x])
329 f = extr[x]
330 if f.startswith(('if', 'for')):
331 app(f + ':')
332 indent += 1
333 elif f.startswith('py:'):
334 app(f[3:])
335 elif f.startswith(('endif', 'endfor')):
336 indent -= 1
337 elif f.startswith(('else', 'elif')):
338 indent -= 1
339 app(f + ':')
340 indent += 1
341 elif f.startswith('xml:'):
342 app('lst.append(xml_escape(%s))' % f[4:])
343 else:
344 #app('lst.append((%s) or "cannot find %s")' % (f, f))
345 app('lst.append(%s)' % f)
347 if extr:
348 if params[-1]:
349 app("lst.append(%r)" % params[-1])
351 fun = COMPILE_TEMPLATE % "\n\t".join(buf)
352 #print(fun)
353 return Task.funex(fun)
356 re_blank = re.compile('(\n|\r|\\s)*\n', re.M)
357 def rm_blank_lines(txt):
358 txt = re_blank.sub('\r\n', txt)
359 return txt
361 BOM = '\xef\xbb\xbf'
362 try:
363 BOM = bytes(BOM, 'latin-1') # python 3
364 except TypeError:
365 pass
367 def stealth_write(self, data, flags='wb'):
368 try:
369 unicode
370 except NameError:
371 data = data.encode('utf-8') # python 3
372 else:
373 data = data.decode(sys.getfilesystemencoding(), 'replace')
374 data = data.encode('utf-8')
376 if self.name.endswith(('.vcproj', '.vcxproj')):
377 data = BOM + data
379 try:
380 txt = self.read(flags='rb')
381 if txt != data:
382 raise ValueError('must write')
383 except (IOError, ValueError):
384 self.write(data, flags=flags)
385 else:
386 Logs.debug('msvs: skipping %s', self.win32path())
387 Node.Node.stealth_write = stealth_write
389 re_win32 = re.compile(r'^([/\\]cygdrive)?[/\\]([a-z])([^a-z0-9_-].*)', re.I)
390 def win32path(self):
391 p = self.abspath()
392 m = re_win32.match(p)
393 if m:
394 return "%s:%s" % (m.group(2).upper(), m.group(3))
395 return p
396 Node.Node.win32path = win32path
398 re_quote = re.compile("[^a-zA-Z0-9-]")
399 def quote(s):
400 return re_quote.sub("_", s)
402 def xml_escape(value):
403 return value.replace("&", "&amp;").replace('"', "&quot;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;")
405 def make_uuid(v, prefix = None):
407 simple utility function
409 if isinstance(v, dict):
410 keys = list(v.keys())
411 keys.sort()
412 tmp = str([(k, v[k]) for k in keys])
413 else:
414 tmp = str(v)
415 d = Utils.md5(tmp.encode()).hexdigest().upper()
416 if prefix:
417 d = '%s%s' % (prefix, d[8:])
418 gid = uuid.UUID(d, version = 4)
419 return str(gid).upper()
421 def diff(node, fromnode):
422 # difference between two nodes, but with "(..)" instead of ".."
423 c1 = node
424 c2 = fromnode
426 c1h = c1.height()
427 c2h = c2.height()
429 lst = []
430 up = 0
432 while c1h > c2h:
433 lst.append(c1.name)
434 c1 = c1.parent
435 c1h -= 1
437 while c2h > c1h:
438 up += 1
439 c2 = c2.parent
440 c2h -= 1
442 while id(c1) != id(c2):
443 lst.append(c1.name)
444 up += 1
446 c1 = c1.parent
447 c2 = c2.parent
449 for i in range(up):
450 lst.append('(..)')
451 lst.reverse()
452 return tuple(lst)
454 class build_property(object):
455 pass
457 class vsnode(object):
459 Abstract class representing visual studio elements
460 We assume that all visual studio nodes have a uuid and a parent
462 def __init__(self, ctx):
463 self.ctx = ctx # msvs context
464 self.name = '' # string, mandatory
465 self.vspath = '' # path in visual studio (name for dirs, absolute path for projects)
466 self.uuid = '' # string, mandatory
467 self.parent = None # parent node for visual studio nesting
469 def get_waf(self):
471 Override in subclasses...
473 return 'cd /d "%s" & %s' % (self.ctx.srcnode.win32path(), getattr(self.ctx, 'waf_command', 'waf.bat'))
475 def ptype(self):
477 Return a special uuid for projects written in the solution file
479 pass
481 def write(self):
483 Write the project file, by default, do nothing
485 pass
487 def make_uuid(self, val):
489 Alias for creating uuid values easily (the templates cannot access global variables)
491 return make_uuid(val)
493 class vsnode_vsdir(vsnode):
495 Nodes representing visual studio folders (which do not match the filesystem tree!)
497 VS_GUID_SOLUTIONFOLDER = "2150E333-8FDC-42A3-9474-1A3956D46DE8"
498 def __init__(self, ctx, uuid, name, vspath=''):
499 vsnode.__init__(self, ctx)
500 self.title = self.name = name
501 self.uuid = uuid
502 self.vspath = vspath or name
504 def ptype(self):
505 return self.VS_GUID_SOLUTIONFOLDER
507 class vsnode_project(vsnode):
509 Abstract class representing visual studio project elements
510 A project is assumed to be writable, and has a node representing the file to write to
512 VS_GUID_VCPROJ = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
513 def ptype(self):
514 return self.VS_GUID_VCPROJ
516 def __init__(self, ctx, node):
517 vsnode.__init__(self, ctx)
518 self.path = node
519 self.uuid = make_uuid(node.win32path())
520 self.name = node.name
521 self.platform_toolset_ver = getattr(ctx, 'platform_toolset_ver', None)
522 self.title = self.path.win32path()
523 self.source = [] # list of node objects
524 self.build_properties = [] # list of properties (nmake commands, output dir, etc)
526 def dirs(self):
528 Get the list of parent folders of the source files (header files included)
529 for writing the filters
531 lst = []
532 def add(x):
533 if x.height() > self.tg.path.height() and x not in lst:
534 lst.append(x)
535 add(x.parent)
536 for x in self.source:
537 add(x.parent)
538 return lst
540 def write(self):
541 Logs.debug('msvs: creating %r', self.path)
543 # first write the project file
544 template1 = compile_template(PROJECT_TEMPLATE)
545 proj_str = template1(self)
546 proj_str = rm_blank_lines(proj_str)
547 self.path.stealth_write(proj_str)
549 # then write the filter
550 template2 = compile_template(FILTER_TEMPLATE)
551 filter_str = template2(self)
552 filter_str = rm_blank_lines(filter_str)
553 tmp = self.path.parent.make_node(self.path.name + '.filters')
554 tmp.stealth_write(filter_str)
556 def get_key(self, node):
558 required for writing the source files
560 name = node.name
561 if name.endswith(('.cpp', '.c')):
562 return 'ClCompile'
563 return 'ClInclude'
565 def collect_properties(self):
567 Returns a list of triplet (configuration, platform, output_directory)
569 ret = []
570 for c in self.ctx.configurations:
571 for p in self.ctx.platforms:
572 x = build_property()
573 x.outdir = ''
575 x.configuration = c
576 x.platform = p
578 x.preprocessor_definitions = ''
579 x.includes_search_path = ''
581 # can specify "deploy_dir" too
582 ret.append(x)
583 self.build_properties = ret
585 def get_build_params(self, props):
586 opt = '--execsolution=%s' % self.ctx.get_solution_node().win32path()
587 return (self.get_waf(), opt)
589 def get_build_command(self, props):
590 return "%s build %s" % self.get_build_params(props)
592 def get_clean_command(self, props):
593 return "%s clean %s" % self.get_build_params(props)
595 def get_rebuild_command(self, props):
596 return "%s clean build %s" % self.get_build_params(props)
598 def get_filter_name(self, node):
599 lst = diff(node, self.tg.path)
600 return '\\'.join(lst) or '.'
602 class vsnode_alias(vsnode_project):
603 def __init__(self, ctx, node, name):
604 vsnode_project.__init__(self, ctx, node)
605 self.name = name
606 self.output_file = ''
608 class vsnode_build_all(vsnode_alias):
610 Fake target used to emulate the behaviour of "make all" (starting one process by target is slow)
611 This is the only alias enabled by default
613 def __init__(self, ctx, node, name='build_all_projects'):
614 vsnode_alias.__init__(self, ctx, node, name)
615 self.is_active = True
617 class vsnode_install_all(vsnode_alias):
619 Fake target used to emulate the behaviour of "make install"
621 def __init__(self, ctx, node, name='install_all_projects'):
622 vsnode_alias.__init__(self, ctx, node, name)
624 def get_build_command(self, props):
625 return "%s build install %s" % self.get_build_params(props)
627 def get_clean_command(self, props):
628 return "%s clean %s" % self.get_build_params(props)
630 def get_rebuild_command(self, props):
631 return "%s clean build install %s" % self.get_build_params(props)
633 class vsnode_project_view(vsnode_alias):
635 Fake target used to emulate a file system view
637 def __init__(self, ctx, node, name='project_view'):
638 vsnode_alias.__init__(self, ctx, node, name)
639 self.tg = self.ctx() # fake one, cannot remove
640 self.exclude_files = Node.exclude_regs + '''
641 waf-2*
642 waf3-2*/**
643 .waf-2*
644 .waf3-2*/**
645 **/*.sdf
646 **/*.suo
647 **/*.ncb
648 **/%s
649 ''' % Options.lockfile
651 def collect_source(self):
652 # this is likely to be slow
653 self.source = self.ctx.srcnode.ant_glob('**', excl=self.exclude_files)
655 def get_build_command(self, props):
656 params = self.get_build_params(props) + (self.ctx.cmd,)
657 return "%s %s %s" % params
659 def get_clean_command(self, props):
660 return ""
662 def get_rebuild_command(self, props):
663 return self.get_build_command(props)
665 class vsnode_target(vsnode_project):
667 Visual studio project representing a targets (programs, libraries, etc) and bound
668 to a task generator
670 def __init__(self, ctx, tg):
672 A project is more or less equivalent to a file/folder
674 base = getattr(ctx, 'projects_dir', None) or tg.path
675 node = base.make_node(quote(tg.name) + ctx.project_extension) # the project file as a Node
676 vsnode_project.__init__(self, ctx, node)
677 self.name = quote(tg.name)
678 self.tg = tg # task generator
680 def get_build_params(self, props):
682 Override the default to add the target name
684 opt = '--execsolution=%s' % self.ctx.get_solution_node().win32path()
685 if getattr(self, 'tg', None):
686 opt += " --targets=%s" % self.tg.name
687 return (self.get_waf(), opt)
689 def collect_source(self):
690 tg = self.tg
691 source_files = tg.to_nodes(getattr(tg, 'source', []))
692 include_dirs = Utils.to_list(getattr(tg, 'msvs_includes', []))
693 include_files = []
694 for x in include_dirs:
695 if isinstance(x, str):
696 x = tg.path.find_node(x)
697 if x:
698 lst = [y for y in x.ant_glob(HEADERS_GLOB, flat=False)]
699 include_files.extend(lst)
701 # remove duplicates
702 self.source.extend(list(set(source_files + include_files)))
703 self.source.sort(key=lambda x: x.win32path())
705 def collect_properties(self):
707 Visual studio projects are associated with platforms and configurations (for building especially)
709 super(vsnode_target, self).collect_properties()
710 for x in self.build_properties:
711 x.outdir = self.path.parent.win32path()
712 x.preprocessor_definitions = ''
713 x.includes_search_path = ''
715 try:
716 tsk = self.tg.link_task
717 except AttributeError:
718 pass
719 else:
720 x.output_file = tsk.outputs[0].win32path()
721 x.preprocessor_definitions = ';'.join(tsk.env.DEFINES)
722 x.includes_search_path = ';'.join(self.tg.env.INCPATHS)
724 class msvs_generator(BuildContext):
725 '''generates a visual studio 2010 solution'''
726 cmd = 'msvs'
727 fun = 'build'
728 numver = '11.00' # Visual Studio Version Number
729 vsver = '2010' # Visual Studio Version Year
730 platform_toolset_ver = 'v110' # Platform Toolset Version Number
732 def init(self):
734 Some data that needs to be present
736 if not getattr(self, 'configurations', None):
737 self.configurations = ['Release'] # LocalRelease, RemoteDebug, etc
738 if not getattr(self, 'platforms', None):
739 self.platforms = ['Win32']
740 if not getattr(self, 'all_projects', None):
741 self.all_projects = []
742 if not getattr(self, 'project_extension', None):
743 self.project_extension = '.vcxproj'
744 if not getattr(self, 'projects_dir', None):
745 self.projects_dir = self.srcnode.make_node('.depproj')
746 self.projects_dir.mkdir()
748 # bind the classes to the object, so that subclass can provide custom generators
749 if not getattr(self, 'vsnode_vsdir', None):
750 self.vsnode_vsdir = vsnode_vsdir
751 if not getattr(self, 'vsnode_target', None):
752 self.vsnode_target = vsnode_target
753 if not getattr(self, 'vsnode_build_all', None):
754 self.vsnode_build_all = vsnode_build_all
755 if not getattr(self, 'vsnode_install_all', None):
756 self.vsnode_install_all = vsnode_install_all
757 if not getattr(self, 'vsnode_project_view', None):
758 self.vsnode_project_view = vsnode_project_view
760 self.numver = self.__class__.numver
761 self.vsver = self.__class__.vsver
762 self.platform_toolset_ver = self.__class__.platform_toolset_ver
764 def execute(self):
766 Entry point
768 self.restore()
769 if not self.all_envs:
770 self.load_envs()
771 self.recurse([self.run_dir])
773 # user initialization
774 self.init()
776 # two phases for creating the solution
777 self.collect_projects() # add project objects into "self.all_projects"
778 self.write_files() # write the corresponding project and solution files
780 def collect_projects(self):
782 Fill the list self.all_projects with project objects
783 Fill the list of build targets
785 self.collect_targets()
786 self.add_aliases()
787 self.collect_dirs()
788 default_project = getattr(self, 'default_project', None)
789 def sortfun(x):
790 # folders should sort to the top
791 if getattr(x, 'VS_GUID_SOLUTIONFOLDER', None):
792 return ''
793 # followed by the default project
794 elif x.name == default_project:
795 return ' '
796 return getattr(x, 'path', None) and x.path.win32path() or x.name
797 self.all_projects.sort(key=sortfun)
799 def write_files(self):
801 Write the project and solution files from the data collected
802 so far. It is unlikely that you will want to change this
804 for p in self.all_projects:
805 p.write()
807 # and finally write the solution file
808 node = self.get_solution_node()
809 node.parent.mkdir()
810 Logs.warn('Creating %r', node)
811 template1 = compile_template(SOLUTION_TEMPLATE)
812 sln_str = template1(self)
813 sln_str = rm_blank_lines(sln_str)
814 node.stealth_write(sln_str)
816 def get_solution_node(self):
818 The solution filename is required when writing the .vcproj files
819 return self.solution_node and if it does not exist, make one
821 try:
822 return self.solution_node
823 except AttributeError:
824 pass
826 solution_name = getattr(self, 'solution_name', None)
827 if not solution_name:
828 solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '.sln'
829 if os.path.isabs(solution_name):
830 self.solution_node = self.root.make_node(solution_name)
831 else:
832 self.solution_node = self.srcnode.make_node(solution_name)
833 return self.solution_node
835 def project_configurations(self):
837 Helper that returns all the pairs (config,platform)
839 ret = []
840 for c in self.configurations:
841 for p in self.platforms:
842 ret.append((c, p))
843 return ret
845 def collect_targets(self):
847 Process the list of task generators
849 for g in self.groups:
850 for tg in g:
851 if not isinstance(tg, TaskGen.task_gen):
852 continue
854 if not hasattr(tg, 'msvs_includes'):
855 tg.msvs_includes = tg.to_list(getattr(tg, 'includes', [])) + tg.to_list(getattr(tg, 'export_includes', []))
856 tg.post()
857 if not getattr(tg, 'link_task', None):
858 continue
860 p = self.vsnode_target(self, tg)
861 p.collect_source() # delegate this processing
862 p.collect_properties()
863 self.all_projects.append(p)
865 def add_aliases(self):
867 Add a specific target that emulates the "make all" necessary for Visual studio when pressing F7
868 We also add an alias for "make install" (disabled by default)
870 base = getattr(self, 'projects_dir', None) or self.tg.path
872 node_project = base.make_node('build_all_projects' + self.project_extension) # Node
873 p_build = self.vsnode_build_all(self, node_project)
874 p_build.collect_properties()
875 self.all_projects.append(p_build)
877 node_project = base.make_node('install_all_projects' + self.project_extension) # Node
878 p_install = self.vsnode_install_all(self, node_project)
879 p_install.collect_properties()
880 self.all_projects.append(p_install)
882 node_project = base.make_node('project_view' + self.project_extension) # Node
883 p_view = self.vsnode_project_view(self, node_project)
884 p_view.collect_source()
885 p_view.collect_properties()
886 self.all_projects.append(p_view)
888 n = self.vsnode_vsdir(self, make_uuid(self.srcnode.win32path() + 'build_aliases'), "build_aliases")
889 p_build.parent = p_install.parent = p_view.parent = n
890 self.all_projects.append(n)
892 def collect_dirs(self):
894 Create the folder structure in the Visual studio project view
896 seen = {}
897 def make_parents(proj):
898 # look at a project, try to make a parent
899 if getattr(proj, 'parent', None):
900 # aliases already have parents
901 return
902 x = proj.iter_path
903 if x in seen:
904 proj.parent = seen[x]
905 return
907 # There is not vsnode_vsdir for x.
908 # So create a project representing the folder "x"
909 n = proj.parent = seen[x] = self.vsnode_vsdir(self, make_uuid(x.win32path()), x.name)
910 n.iter_path = x.parent
911 self.all_projects.append(n)
913 # recurse up to the project directory
914 if x.height() > self.srcnode.height() + 1:
915 make_parents(n)
917 for p in self.all_projects[:]: # iterate over a copy of all projects
918 if not getattr(p, 'tg', None):
919 # but only projects that have a task generator
920 continue
922 # make a folder for each task generator
923 p.iter_path = p.tg.path
924 make_parents(p)
926 def wrap_2008(cls):
927 class dec(cls):
928 def __init__(self, *k, **kw):
929 cls.__init__(self, *k, **kw)
930 self.project_template = PROJECT_2008_TEMPLATE
932 def display_filter(self):
934 root = build_property()
935 root.subfilters = []
936 root.sourcefiles = []
937 root.source = []
938 root.name = ''
940 @Utils.run_once
941 def add_path(lst):
942 if not lst:
943 return root
944 child = build_property()
945 child.subfilters = []
946 child.sourcefiles = []
947 child.source = []
948 child.name = lst[-1]
950 par = add_path(lst[:-1])
951 par.subfilters.append(child)
952 return child
954 for x in self.source:
955 # this crap is for enabling subclasses to override get_filter_name
956 tmp = self.get_filter_name(x.parent)
957 tmp = tmp != '.' and tuple(tmp.split('\\')) or ()
958 par = add_path(tmp)
959 par.source.append(x)
961 def display(n):
962 buf = []
963 for x in n.source:
964 buf.append('<File RelativePath="%s" FileType="%s"/>\n' % (xml_escape(x.win32path()), self.get_key(x)))
965 for x in n.subfilters:
966 buf.append('<Filter Name="%s">' % xml_escape(x.name))
967 buf.append(display(x))
968 buf.append('</Filter>')
969 return '\n'.join(buf)
971 return display(root)
973 def get_key(self, node):
975 If you do not want to let visual studio use the default file extensions,
976 override this method to return a value:
977 0: C/C++ Code, 1: C++ Class, 2: C++ Header File, 3: C++ Form,
978 4: C++ Control, 5: Text File, 6: DEF File, 7: IDL File,
979 8: Makefile, 9: RGS File, 10: RC File, 11: RES File, 12: XSD File,
980 13: XML File, 14: HTML File, 15: CSS File, 16: Bitmap, 17: Icon,
981 18: Resx File, 19: BSC File, 20: XSX File, 21: C++ Web Service,
982 22: ASAX File, 23: Asp Page, 24: Document, 25: Discovery File,
983 26: C# File, 27: eFileTypeClassDiagram, 28: MHTML Document,
984 29: Property Sheet, 30: Cursor, 31: Manifest, 32: eFileTypeRDLC
986 return ''
988 def write(self):
989 Logs.debug('msvs: creating %r', self.path)
990 template1 = compile_template(self.project_template)
991 proj_str = template1(self)
992 proj_str = rm_blank_lines(proj_str)
993 self.path.stealth_write(proj_str)
995 return dec
997 class msvs_2008_generator(msvs_generator):
998 '''generates a visual studio 2008 solution'''
999 cmd = 'msvs2008'
1000 fun = msvs_generator.fun
1001 numver = '10.00'
1002 vsver = '2008'
1004 def init(self):
1005 if not getattr(self, 'project_extension', None):
1006 self.project_extension = '_2008.vcproj'
1007 if not getattr(self, 'solution_name', None):
1008 self.solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '_2008.sln'
1010 if not getattr(self, 'vsnode_target', None):
1011 self.vsnode_target = wrap_2008(vsnode_target)
1012 if not getattr(self, 'vsnode_build_all', None):
1013 self.vsnode_build_all = wrap_2008(vsnode_build_all)
1014 if not getattr(self, 'vsnode_install_all', None):
1015 self.vsnode_install_all = wrap_2008(vsnode_install_all)
1016 if not getattr(self, 'vsnode_project_view', None):
1017 self.vsnode_project_view = wrap_2008(vsnode_project_view)
1019 msvs_generator.init(self)
1021 def options(ctx):
1023 If the msvs option is used, try to detect if the build is made from visual studio
1025 ctx.add_option('--execsolution', action='store', help='when building with visual studio, use a build state file')
1027 old = BuildContext.execute
1028 def override_build_state(ctx):
1029 def lock(rm, add):
1030 uns = ctx.options.execsolution.replace('.sln', rm)
1031 uns = ctx.root.make_node(uns)
1032 try:
1033 uns.delete()
1034 except OSError:
1035 pass
1037 uns = ctx.options.execsolution.replace('.sln', add)
1038 uns = ctx.root.make_node(uns)
1039 try:
1040 uns.write('')
1041 except EnvironmentError:
1042 pass
1044 if ctx.options.execsolution:
1045 ctx.launch_dir = Context.top_dir # force a build for the whole project (invalid cwd when called by visual studio)
1046 lock('.lastbuildstate', '.unsuccessfulbuild')
1047 old(ctx)
1048 lock('.unsuccessfulbuild', '.lastbuildstate')
1049 else:
1050 old(ctx)
1051 BuildContext.execute = override_build_state