1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048 |
- #! /usr/bin/env python
- # encoding: utf-8
- # Avalanche Studios 2009-2011
- # Thomas Nagy 2011
- """
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- """
- """
- To add this tool to your project:
- def options(conf):
- opt.load('msvs')
- It can be a good idea to add the sync_exec tool too.
- To generate solution files:
- $ waf configure msvs
- To customize the outputs, provide subclasses in your wscript files::
- from waflib.extras import msvs
- class vsnode_target(msvs.vsnode_target):
- def get_build_command(self, props):
- # likely to be required
- return "waf.bat build"
- def collect_source(self):
- # likely to be required
- ...
- class msvs_bar(msvs.msvs_generator):
- def init(self):
- msvs.msvs_generator.init(self)
- self.vsnode_target = vsnode_target
- The msvs class re-uses the same build() function for reading the targets (task generators),
- you may therefore specify msvs settings on the context object::
- def build(bld):
- bld.solution_name = 'foo.sln'
- bld.waf_command = 'waf.bat'
- bld.projects_dir = bld.srcnode.make_node('.depproj')
- bld.projects_dir.mkdir()
- For visual studio 2008, the command is called 'msvs2008', and the classes
- such as vsnode_target are wrapped by a decorator class 'wrap_2008' to
- provide special functionality.
- To customize platform toolsets, pass additional parameters, for example::
- class msvs_2013(msvs.msvs_generator):
- cmd = 'msvs2013'
- numver = '13.00'
- vsver = '2013'
- platform_toolset_ver = 'v120'
- ASSUMPTIONS:
- * a project can be either a directory or a target, vcxproj files are written only for targets that have source files
- * each project is a vcxproj file, therefore the project uuid needs only to be a hash of the absolute path
- """
- import os, re, sys
- import uuid # requires python 2.5
- from waflib.Build import BuildContext
- from waflib import Utils, TaskGen, Logs, Task, Context, Node, Options
- HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)'
- PROJECT_TEMPLATE = r'''<?xml version="1.0" encoding="UTF-8"?>
- <Project DefaultTargets="Build" ToolsVersion="4.0"
- xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- ${for b in project.build_properties}
- <ProjectConfiguration Include="${b.configuration}|${b.platform}">
- <Configuration>${b.configuration}</Configuration>
- <Platform>${b.platform}</Platform>
- </ProjectConfiguration>
- ${endfor}
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{${project.uuid}}</ProjectGuid>
- <Keyword>MakeFileProj</Keyword>
- <ProjectName>${project.name}</ProjectName>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- ${for b in project.build_properties}
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'" Label="Configuration">
- <ConfigurationType>Makefile</ConfigurationType>
- <OutDir>${b.outdir}</OutDir>
- <PlatformToolset>${project.platform_toolset_ver}</PlatformToolset>
- </PropertyGroup>
- ${endfor}
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- ${for b in project.build_properties}
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- ${endfor}
- ${for b in project.build_properties}
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
- <NMakeBuildCommandLine>${xml:project.get_build_command(b)}</NMakeBuildCommandLine>
- <NMakeReBuildCommandLine>${xml:project.get_rebuild_command(b)}</NMakeReBuildCommandLine>
- <NMakeCleanCommandLine>${xml:project.get_clean_command(b)}</NMakeCleanCommandLine>
- <NMakeIncludeSearchPath>${xml:b.includes_search_path}</NMakeIncludeSearchPath>
- <NMakePreprocessorDefinitions>${xml:b.preprocessor_definitions};$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
- <IncludePath>${xml:b.includes_search_path}</IncludePath>
- <ExecutablePath>$(ExecutablePath)</ExecutablePath>
- ${if getattr(b, 'output_file', None)}
- <NMakeOutput>${xml:b.output_file}</NMakeOutput>
- ${endif}
- ${if getattr(b, 'deploy_dir', None)}
- <RemoteRoot>${xml:b.deploy_dir}</RemoteRoot>
- ${endif}
- </PropertyGroup>
- ${endfor}
- ${for b in project.build_properties}
- ${if getattr(b, 'deploy_dir', None)}
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='${b.configuration}|${b.platform}'">
- <Deploy>
- <DeploymentType>CopyToHardDrive</DeploymentType>
- </Deploy>
- </ItemDefinitionGroup>
- ${endif}
- ${endfor}
- <ItemGroup>
- ${for x in project.source}
- <${project.get_key(x)} Include='${x.win32path()}' />
- ${endfor}
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
- </Project>
- '''
- FILTER_TEMPLATE = '''<?xml version="1.0" encoding="UTF-8"?>
- <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- ${for x in project.source}
- <${project.get_key(x)} Include="${x.win32path()}">
- <Filter>${project.get_filter_name(x.parent)}</Filter>
- </${project.get_key(x)}>
- ${endfor}
- </ItemGroup>
- <ItemGroup>
- ${for x in project.dirs()}
- <Filter Include="${project.get_filter_name(x)}">
- <UniqueIdentifier>{${project.make_uuid(x.win32path())}}</UniqueIdentifier>
- </Filter>
- ${endfor}
- </ItemGroup>
- </Project>
- '''
- PROJECT_2008_TEMPLATE = r'''<?xml version="1.0" encoding="UTF-8"?>
- <VisualStudioProject ProjectType="Visual C++" Version="9,00"
- Name="${xml: project.name}" ProjectGUID="{${project.uuid}}"
- Keyword="MakeFileProj"
- TargetFrameworkVersion="196613">
- <Platforms>
- ${if project.build_properties}
- ${for b in project.build_properties}
- <Platform Name="${xml: b.platform}" />
- ${endfor}
- ${else}
- <Platform Name="Win32" />
- ${endif}
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- ${if project.build_properties}
- ${for b in project.build_properties}
- <Configuration
- Name="${xml: b.configuration}|${xml: b.platform}"
- IntermediateDirectory="$ConfigurationName"
- OutputDirectory="${xml: b.outdir}"
- ConfigurationType="0">
- <Tool
- Name="VCNMakeTool"
- BuildCommandLine="${xml: project.get_build_command(b)}"
- ReBuildCommandLine="${xml: project.get_rebuild_command(b)}"
- CleanCommandLine="${xml: project.get_clean_command(b)}"
- ${if getattr(b, 'output_file', None)}
- Output="${xml: b.output_file}"
- ${endif}
- PreprocessorDefinitions="${xml: b.preprocessor_definitions}"
- IncludeSearchPath="${xml: b.includes_search_path}"
- ForcedIncludes=""
- ForcedUsingAssemblies=""
- AssemblySearchPath=""
- CompileAsManaged=""
- />
- </Configuration>
- ${endfor}
- ${else}
- <Configuration Name="Release|Win32" >
- </Configuration>
- ${endif}
- </Configurations>
- <References>
- </References>
- <Files>
- ${project.display_filter()}
- </Files>
- </VisualStudioProject>
- '''
- SOLUTION_TEMPLATE = '''Microsoft Visual Studio Solution File, Format Version ${project.numver}
- # Visual Studio ${project.vsver}
- ${for p in project.all_projects}
- Project("{${p.ptype()}}") = "${p.name}", "${p.title}", "{${p.uuid}}"
- EndProject${endfor}
- Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- ${if project.all_projects}
- ${for (configuration, platform) in project.all_projects[0].ctx.project_configurations()}
- ${configuration}|${platform} = ${configuration}|${platform}
- ${endfor}
- ${endif}
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- ${for p in project.all_projects}
- ${if hasattr(p, 'source')}
- ${for b in p.build_properties}
- {${p.uuid}}.${b.configuration}|${b.platform}.ActiveCfg = ${b.configuration}|${b.platform}
- ${if getattr(p, 'is_active', None)}
- {${p.uuid}}.${b.configuration}|${b.platform}.Build.0 = ${b.configuration}|${b.platform}
- ${endif}
- ${if getattr(p, 'is_deploy', None)}
- {${p.uuid}}.${b.configuration}|${b.platform}.Deploy.0 = ${b.configuration}|${b.platform}
- ${endif}
- ${endfor}
- ${endif}
- ${endfor}
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- ${for p in project.all_projects}
- ${if p.parent}
- {${p.uuid}} = {${p.parent.uuid}}
- ${endif}
- ${endfor}
- EndGlobalSection
- EndGlobal
- '''
- COMPILE_TEMPLATE = '''def f(project):
- lst = []
- def xml_escape(value):
- return value.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">")
- %s
- #f = open('cmd.txt', 'w')
- #f.write(str(lst))
- #f.close()
- return ''.join(lst)
- '''
- reg_act = re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<code>[^}]*?)\})", re.M)
- def compile_template(line):
- """
- Compile a template expression into a python function (like jsps, but way shorter)
- """
- extr = []
- def repl(match):
- g = match.group
- if g('dollar'):
- return "$"
- elif g('backslash'):
- return "\\"
- elif g('subst'):
- extr.append(g('code'))
- return "<<|@|>>"
- return None
- line2 = reg_act.sub(repl, line)
- params = line2.split('<<|@|>>')
- assert(extr)
- indent = 0
- buf = []
- app = buf.append
- def app(txt):
- buf.append(indent * '\t' + txt)
- for x in range(len(extr)):
- if params[x]:
- app("lst.append(%r)" % params[x])
- f = extr[x]
- if f.startswith(('if', 'for')):
- app(f + ':')
- indent += 1
- elif f.startswith('py:'):
- app(f[3:])
- elif f.startswith(('endif', 'endfor')):
- indent -= 1
- elif f.startswith(('else', 'elif')):
- indent -= 1
- app(f + ':')
- indent += 1
- elif f.startswith('xml:'):
- app('lst.append(xml_escape(%s))' % f[4:])
- else:
- #app('lst.append((%s) or "cannot find %s")' % (f, f))
- app('lst.append(%s)' % f)
- if extr:
- if params[-1]:
- app("lst.append(%r)" % params[-1])
- fun = COMPILE_TEMPLATE % "\n\t".join(buf)
- #print(fun)
- return Task.funex(fun)
- re_blank = re.compile('(\n|\r|\\s)*\n', re.M)
- def rm_blank_lines(txt):
- txt = re_blank.sub('\r\n', txt)
- return txt
- BOM = '\xef\xbb\xbf'
- try:
- BOM = bytes(BOM, 'latin-1') # python 3
- except TypeError:
- pass
- def stealth_write(self, data, flags='wb'):
- try:
- unicode
- except NameError:
- data = data.encode('utf-8') # python 3
- else:
- data = data.decode(sys.getfilesystemencoding(), 'replace')
- data = data.encode('utf-8')
- if self.name.endswith(('.vcproj', '.vcxproj')):
- data = BOM + data
- try:
- txt = self.read(flags='rb')
- if txt != data:
- raise ValueError('must write')
- except (IOError, ValueError):
- self.write(data, flags=flags)
- else:
- Logs.debug('msvs: skipping %s', self.win32path())
- Node.Node.stealth_write = stealth_write
- re_win32 = re.compile(r'^([/\\]cygdrive)?[/\\]([a-z])([^a-z0-9_-].*)', re.I)
- def win32path(self):
- p = self.abspath()
- m = re_win32.match(p)
- if m:
- return "%s:%s" % (m.group(2).upper(), m.group(3))
- return p
- Node.Node.win32path = win32path
- re_quote = re.compile("[^a-zA-Z0-9-]")
- def quote(s):
- return re_quote.sub("_", s)
- def xml_escape(value):
- return value.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">")
- def make_uuid(v, prefix = None):
- """
- simple utility function
- """
- if isinstance(v, dict):
- keys = list(v.keys())
- keys.sort()
- tmp = str([(k, v[k]) for k in keys])
- else:
- tmp = str(v)
- d = Utils.md5(tmp.encode()).hexdigest().upper()
- if prefix:
- d = '%s%s' % (prefix, d[8:])
- gid = uuid.UUID(d, version = 4)
- return str(gid).upper()
- def diff(node, fromnode):
- # difference between two nodes, but with "(..)" instead of ".."
- c1 = node
- c2 = fromnode
- c1h = c1.height()
- c2h = c2.height()
- lst = []
- up = 0
- while c1h > c2h:
- lst.append(c1.name)
- c1 = c1.parent
- c1h -= 1
- while c2h > c1h:
- up += 1
- c2 = c2.parent
- c2h -= 1
- while id(c1) != id(c2):
- lst.append(c1.name)
- up += 1
- c1 = c1.parent
- c2 = c2.parent
- for i in range(up):
- lst.append('(..)')
- lst.reverse()
- return tuple(lst)
- class build_property(object):
- pass
- class vsnode(object):
- """
- Abstract class representing visual studio elements
- We assume that all visual studio nodes have a uuid and a parent
- """
- def __init__(self, ctx):
- self.ctx = ctx # msvs context
- self.name = '' # string, mandatory
- self.vspath = '' # path in visual studio (name for dirs, absolute path for projects)
- self.uuid = '' # string, mandatory
- self.parent = None # parent node for visual studio nesting
- def get_waf(self):
- """
- Override in subclasses...
- """
- return 'cd /d "%s" & %s' % (self.ctx.srcnode.win32path(), getattr(self.ctx, 'waf_command', 'waf.bat'))
- def ptype(self):
- """
- Return a special uuid for projects written in the solution file
- """
- pass
- def write(self):
- """
- Write the project file, by default, do nothing
- """
- pass
- def make_uuid(self, val):
- """
- Alias for creating uuid values easily (the templates cannot access global variables)
- """
- return make_uuid(val)
- class vsnode_vsdir(vsnode):
- """
- Nodes representing visual studio folders (which do not match the filesystem tree!)
- """
- VS_GUID_SOLUTIONFOLDER = "2150E333-8FDC-42A3-9474-1A3956D46DE8"
- def __init__(self, ctx, uuid, name, vspath=''):
- vsnode.__init__(self, ctx)
- self.title = self.name = name
- self.uuid = uuid
- self.vspath = vspath or name
- def ptype(self):
- return self.VS_GUID_SOLUTIONFOLDER
- class vsnode_project(vsnode):
- """
- Abstract class representing visual studio project elements
- A project is assumed to be writable, and has a node representing the file to write to
- """
- VS_GUID_VCPROJ = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
- def ptype(self):
- return self.VS_GUID_VCPROJ
- def __init__(self, ctx, node):
- vsnode.__init__(self, ctx)
- self.path = node
- self.uuid = make_uuid(node.win32path())
- self.name = node.name
- self.platform_toolset_ver = getattr(ctx, 'platform_toolset_ver', None)
- self.title = self.path.win32path()
- self.source = [] # list of node objects
- self.build_properties = [] # list of properties (nmake commands, output dir, etc)
- def dirs(self):
- """
- Get the list of parent folders of the source files (header files included)
- for writing the filters
- """
- lst = []
- def add(x):
- if x.height() > self.tg.path.height() and x not in lst:
- lst.append(x)
- add(x.parent)
- for x in self.source:
- add(x.parent)
- return lst
- def write(self):
- Logs.debug('msvs: creating %r', self.path)
- # first write the project file
- template1 = compile_template(PROJECT_TEMPLATE)
- proj_str = template1(self)
- proj_str = rm_blank_lines(proj_str)
- self.path.stealth_write(proj_str)
- # then write the filter
- template2 = compile_template(FILTER_TEMPLATE)
- filter_str = template2(self)
- filter_str = rm_blank_lines(filter_str)
- tmp = self.path.parent.make_node(self.path.name + '.filters')
- tmp.stealth_write(filter_str)
- def get_key(self, node):
- """
- required for writing the source files
- """
- name = node.name
- if name.endswith(('.cpp', '.c')):
- return 'ClCompile'
- return 'ClInclude'
- def collect_properties(self):
- """
- Returns a list of triplet (configuration, platform, output_directory)
- """
- ret = []
- for c in self.ctx.configurations:
- for p in self.ctx.platforms:
- x = build_property()
- x.outdir = ''
- x.configuration = c
- x.platform = p
- x.preprocessor_definitions = ''
- x.includes_search_path = ''
- # can specify "deploy_dir" too
- ret.append(x)
- self.build_properties = ret
- def get_build_params(self, props):
- opt = '--execsolution=%s' % self.ctx.get_solution_node().win32path()
- return (self.get_waf(), opt)
- def get_build_command(self, props):
- return "%s build %s" % self.get_build_params(props)
- def get_clean_command(self, props):
- return "%s clean %s" % self.get_build_params(props)
- def get_rebuild_command(self, props):
- return "%s clean build %s" % self.get_build_params(props)
- def get_filter_name(self, node):
- lst = diff(node, self.tg.path)
- return '\\'.join(lst) or '.'
- class vsnode_alias(vsnode_project):
- def __init__(self, ctx, node, name):
- vsnode_project.__init__(self, ctx, node)
- self.name = name
- self.output_file = ''
- class vsnode_build_all(vsnode_alias):
- """
- Fake target used to emulate the behaviour of "make all" (starting one process by target is slow)
- This is the only alias enabled by default
- """
- def __init__(self, ctx, node, name='build_all_projects'):
- vsnode_alias.__init__(self, ctx, node, name)
- self.is_active = True
- class vsnode_install_all(vsnode_alias):
- """
- Fake target used to emulate the behaviour of "make install"
- """
- def __init__(self, ctx, node, name='install_all_projects'):
- vsnode_alias.__init__(self, ctx, node, name)
- def get_build_command(self, props):
- return "%s build install %s" % self.get_build_params(props)
- def get_clean_command(self, props):
- return "%s clean %s" % self.get_build_params(props)
- def get_rebuild_command(self, props):
- return "%s clean build install %s" % self.get_build_params(props)
- class vsnode_project_view(vsnode_alias):
- """
- Fake target used to emulate a file system view
- """
- def __init__(self, ctx, node, name='project_view'):
- vsnode_alias.__init__(self, ctx, node, name)
- self.tg = self.ctx() # fake one, cannot remove
- self.exclude_files = Node.exclude_regs + '''
- waf-2*
- waf3-2*/**
- .waf-2*
- .waf3-2*/**
- **/*.sdf
- **/*.suo
- **/*.ncb
- **/%s
- ''' % Options.lockfile
- def collect_source(self):
- # this is likely to be slow
- self.source = self.ctx.srcnode.ant_glob('**', excl=self.exclude_files)
- def get_build_command(self, props):
- params = self.get_build_params(props) + (self.ctx.cmd,)
- return "%s %s %s" % params
- def get_clean_command(self, props):
- return ""
- def get_rebuild_command(self, props):
- return self.get_build_command(props)
- class vsnode_target(vsnode_project):
- """
- Visual studio project representing a targets (programs, libraries, etc) and bound
- to a task generator
- """
- def __init__(self, ctx, tg):
- """
- A project is more or less equivalent to a file/folder
- """
- base = getattr(ctx, 'projects_dir', None) or tg.path
- node = base.make_node(quote(tg.name) + ctx.project_extension) # the project file as a Node
- vsnode_project.__init__(self, ctx, node)
- self.name = quote(tg.name)
- self.tg = tg # task generator
- def get_build_params(self, props):
- """
- Override the default to add the target name
- """
- opt = '--execsolution=%s' % self.ctx.get_solution_node().win32path()
- if getattr(self, 'tg', None):
- opt += " --targets=%s" % self.tg.name
- return (self.get_waf(), opt)
- def collect_source(self):
- tg = self.tg
- source_files = tg.to_nodes(getattr(tg, 'source', []))
- include_dirs = Utils.to_list(getattr(tg, 'msvs_includes', []))
- include_files = []
- for x in include_dirs:
- if isinstance(x, str):
- x = tg.path.find_node(x)
- if x:
- lst = [y for y in x.ant_glob(HEADERS_GLOB, flat=False)]
- include_files.extend(lst)
- # remove duplicates
- self.source.extend(list(set(source_files + include_files)))
- self.source.sort(key=lambda x: x.win32path())
- def collect_properties(self):
- """
- Visual studio projects are associated with platforms and configurations (for building especially)
- """
- super(vsnode_target, self).collect_properties()
- for x in self.build_properties:
- x.outdir = self.path.parent.win32path()
- x.preprocessor_definitions = ''
- x.includes_search_path = ''
- try:
- tsk = self.tg.link_task
- except AttributeError:
- pass
- else:
- x.output_file = tsk.outputs[0].win32path()
- x.preprocessor_definitions = ';'.join(tsk.env.DEFINES)
- x.includes_search_path = ';'.join(self.tg.env.INCPATHS)
- class msvs_generator(BuildContext):
- '''generates a visual studio 2010 solution'''
- cmd = 'msvs'
- fun = 'build'
- numver = '11.00' # Visual Studio Version Number
- vsver = '2010' # Visual Studio Version Year
- platform_toolset_ver = 'v110' # Platform Toolset Version Number
- def init(self):
- """
- Some data that needs to be present
- """
- if not getattr(self, 'configurations', None):
- self.configurations = ['Release'] # LocalRelease, RemoteDebug, etc
- if not getattr(self, 'platforms', None):
- self.platforms = ['Win32']
- if not getattr(self, 'all_projects', None):
- self.all_projects = []
- if not getattr(self, 'project_extension', None):
- self.project_extension = '.vcxproj'
- if not getattr(self, 'projects_dir', None):
- self.projects_dir = self.srcnode.make_node('.depproj')
- self.projects_dir.mkdir()
- # bind the classes to the object, so that subclass can provide custom generators
- if not getattr(self, 'vsnode_vsdir', None):
- self.vsnode_vsdir = vsnode_vsdir
- if not getattr(self, 'vsnode_target', None):
- self.vsnode_target = vsnode_target
- if not getattr(self, 'vsnode_build_all', None):
- self.vsnode_build_all = vsnode_build_all
- if not getattr(self, 'vsnode_install_all', None):
- self.vsnode_install_all = vsnode_install_all
- if not getattr(self, 'vsnode_project_view', None):
- self.vsnode_project_view = vsnode_project_view
- self.numver = self.__class__.numver
- self.vsver = self.__class__.vsver
- self.platform_toolset_ver = self.__class__.platform_toolset_ver
- def execute(self):
- """
- Entry point
- """
- self.restore()
- if not self.all_envs:
- self.load_envs()
- self.recurse([self.run_dir])
- # user initialization
- self.init()
- # two phases for creating the solution
- self.collect_projects() # add project objects into "self.all_projects"
- self.write_files() # write the corresponding project and solution files
- def collect_projects(self):
- """
- Fill the list self.all_projects with project objects
- Fill the list of build targets
- """
- self.collect_targets()
- self.add_aliases()
- self.collect_dirs()
- default_project = getattr(self, 'default_project', None)
- def sortfun(x):
- if x.name == default_project:
- return ''
- return getattr(x, 'path', None) and x.path.win32path() or x.name
- self.all_projects.sort(key=sortfun)
- def write_files(self):
- """
- Write the project and solution files from the data collected
- so far. It is unlikely that you will want to change this
- """
- for p in self.all_projects:
- p.write()
- # and finally write the solution file
- node = self.get_solution_node()
- node.parent.mkdir()
- Logs.warn('Creating %r', node)
- template1 = compile_template(SOLUTION_TEMPLATE)
- sln_str = template1(self)
- sln_str = rm_blank_lines(sln_str)
- node.stealth_write(sln_str)
- def get_solution_node(self):
- """
- The solution filename is required when writing the .vcproj files
- return self.solution_node and if it does not exist, make one
- """
- try:
- return self.solution_node
- except AttributeError:
- pass
- solution_name = getattr(self, 'solution_name', None)
- if not solution_name:
- solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '.sln'
- if os.path.isabs(solution_name):
- self.solution_node = self.root.make_node(solution_name)
- else:
- self.solution_node = self.srcnode.make_node(solution_name)
- return self.solution_node
- def project_configurations(self):
- """
- Helper that returns all the pairs (config,platform)
- """
- ret = []
- for c in self.configurations:
- for p in self.platforms:
- ret.append((c, p))
- return ret
- def collect_targets(self):
- """
- Process the list of task generators
- """
- for g in self.groups:
- for tg in g:
- if not isinstance(tg, TaskGen.task_gen):
- continue
- if not hasattr(tg, 'msvs_includes'):
- tg.msvs_includes = tg.to_list(getattr(tg, 'includes', [])) + tg.to_list(getattr(tg, 'export_includes', []))
- tg.post()
- if not getattr(tg, 'link_task', None):
- continue
- p = self.vsnode_target(self, tg)
- p.collect_source() # delegate this processing
- p.collect_properties()
- self.all_projects.append(p)
- def add_aliases(self):
- """
- Add a specific target that emulates the "make all" necessary for Visual studio when pressing F7
- We also add an alias for "make install" (disabled by default)
- """
- base = getattr(self, 'projects_dir', None) or self.tg.path
- node_project = base.make_node('build_all_projects' + self.project_extension) # Node
- p_build = self.vsnode_build_all(self, node_project)
- p_build.collect_properties()
- self.all_projects.append(p_build)
- node_project = base.make_node('install_all_projects' + self.project_extension) # Node
- p_install = self.vsnode_install_all(self, node_project)
- p_install.collect_properties()
- self.all_projects.append(p_install)
- node_project = base.make_node('project_view' + self.project_extension) # Node
- p_view = self.vsnode_project_view(self, node_project)
- p_view.collect_source()
- p_view.collect_properties()
- self.all_projects.append(p_view)
- n = self.vsnode_vsdir(self, make_uuid(self.srcnode.win32path() + 'build_aliases'), "build_aliases")
- p_build.parent = p_install.parent = p_view.parent = n
- self.all_projects.append(n)
- def collect_dirs(self):
- """
- Create the folder structure in the Visual studio project view
- """
- seen = {}
- def make_parents(proj):
- # look at a project, try to make a parent
- if getattr(proj, 'parent', None):
- # aliases already have parents
- return
- x = proj.iter_path
- if x in seen:
- proj.parent = seen[x]
- return
- # There is not vsnode_vsdir for x.
- # So create a project representing the folder "x"
- n = proj.parent = seen[x] = self.vsnode_vsdir(self, make_uuid(x.win32path()), x.name)
- n.iter_path = x.parent
- self.all_projects.append(n)
- # recurse up to the project directory
- if x.height() > self.srcnode.height() + 1:
- make_parents(n)
- for p in self.all_projects[:]: # iterate over a copy of all projects
- if not getattr(p, 'tg', None):
- # but only projects that have a task generator
- continue
- # make a folder for each task generator
- p.iter_path = p.tg.path
- make_parents(p)
- def wrap_2008(cls):
- class dec(cls):
- def __init__(self, *k, **kw):
- cls.__init__(self, *k, **kw)
- self.project_template = PROJECT_2008_TEMPLATE
- def display_filter(self):
- root = build_property()
- root.subfilters = []
- root.sourcefiles = []
- root.source = []
- root.name = ''
- @Utils.run_once
- def add_path(lst):
- if not lst:
- return root
- child = build_property()
- child.subfilters = []
- child.sourcefiles = []
- child.source = []
- child.name = lst[-1]
- par = add_path(lst[:-1])
- par.subfilters.append(child)
- return child
- for x in self.source:
- # this crap is for enabling subclasses to override get_filter_name
- tmp = self.get_filter_name(x.parent)
- tmp = tmp != '.' and tuple(tmp.split('\\')) or ()
- par = add_path(tmp)
- par.source.append(x)
- def display(n):
- buf = []
- for x in n.source:
- buf.append('<File RelativePath="%s" FileType="%s"/>\n' % (xml_escape(x.win32path()), self.get_key(x)))
- for x in n.subfilters:
- buf.append('<Filter Name="%s">' % xml_escape(x.name))
- buf.append(display(x))
- buf.append('</Filter>')
- return '\n'.join(buf)
- return display(root)
- def get_key(self, node):
- """
- If you do not want to let visual studio use the default file extensions,
- override this method to return a value:
- 0: C/C++ Code, 1: C++ Class, 2: C++ Header File, 3: C++ Form,
- 4: C++ Control, 5: Text File, 6: DEF File, 7: IDL File,
- 8: Makefile, 9: RGS File, 10: RC File, 11: RES File, 12: XSD File,
- 13: XML File, 14: HTML File, 15: CSS File, 16: Bitmap, 17: Icon,
- 18: Resx File, 19: BSC File, 20: XSX File, 21: C++ Web Service,
- 22: ASAX File, 23: Asp Page, 24: Document, 25: Discovery File,
- 26: C# File, 27: eFileTypeClassDiagram, 28: MHTML Document,
- 29: Property Sheet, 30: Cursor, 31: Manifest, 32: eFileTypeRDLC
- """
- return ''
- def write(self):
- Logs.debug('msvs: creating %r', self.path)
- template1 = compile_template(self.project_template)
- proj_str = template1(self)
- proj_str = rm_blank_lines(proj_str)
- self.path.stealth_write(proj_str)
- return dec
- class msvs_2008_generator(msvs_generator):
- '''generates a visual studio 2008 solution'''
- cmd = 'msvs2008'
- fun = msvs_generator.fun
- numver = '10.00'
- vsver = '2008'
- def init(self):
- if not getattr(self, 'project_extension', None):
- self.project_extension = '_2008.vcproj'
- if not getattr(self, 'solution_name', None):
- self.solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '_2008.sln'
- if not getattr(self, 'vsnode_target', None):
- self.vsnode_target = wrap_2008(vsnode_target)
- if not getattr(self, 'vsnode_build_all', None):
- self.vsnode_build_all = wrap_2008(vsnode_build_all)
- if not getattr(self, 'vsnode_install_all', None):
- self.vsnode_install_all = wrap_2008(vsnode_install_all)
- if not getattr(self, 'vsnode_project_view', None):
- self.vsnode_project_view = wrap_2008(vsnode_project_view)
- msvs_generator.init(self)
- def options(ctx):
- """
- If the msvs option is used, try to detect if the build is made from visual studio
- """
- ctx.add_option('--execsolution', action='store', help='when building with visual studio, use a build state file')
- old = BuildContext.execute
- def override_build_state(ctx):
- def lock(rm, add):
- uns = ctx.options.execsolution.replace('.sln', rm)
- uns = ctx.root.make_node(uns)
- try:
- uns.delete()
- except OSError:
- pass
- uns = ctx.options.execsolution.replace('.sln', add)
- uns = ctx.root.make_node(uns)
- try:
- uns.write('')
- except EnvironmentError:
- pass
- if ctx.options.execsolution:
- ctx.launch_dir = Context.top_dir # force a build for the whole project (invalid cwd when called by visual studio)
- lock('.lastbuildstate', '.unsuccessfulbuild')
- old(ctx)
- lock('.unsuccessfulbuild', '.lastbuildstate')
- else:
- old(ctx)
- BuildContext.execute = override_build_state
|