1#! /usr/bin/env python
2# encoding: utf-8
3# CodeLite Project
4# Christian Klein (chrikle@berlios.de)
5# Created: Jan 2012
6# As templete for this file I used the msvs.py
7# I hope this template will work proper
8
9"""
10Redistribution and use in source and binary forms, with or without
11modification, are permitted provided that the following conditions
12are met:
13
141. Redistributions of source code must retain the above copyright
15   notice, this list of conditions and the following disclaimer.
16
172. Redistributions in binary form must reproduce the above copyright
18   notice, this list of conditions and the following disclaimer in the
19   documentation and/or other materials provided with the distribution.
20
213. The name of the author may not be used to endorse or promote products
22   derived from this software without specific prior written permission.
23
24THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
25IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34POSSIBILITY OF SUCH DAMAGE.
35"""
36
37"""
38
39
40To add this tool to your project:
41def options(conf):
42        opt.load('codelite')
43
44It can be a good idea to add the sync_exec tool too.
45
46To generate solution files:
47$ waf configure codelite
48
49To customize the outputs, provide subclasses in your wscript files:
50
51from waflib.extras import codelite
52class vsnode_target(codelite.vsnode_target):
53        def get_build_command(self, props):
54                # likely to be required
55                return "waf.bat build"
56        def collect_source(self):
57                # likely to be required
58                ...
59class codelite_bar(codelite.codelite_generator):
60        def init(self):
61                codelite.codelite_generator.init(self)
62                self.vsnode_target = vsnode_target
63
64The codelite class re-uses the same build() function for reading the targets (task generators),
65you may therefore specify codelite settings on the context object:
66
67def build(bld):
68        bld.codelite_solution_name = 'foo.workspace'
69        bld.waf_command = 'waf.bat'
70        bld.projects_dir = bld.srcnode.make_node('')
71        bld.projects_dir.mkdir()
72
73
74ASSUMPTIONS:
75* a project can be either a directory or a target, project files are written only for targets that have source files
76* each project is a vcxproj file, therefore the project uuid needs only to be a hash of the absolute path
77"""
78
79import os, re, sys
80import uuid # requires python 2.5
81from waflib.Build import BuildContext
82from waflib import Utils, TaskGen, Logs, Task, Context, Node, Options
83
84HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)'
85
86PROJECT_TEMPLATE = r'''<?xml version="1.0" encoding="utf-8"?>
87<CodeLite_Project Name="${project.name}" InternalType="Library">
88  <Plugins>
89    <Plugin Name="qmake">
90      <![CDATA[00010001N0005Release000000000000]]>
91    </Plugin>
92  </Plugins>
93  <Description/>
94  <Dependencies/>
95  <VirtualDirectory Name="src">
96  ${for x in project.source}
97  ${if (project.get_key(x)=="sourcefile")}
98  <File Name="${x.abspath()}"/>
99  ${endif}
100  ${endfor}
101  </VirtualDirectory>
102  <VirtualDirectory Name="include">
103  ${for x in project.source}
104  ${if (project.get_key(x)=="headerfile")}
105  <File Name="${x.abspath()}"/>
106  ${endif}
107  ${endfor}
108  </VirtualDirectory>
109  <Settings Type="Dynamic Library">
110    <GlobalSettings>
111      <Compiler Options="" C_Options="">
112        <IncludePath Value="."/>
113      </Compiler>
114      <Linker Options="">
115        <LibraryPath Value="."/>
116      </Linker>
117      <ResourceCompiler Options=""/>
118    </GlobalSettings>
119    <Configuration Name="Release" CompilerType="gnu gcc" ReleasegerType="GNU gdb Releaseger" Type="Dynamic Library" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">
120      <Compiler Options="" C_Options="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" UseDifferentPCHFlags="no" PCHFlags="">
121        <IncludePath Value="."/>
122        <IncludePath Value="."/>
123      </Compiler>
124      <Linker Options="" Required="yes">
125        <LibraryPath Value=""/>
126      </Linker>
127      <ResourceCompiler Options="" Required="no"/>
128      <General OutputFile="${xml:project.build_properties[0].output_file}" IntermediateDirectory="" Command="" CommandArguments="" PauseExecWhenProcTerminates="yes"/>
129      <Environment EnvVarSetName="&lt;Use Defaults&gt;" DbgSetName="&lt;Use Defaults&gt;">
130        <![CDATA[]]>
131      </Environment>
132      <Releaseger IsRemote="no" RemoteHostName="" RemoteHostPort="" ReleasegerPath="">
133        <PostConnectCommands/>
134        <StartupCommands/>
135      </Releaseger>
136      <PreBuild/>
137      <PostBuild/>
138      <CustomBuild Enabled="yes">
139        $b = project.build_properties[0]}
140        <RebuildCommand>${xml:project.get_rebuild_command(project.build_properties[0])}</RebuildCommand>
141        <CleanCommand>${xml:project.get_clean_command(project.build_properties[0])}</CleanCommand>
142        <BuildCommand>${xml:project.get_build_command(project.build_properties[0])}</BuildCommand>
143        <Target Name="Install">${xml:project.get_install_command(project.build_properties[0])}</Target>
144        <Target Name="Build and Install">${xml:project.get_build_and_install_command(project.build_properties[0])}</Target>
145        <Target Name="Build All">${xml:project.get_build_all_command(project.build_properties[0])}</Target>
146        <Target Name="Rebuild All">${xml:project.get_rebuild_all_command(project.build_properties[0])}</Target>
147        <Target Name="Clean All">${xml:project.get_clean_all_command(project.build_properties[0])}</Target>
148        <Target Name="Build and Install All">${xml:project.get_build_and_install_all_command(project.build_properties[0])}</Target>
149        <PreprocessFileCommand/>
150        <SingleFileCommand/>
151        <MakefileGenerationCommand/>
152        <ThirdPartyToolName>None</ThirdPartyToolName>
153        <WorkingDirectory/>
154      </CustomBuild>
155      <AdditionalRules>
156        <CustomPostBuild/>
157        <CustomPreBuild/>
158      </AdditionalRules>
159      <Completion>
160        <ClangCmpFlags/>
161        <ClangPP/>
162        <SearchPaths/>
163      </Completion>
164    </Configuration>
165    <Configuration Name="Release" CompilerType="gnu gcc" ReleasegerType="GNU gdb Releaseger" Type="" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">
166      <Compiler Options="" C_Options="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" UseDifferentPCHFlags="no" PCHFlags="">
167        <IncludePath Value="."/>
168      </Compiler>
169      <Linker Options="" Required="yes"/>
170      <ResourceCompiler Options="" Required="no"/>
171      <General OutputFile="" IntermediateDirectory="./Release" Command="" CommandArguments="" UseSeparateReleaseArgs="no" ReleaseArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes"/>
172      <Environment EnvVarSetName="&lt;Use Defaults&gt;" DbgSetName="&lt;Use Defaults&gt;">
173        <![CDATA[
174
175
176
177      ]]>
178      </Environment>
179      <Releaseger IsRemote="no" RemoteHostName="" RemoteHostPort="" ReleasegerPath="">
180        <PostConnectCommands/>
181        <StartupCommands/>
182      </Releaseger>
183      <PreBuild/>
184      <PostBuild/>
185      <CustomBuild Enabled="no">
186        <RebuildCommand/>
187        <CleanCommand/>
188        <BuildCommand/>
189        <PreprocessFileCommand/>
190        <SingleFileCommand/>
191        <MakefileGenerationCommand/>
192        <ThirdPartyToolName/>
193        <WorkingDirectory/>
194      </CustomBuild>
195      <AdditionalRules>
196        <CustomPostBuild/>
197        <CustomPreBuild/>
198      </AdditionalRules>
199      <Completion>
200        <ClangCmpFlags/>
201        <ClangPP/>
202        <SearchPaths/>
203      </Completion>
204    </Configuration>
205  </Settings>
206</CodeLite_Project>'''
207
208
209
210
211SOLUTION_TEMPLATE = '''<?xml version="1.0" encoding="utf-8"?>
212<CodeLite_Workspace Name="${getattr(project, 'codelite_solution_name', None)[:-10]}" Database="./${getattr(project, 'codelite_solution_name', None)[:-10]}.tags">
213${for p in project.all_projects}
214  <Project Name = "${p.name}" Path = "${p.title}" Active="No"/>
215${endfor}
216  <BuildMatrix>
217    <WorkspaceConfiguration Name="Release" Selected="yes">
218${for p in project.all_projects}
219      <Project Name="${p.name}" ConfigName="Release"/>
220${endfor}
221    </WorkspaceConfiguration>
222  </BuildMatrix>
223</CodeLite_Workspace>'''
224
225
226
227COMPILE_TEMPLATE = '''def f(project):
228        lst = []
229        def xml_escape(value):
230                return value.replace("&", "&amp;").replace('"', "&quot;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;")
231
232        %s
233
234        #f = open('cmd.txt', 'w')
235        #f.write(str(lst))
236        #f.close()
237        return ''.join(lst)
238'''
239reg_act = re.compile(r"(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<code>[^}]*?)\})", re.M)
240def compile_template(line):
241        """
242        Compile a template expression into a python function (like jsps, but way shorter)
243        """
244        extr = []
245        def repl(match):
246                g = match.group
247                if g('dollar'):
248                        return "$"
249                elif g('backslash'):
250                        return "\\"
251                elif g('subst'):
252                        extr.append(g('code'))
253                        return "<<|@|>>"
254                return None
255
256        line2 = reg_act.sub(repl, line)
257        params = line2.split('<<|@|>>')
258        assert(extr)
259
260
261        indent = 0
262        buf = []
263        app = buf.append
264
265        def app(txt):
266                buf.append(indent * '\t' + txt)
267
268        for x in range(len(extr)):
269                if params[x]:
270                        app("lst.append(%r)" % params[x])
271
272                f = extr[x]
273                if f.startswith(('if', 'for')):
274                        app(f + ':')
275                        indent += 1
276                elif f.startswith('py:'):
277                        app(f[3:])
278                elif f.startswith(('endif', 'endfor')):
279                        indent -= 1
280                elif f.startswith(('else', 'elif')):
281                        indent -= 1
282                        app(f + ':')
283                        indent += 1
284                elif f.startswith('xml:'):
285                        app('lst.append(xml_escape(%s))' % f[4:])
286                else:
287                        #app('lst.append((%s) or "cannot find %s")' % (f, f))
288                        app('lst.append(%s)' % f)
289
290        if extr:
291                if params[-1]:
292                        app("lst.append(%r)" % params[-1])
293
294        fun = COMPILE_TEMPLATE % "\n\t".join(buf)
295        #print(fun)
296        return Task.funex(fun)
297
298
299re_blank = re.compile('(\n|\r|\\s)*\n', re.M)
300def rm_blank_lines(txt):
301        txt = re_blank.sub('\r\n', txt)
302        return txt
303
304BOM = '\xef\xbb\xbf'
305try:
306        BOM = bytes(BOM, 'latin-1') # python 3
307except (TypeError, NameError):
308        pass
309
310def stealth_write(self, data, flags='wb'):
311        try:
312                unicode
313        except NameError:
314                data = data.encode('utf-8') # python 3
315        else:
316                data = data.decode(sys.getfilesystemencoding(), 'replace')
317                data = data.encode('utf-8')
318
319        if self.name.endswith('.project'):
320                data = BOM + data
321
322        try:
323                txt = self.read(flags='rb')
324                if txt != data:
325                        raise ValueError('must write')
326        except (IOError, ValueError):
327                self.write(data, flags=flags)
328        else:
329                Logs.debug('codelite: skipping %r', self)
330Node.Node.stealth_write = stealth_write
331
332re_quote = re.compile("[^a-zA-Z0-9-]")
333def quote(s):
334        return re_quote.sub("_", s)
335
336def xml_escape(value):
337        return value.replace("&", "&amp;").replace('"', "&quot;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;")
338
339def make_uuid(v, prefix = None):
340        """
341        simple utility function
342        """
343        if isinstance(v, dict):
344                keys = list(v.keys())
345                keys.sort()
346                tmp = str([(k, v[k]) for k in keys])
347        else:
348                tmp = str(v)
349        d = Utils.md5(tmp.encode()).hexdigest().upper()
350        if prefix:
351                d = '%s%s' % (prefix, d[8:])
352        gid = uuid.UUID(d, version = 4)
353        return str(gid).upper()
354
355def diff(node, fromnode):
356        # difference between two nodes, but with "(..)" instead of ".."
357        c1 = node
358        c2 = fromnode
359
360        c1h = c1.height()
361        c2h = c2.height()
362
363        lst = []
364        up = 0
365
366        while c1h > c2h:
367                lst.append(c1.name)
368                c1 = c1.parent
369                c1h -= 1
370
371        while c2h > c1h:
372                up += 1
373                c2 = c2.parent
374                c2h -= 1
375
376        while id(c1) != id(c2):
377                lst.append(c1.name)
378                up += 1
379
380                c1 = c1.parent
381                c2 = c2.parent
382
383        for i in range(up):
384                lst.append('(..)')
385        lst.reverse()
386        return tuple(lst)
387
388class build_property(object):
389        pass
390
391class vsnode(object):
392        """
393        Abstract class representing visual studio elements
394        We assume that all visual studio nodes have a uuid and a parent
395        """
396        def __init__(self, ctx):
397                self.ctx = ctx # codelite context
398                self.name = '' # string, mandatory
399                self.vspath = '' # path in visual studio (name for dirs, absolute path for projects)
400                self.uuid = '' # string, mandatory
401                self.parent = None # parent node for visual studio nesting
402
403        def get_waf(self):
404                """
405                Override in subclasses...
406                """
407                return '%s/%s' % (self.ctx.srcnode.abspath(), getattr(self.ctx, 'waf_command', 'waf'))
408
409        def ptype(self):
410                """
411                Return a special uuid for projects written in the solution file
412                """
413                pass
414
415        def write(self):
416                """
417                Write the project file, by default, do nothing
418                """
419                pass
420
421        def make_uuid(self, val):
422                """
423                Alias for creating uuid values easily (the templates cannot access global variables)
424                """
425                return make_uuid(val)
426
427class vsnode_vsdir(vsnode):
428        """
429        Nodes representing visual studio folders (which do not match the filesystem tree!)
430        """
431        VS_GUID_SOLUTIONFOLDER = "2150E333-8FDC-42A3-9474-1A3956D46DE8"
432        def __init__(self, ctx, uuid, name, vspath=''):
433                vsnode.__init__(self, ctx)
434                self.title = self.name = name
435                self.uuid = uuid
436                self.vspath = vspath or name
437
438        def ptype(self):
439                return self.VS_GUID_SOLUTIONFOLDER
440
441class vsnode_project(vsnode):
442        """
443        Abstract class representing visual studio project elements
444        A project is assumed to be writable, and has a node representing the file to write to
445        """
446        VS_GUID_VCPROJ = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
447        def ptype(self):
448                return self.VS_GUID_VCPROJ
449
450        def __init__(self, ctx, node):
451                vsnode.__init__(self, ctx)
452                self.path = node
453                self.uuid = make_uuid(node.abspath())
454                self.name = node.name
455                self.title = self.path.abspath()
456                self.source = [] # list of node objects
457                self.build_properties = [] # list of properties (nmake commands, output dir, etc)
458
459        def dirs(self):
460                """
461                Get the list of parent folders of the source files (header files included)
462                for writing the filters
463                """
464                lst = []
465                def add(x):
466                        if x.height() > self.tg.path.height() and x not in lst:
467                                lst.append(x)
468                                add(x.parent)
469                for x in self.source:
470                        add(x.parent)
471                return lst
472
473        def write(self):
474                Logs.debug('codelite: creating %r', self.path)
475                #print "self.name:",self.name
476
477                # first write the project file
478                template1 = compile_template(PROJECT_TEMPLATE)
479                proj_str = template1(self)
480                proj_str = rm_blank_lines(proj_str)
481                self.path.stealth_write(proj_str)
482
483                # then write the filter
484                #template2 = compile_template(FILTER_TEMPLATE)
485                #filter_str = template2(self)
486                #filter_str = rm_blank_lines(filter_str)
487                #tmp = self.path.parent.make_node(self.path.name + '.filters')
488                #tmp.stealth_write(filter_str)
489
490        def get_key(self, node):
491                """
492                required for writing the source files
493                """
494                name = node.name
495                if name.endswith(('.cpp', '.c')):
496                        return 'sourcefile'
497                return 'headerfile'
498
499        def collect_properties(self):
500                """
501                Returns a list of triplet (configuration, platform, output_directory)
502                """
503                ret = []
504                for c in self.ctx.configurations:
505                        for p in self.ctx.platforms:
506                                x = build_property()
507                                x.outdir = ''
508
509                                x.configuration = c
510                                x.platform = p
511
512                                x.preprocessor_definitions = ''
513                                x.includes_search_path = ''
514
515                                # can specify "deploy_dir" too
516                                ret.append(x)
517                self.build_properties = ret
518
519        def get_build_params(self, props):
520                opt = ''
521                return (self.get_waf(), opt)
522
523        def get_build_command(self, props):
524                return "%s build %s" % self.get_build_params(props)
525
526        def get_clean_command(self, props):
527                return "%s clean %s" % self.get_build_params(props)
528
529        def get_rebuild_command(self, props):
530                return "%s clean build %s" % self.get_build_params(props)
531
532        def get_install_command(self, props):
533                return "%s install %s" % self.get_build_params(props)
534        def get_build_and_install_command(self, props):
535                return "%s build install %s" % self.get_build_params(props)
536
537        def get_build_and_install_all_command(self, props):
538                return "%s build install" % self.get_build_params(props)[0]
539
540        def get_clean_all_command(self, props):
541                return "%s clean" % self.get_build_params(props)[0]
542
543        def get_build_all_command(self, props):
544                return "%s build" % self.get_build_params(props)[0]
545
546        def get_rebuild_all_command(self, props):
547                return "%s clean build" % self.get_build_params(props)[0]
548
549        def get_filter_name(self, node):
550                lst = diff(node, self.tg.path)
551                return '\\'.join(lst) or '.'
552
553class vsnode_alias(vsnode_project):
554        def __init__(self, ctx, node, name):
555                vsnode_project.__init__(self, ctx, node)
556                self.name = name
557                self.output_file = ''
558
559class vsnode_build_all(vsnode_alias):
560        """
561        Fake target used to emulate the behaviour of "make all" (starting one process by target is slow)
562        This is the only alias enabled by default
563        """
564        def __init__(self, ctx, node, name='build_all_projects'):
565                vsnode_alias.__init__(self, ctx, node, name)
566                self.is_active = True
567
568class vsnode_install_all(vsnode_alias):
569        """
570        Fake target used to emulate the behaviour of "make install"
571        """
572        def __init__(self, ctx, node, name='install_all_projects'):
573                vsnode_alias.__init__(self, ctx, node, name)
574
575        def get_build_command(self, props):
576                return "%s build install %s" % self.get_build_params(props)
577
578        def get_clean_command(self, props):
579                return "%s clean %s" % self.get_build_params(props)
580
581        def get_rebuild_command(self, props):
582                return "%s clean build install %s" % self.get_build_params(props)
583
584class vsnode_project_view(vsnode_alias):
585        """
586        Fake target used to emulate a file system view
587        """
588        def __init__(self, ctx, node, name='project_view'):
589                vsnode_alias.__init__(self, ctx, node, name)
590                self.tg = self.ctx() # fake one, cannot remove
591                self.exclude_files = Node.exclude_regs + '''
592waf-2*
593waf3-2*/**
594.waf-2*
595.waf3-2*/**
596**/*.sdf
597**/*.suo
598**/*.ncb
599**/%s
600                ''' % Options.lockfile
601
602        def collect_source(self):
603                # this is likely to be slow
604                self.source = self.ctx.srcnode.ant_glob('**', excl=self.exclude_files)
605
606        def get_build_command(self, props):
607                params = self.get_build_params(props) + (self.ctx.cmd,)
608                return "%s %s %s" % params
609
610        def get_clean_command(self, props):
611                return ""
612
613        def get_rebuild_command(self, props):
614                return self.get_build_command(props)
615
616class vsnode_target(vsnode_project):
617        """
618        CodeLite project representing a targets (programs, libraries, etc) and bound
619        to a task generator
620        """
621        def __init__(self, ctx, tg):
622                """
623                A project is more or less equivalent to a file/folder
624                """
625                base = getattr(ctx, 'projects_dir', None) or tg.path
626                node = base.make_node(quote(tg.name) + ctx.project_extension) # the project file as a Node
627                vsnode_project.__init__(self, ctx, node)
628                self.name = quote(tg.name)
629                self.tg     = tg  # task generator
630
631        def get_build_params(self, props):
632                """
633                Override the default to add the target name
634                """
635                opt = ''
636                if getattr(self, 'tg', None):
637                        opt += " --targets=%s" % self.tg.name
638                return (self.get_waf(), opt)
639
640        def collect_source(self):
641                tg = self.tg
642                source_files = tg.to_nodes(getattr(tg, 'source', []))
643                include_dirs = Utils.to_list(getattr(tg, 'codelite_includes', []))
644                include_files = []
645                for x in include_dirs:
646                        if isinstance(x, str):
647                                x = tg.path.find_node(x)
648                        if x:
649                                lst = [y for y in x.ant_glob(HEADERS_GLOB, flat=False)]
650                                include_files.extend(lst)
651
652                # remove duplicates
653                self.source.extend(list(set(source_files + include_files)))
654                self.source.sort(key=lambda x: x.abspath())
655
656        def collect_properties(self):
657                """
658                CodeLite projects are associated with platforms and configurations (for building especially)
659                """
660                super(vsnode_target, self).collect_properties()
661                for x in self.build_properties:
662                        x.outdir = self.path.parent.abspath()
663                        x.preprocessor_definitions = ''
664                        x.includes_search_path = ''
665
666                        try:
667                                tsk = self.tg.link_task
668                        except AttributeError:
669                                pass
670                        else:
671                                x.output_file = tsk.outputs[0].abspath()
672                                x.preprocessor_definitions = ';'.join(tsk.env.DEFINES)
673                                x.includes_search_path = ';'.join(self.tg.env.INCPATHS)
674
675class codelite_generator(BuildContext):
676        '''generates a CodeLite workspace'''
677        cmd = 'codelite'
678        fun = 'build'
679
680        def init(self):
681                """
682                Some data that needs to be present
683                """
684                if not getattr(self, 'configurations', None):
685                        self.configurations = ['Release'] # LocalRelease, RemoteDebug, etc
686                if not getattr(self, 'platforms', None):
687                        self.platforms = ['Win32']
688                if not getattr(self, 'all_projects', None):
689                        self.all_projects = []
690                if not getattr(self, 'project_extension', None):
691                        self.project_extension = '.project'
692                if not getattr(self, 'projects_dir', None):
693                        self.projects_dir = self.srcnode.make_node('')
694                        self.projects_dir.mkdir()
695
696                # bind the classes to the object, so that subclass can provide custom generators
697                if not getattr(self, 'vsnode_vsdir', None):
698                        self.vsnode_vsdir = vsnode_vsdir
699                if not getattr(self, 'vsnode_target', None):
700                        self.vsnode_target = vsnode_target
701                if not getattr(self, 'vsnode_build_all', None):
702                        self.vsnode_build_all = vsnode_build_all
703                if not getattr(self, 'vsnode_install_all', None):
704                        self.vsnode_install_all = vsnode_install_all
705                if not getattr(self, 'vsnode_project_view', None):
706                        self.vsnode_project_view = vsnode_project_view
707
708                self.numver = '11.00'
709                self.vsver  = '2010'
710
711        def execute(self):
712                """
713                Entry point
714                """
715                self.restore()
716                if not self.all_envs:
717                        self.load_envs()
718                self.recurse([self.run_dir])
719
720                # user initialization
721                self.init()
722
723                # two phases for creating the solution
724                self.collect_projects() # add project objects into "self.all_projects"
725                self.write_files() # write the corresponding project and solution files
726
727        def collect_projects(self):
728                """
729                Fill the list self.all_projects with project objects
730                Fill the list of build targets
731                """
732                self.collect_targets()
733                #self.add_aliases()
734                #self.collect_dirs()
735                default_project = getattr(self, 'default_project', None)
736                def sortfun(x):
737                        if x.name == default_project:
738                                return ''
739                        return getattr(x, 'path', None) and x.path.abspath() or x.name
740                self.all_projects.sort(key=sortfun)
741
742        def write_files(self):
743                """
744                Write the project and solution files from the data collected
745                so far. It is unlikely that you will want to change this
746                """
747                for p in self.all_projects:
748                        p.write()
749
750                # and finally write the solution file
751                node = self.get_solution_node()
752                node.parent.mkdir()
753                Logs.warn('Creating %r', node)
754                #a = dir(self.root)
755                #for b in a:
756                #        print b
757                #print self.group_names
758                #print "Hallo2:   ",self.root.listdir()
759                #print getattr(self, 'codelite_solution_name', None)
760                template1 = compile_template(SOLUTION_TEMPLATE)
761                sln_str = template1(self)
762                sln_str = rm_blank_lines(sln_str)
763                node.stealth_write(sln_str)
764
765        def get_solution_node(self):
766                """
767                The solution filename is required when writing the .vcproj files
768                return self.solution_node and if it does not exist, make one
769                """
770                try:
771                        return self.solution_node
772                except:
773                        pass
774
775                codelite_solution_name = getattr(self, 'codelite_solution_name', None)
776                if not codelite_solution_name:
777                        codelite_solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '.workspace'
778                        setattr(self, 'codelite_solution_name', codelite_solution_name)
779                if os.path.isabs(codelite_solution_name):
780                        self.solution_node = self.root.make_node(codelite_solution_name)
781                else:
782                        self.solution_node = self.srcnode.make_node(codelite_solution_name)
783                return self.solution_node
784
785        def project_configurations(self):
786                """
787                Helper that returns all the pairs (config,platform)
788                """
789                ret = []
790                for c in self.configurations:
791                        for p in self.platforms:
792                                ret.append((c, p))
793                return ret
794
795        def collect_targets(self):
796                """
797                Process the list of task generators
798                """
799                for g in self.groups:
800                        for tg in g:
801                                if not isinstance(tg, TaskGen.task_gen):
802                                        continue
803
804                                if not hasattr(tg, 'codelite_includes'):
805                                        tg.codelite_includes = tg.to_list(getattr(tg, 'includes', [])) + tg.to_list(getattr(tg, 'export_includes', []))
806                                tg.post()
807                                if not getattr(tg, 'link_task', None):
808                                        continue
809
810                                p = self.vsnode_target(self, tg)
811                                p.collect_source() # delegate this processing
812                                p.collect_properties()
813                                self.all_projects.append(p)
814
815        def add_aliases(self):
816                """
817                Add a specific target that emulates the "make all" necessary for Visual studio when pressing F7
818                We also add an alias for "make install" (disabled by default)
819                """
820                base = getattr(self, 'projects_dir', None) or self.tg.path
821
822                node_project = base.make_node('build_all_projects' + self.project_extension) # Node
823                p_build = self.vsnode_build_all(self, node_project)
824                p_build.collect_properties()
825                self.all_projects.append(p_build)
826
827                node_project = base.make_node('install_all_projects' + self.project_extension) # Node
828                p_install = self.vsnode_install_all(self, node_project)
829                p_install.collect_properties()
830                self.all_projects.append(p_install)
831
832                node_project = base.make_node('project_view' + self.project_extension) # Node
833                p_view = self.vsnode_project_view(self, node_project)
834                p_view.collect_source()
835                p_view.collect_properties()
836                self.all_projects.append(p_view)
837
838                n = self.vsnode_vsdir(self, make_uuid(self.srcnode.abspath() + 'build_aliases'), "build_aliases")
839                p_build.parent = p_install.parent = p_view.parent = n
840                self.all_projects.append(n)
841
842        def collect_dirs(self):
843                """
844                Create the folder structure in the CodeLite project view
845                """
846                seen = {}
847                def make_parents(proj):
848                        # look at a project, try to make a parent
849                        if getattr(proj, 'parent', None):
850                                # aliases already have parents
851                                return
852                        x = proj.iter_path
853                        if x in seen:
854                                proj.parent = seen[x]
855                                return
856
857                        # There is not vsnode_vsdir for x.
858                        # So create a project representing the folder "x"
859                        n = proj.parent = seen[x] = self.vsnode_vsdir(self, make_uuid(x.abspath()), x.name)
860                        n.iter_path = x.parent
861                        self.all_projects.append(n)
862
863                        # recurse up to the project directory
864                        if x.height() > self.srcnode.height() + 1:
865                                make_parents(n)
866
867                for p in self.all_projects[:]: # iterate over a copy of all projects
868                        if not getattr(p, 'tg', None):
869                                # but only projects that have a task generator
870                                continue
871
872                        # make a folder for each task generator
873                        p.iter_path = p.tg.path
874                        make_parents(p)
875
876