3 # Avalanche Studios 2009-2011
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
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.
35 To add this tool to your project:
39 It can be a good idea to add the sync_exec tool too.
41 To generate solution files:
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
54 class msvs_bar(msvs.msvs_generator):
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::
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):
78 platform_toolset_ver = 'v120'
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
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>
105 <PropertyGroup Label="Globals">
106 <ProjectGuid>{${project.uuid}}</ProjectGuid>
107 <Keyword>MakeFileProj</Keyword>
108 <ProjectName>${project.name}</ProjectName>
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>
120 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
121 <ImportGroup Label="ExtensionSettings">
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" />
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>
143 ${if getattr(b, 'deploy_dir', None)}
144 <RemoteRoot>${xml:b.deploy_dir}</RemoteRoot>
149 ${for b in project.build_properties}
150 ${if getattr(b, 'deploy_dir', None)}
151 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
153 <DeploymentType>CopyToHardDrive</DeploymentType>
155 </ItemDefinitionGroup>
160 ${for x in project.source}
161 <${project.get_key(x)} Include='${x.win32path()}' />
164 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
165 <ImportGroup Label="ExtensionTargets">
170 FILTER_TEMPLATE
= '''<?xml version="1.0" encoding="UTF-8"?>
171 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
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)}>
180 ${for x in project.dirs()}
181 <Filter Include="${project.get_filter_name(x)}">
182 <UniqueIdentifier>{${project.make_uuid(x.win32path())}}</UniqueIdentifier>
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">
195 ${if project.build_properties}
196 ${for b in project.build_properties}
197 <Platform Name="${xml: b.platform}" />
200 <Platform Name="Win32" />
206 ${if project.build_properties}
207 ${for b in project.build_properties}
209 Name="${xml: b.configuration}|${xml: b.platform}"
210 IntermediateDirectory="$ConfigurationName"
211 OutputDirectory="${xml: b.outdir}"
212 ConfigurationType="0">
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}"
221 PreprocessorDefinitions="${xml: b.preprocessor_definitions}"
222 IncludeSearchPath="${xml: b.includes_search_path}"
224 ForcedUsingAssemblies=""
225 AssemblySearchPath=""
231 <Configuration Name="Release|Win32" >
238 ${project.display_filter()}
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}}"
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}
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}
264 ${if getattr(p, 'is_deploy', None)}
265 {${p.uuid}}.${b.configuration}|${b.platform}.Deploy.0 = ${b.configuration}|${b.platform}
271 GlobalSection(SolutionProperties) = preSolution
272 HideSolutionNode = FALSE
274 GlobalSection(NestedProjects) = preSolution
275 ${for p in project.all_projects}
277 {${p.uuid}} = {${p.parent.uuid}}
284 COMPILE_TEMPLATE
= '''def f(project):
286 def xml_escape(value):
287 return value.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">")
291 #f = open('cmd.txt', 'w')
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)
309 extr
.append(g('code'))
313 line2
= reg_act
.sub(repl
, line
)
314 params
= line2
.split('<<|@|>>')
323 buf
.append(indent
* '\t' + txt
)
325 for x
in range(len(extr
)):
327 app("lst.append(%r)" % params
[x
])
330 if f
.startswith(('if', 'for')):
333 elif f
.startswith('py:'):
335 elif f
.startswith(('endif', 'endfor')):
337 elif f
.startswith(('else', 'elif')):
341 elif f
.startswith('xml:'):
342 app('lst.append(xml_escape(%s))' % f
[4:])
344 #app('lst.append((%s) or "cannot find %s")' % (f, f))
345 app('lst.append(%s)' % f
)
349 app("lst.append(%r)" % params
[-1])
351 fun
= COMPILE_TEMPLATE
% "\n\t".join(buf
)
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
)
363 BOM
= bytes(BOM
, 'latin-1') # python 3
367 def stealth_write(self
, data
, flags
='wb'):
371 data
= data
.encode('utf-8') # python 3
373 data
= data
.decode(sys
.getfilesystemencoding(), 'replace')
374 data
= data
.encode('utf-8')
376 if self
.name
.endswith(('.vcproj', '.vcxproj')):
380 txt
= self
.read(flags
='rb')
382 raise ValueError('must write')
383 except (IOError, ValueError):
384 self
.write(data
, flags
=flags
)
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
)
392 m
= re_win32
.match(p
)
394 return "%s:%s" % (m
.group(2).upper(), m
.group(3))
396 Node
.Node
.win32path
= win32path
398 re_quote
= re
.compile("[^a-zA-Z0-9-]")
400 return re_quote
.sub("_", s
)
402 def xml_escape(value
):
403 return value
.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">")
405 def make_uuid(v
, prefix
= None):
407 simple utility function
409 if isinstance(v
, dict):
410 keys
= list(v
.keys())
412 tmp
= str([(k
, v
[k
]) for k
in keys
])
415 d
= Utils
.md5(tmp
.encode()).hexdigest().upper()
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 ".."
442 while id(c1
) != id(c2
):
454 class build_property(object):
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
471 Override in subclasses...
473 return 'cd /d "%s" & %s' % (self
.ctx
.srcnode
.win32path(), getattr(self
.ctx
, 'waf_command', 'waf.bat'))
477 Return a special uuid for projects written in the solution file
483 Write the project file, by default, do nothing
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
502 self
.vspath
= vspath
or name
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"
514 return self
.VS_GUID_VCPROJ
516 def __init__(self
, ctx
, node
):
517 vsnode
.__init
__(self
, ctx
)
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)
528 Get the list of parent folders of the source files (header files included)
529 for writing the filters
533 if x
.height() > self
.tg
.path
.height() and x
not in lst
:
536 for x
in self
.source
:
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
561 if name
.endswith(('.cpp', '.c')):
565 def collect_properties(self
):
567 Returns a list of triplet (configuration, platform, output_directory)
570 for c
in self
.ctx
.configurations
:
571 for p
in self
.ctx
.platforms
:
578 x
.preprocessor_definitions
= ''
579 x
.includes_search_path
= ''
581 # can specify "deploy_dir" too
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
)
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
+ '''
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
):
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
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
):
691 source_files
= tg
.to_nodes(getattr(tg
, 'source', []))
692 include_dirs
= Utils
.to_list(getattr(tg
, 'msvs_includes', []))
694 for x
in include_dirs
:
695 if isinstance(x
, str):
696 x
= tg
.path
.find_node(x
)
698 lst
= [y
for y
in x
.ant_glob(HEADERS_GLOB
, flat
=False)]
699 include_files
.extend(lst
)
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
= ''
716 tsk
= self
.tg
.link_task
717 except AttributeError:
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'''
728 numver
= '11.00' # Visual Studio Version Number
729 vsver
= '2010' # Visual Studio Version Year
730 platform_toolset_ver
= 'v110' # Platform Toolset Version Number
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
769 if not self
.all_envs
:
771 self
.recurse([self
.run_dir
])
773 # user initialization
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()
788 default_project
= getattr(self
, 'default_project', None)
790 # folders should sort to the top
791 if getattr(x
, 'VS_GUID_SOLUTIONFOLDER', None):
793 # followed by the default project
794 elif x
.name
== default_project
:
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
:
807 # and finally write the solution file
808 node
= self
.get_solution_node()
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
822 return self
.solution_node
823 except AttributeError:
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
)
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)
840 for c
in self
.configurations
:
841 for p
in self
.platforms
:
845 def collect_targets(self
):
847 Process the list of task generators
849 for g
in self
.groups
:
851 if not isinstance(tg
, TaskGen
.task_gen
):
854 if not hasattr(tg
, 'msvs_includes'):
855 tg
.msvs_includes
= tg
.to_list(getattr(tg
, 'includes', [])) + tg
.to_list(getattr(tg
, 'export_includes', []))
857 if not getattr(tg
, 'link_task', None):
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
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
904 proj
.parent
= seen
[x
]
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:
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
922 # make a folder for each task generator
923 p
.iter_path
= p
.tg
.path
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()
936 root
.sourcefiles
= []
944 child
= build_property()
945 child
.subfilters
= []
946 child
.sourcefiles
= []
950 par
= add_path(lst
[:-1])
951 par
.subfilters
.append(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 ()
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
)
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
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
)
997 class msvs_2008_generator(msvs_generator
):
998 '''generates a visual studio 2008 solution'''
1000 fun
= msvs_generator
.fun
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
)
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
):
1030 uns
= ctx
.options
.execsolution
.replace('.sln', rm
)
1031 uns
= ctx
.root
.make_node(uns
)
1037 uns
= ctx
.options
.execsolution
.replace('.sln', add
)
1038 uns
= ctx
.root
.make_node(uns
)
1041 except EnvironmentError:
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')
1048 lock('.unsuccessfulbuild', '.lastbuildstate')
1051 BuildContext
.execute
= override_build_state