source: rtems-libbsd/waf_generator.py @ e1e10cd

5-freebsd-12freebsd-9.3
Last change on this file since e1e10cd was e1e10cd, checked in by Chris Johns <chrisj@…>, on Apr 23, 2016 at 7:37:27 AM

waf: Add the ability to set FreeBSD options on the configure command line.

Add --freebsd-options to add specific FreeBSD compile time options to
the build. This is a developer tool.

  • Property mode set to 100755
File size: 24.1 KB
Line 
1#
2#  Copyright (c) 2015-2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
3#
4#  Copyright (c) 2009-2015 embedded brains GmbH.  All rights reserved.
5#
6#   embedded brains GmbH
7#   Dornierstr. 4
8#   82178 Puchheim
9#   Germany
10#   <info@embedded-brains.de>
11#
12#  Copyright (c) 2012 OAR Corporation. All rights reserved.
13#
14#  Redistribution and use in source and binary forms, with or without
15#  modification, are permitted provided that the following conditions
16#  are met:
17#  1. Redistributions of source code must retain the above copyright
18#     notice, this list of conditions and the following disclaimer.
19#  2. Redistributions in binary form must reproduce the above copyright
20#     notice, this list of conditions and the following disclaimer in the
21#     documentation and/or other materials provided with the distribution.
22#
23#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27#  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29#  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30#  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31#  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35from __future__ import print_function
36
37import os
38import sys
39import tempfile
40
41import builder
42
43trace = False
44
45data = { }
46
47def _add_files(name, files):
48    if type(files) is not list:
49        files = [files]
50    if name not in data:
51        data[name] = []
52    data[name] += files
53
54def _clfags_includes(cflags, includes):
55    if type(cflags) is not list:
56        if cflags is not None:
57            _cflags = cflags.split(' ')
58        else:
59            _cflags = [None]
60    else:
61        _cflags = cflags
62    if type(includes) is not list:
63        _includes = [includes]
64    else:
65        _includes = includes
66    return _cflags, _includes
67
68class SourceFileFragmentComposer(builder.BuildSystemFragmentComposer):
69
70    def __init__(self, cflags = "default", includes = None):
71        self.cflags, self.includes = _clfags_includes(cflags, includes)
72
73    def compose(self, path):
74        if None in self.includes:
75            return ['sources', self.cflags], [path], self.cflags, self.includes
76        return ['sources', self.cflags + self.includes], [path], self.cflags, self.includes
77
78class TestFragementComposer(builder.BuildSystemFragmentComposer):
79
80    def __init__(self, testName, fileFragments, runTest = True, netTest = False):
81        self.testName = testName
82        self.fileFragments = fileFragments
83        self.runTest = runTest
84        self.netTest = netTest
85
86    def compose(self, path):
87        return ['tests', self.testName], { 'files': self.fileFragments,
88                                           'run': self.runTest,
89                                           'net': self.netTest }
90
91class KVMSymbolsFragmentComposer(builder.BuildSystemFragmentComposer):
92
93    def compose(self, path):
94        return ['KVMSymbols', 'files'], [path], self.includes
95
96class RPCGENFragmentComposer(builder.BuildSystemFragmentComposer):
97
98    def compose(self, path):
99        return ['RPCGen', 'files'], [path]
100
101class RouteKeywordsFragmentComposer(builder.BuildSystemFragmentComposer):
102
103    def compose(self, path):
104        return ['RouteKeywords', 'files'], [path]
105
106class LexFragmentComposer(builder.BuildSystemFragmentComposer):
107
108    def __init__(self, sym, dep, cflags = None, includes = None):
109        self.sym = sym
110        self.dep = dep
111        self.cflags, self.includes = _clfags_includes(cflags, includes)
112
113    def compose(self, path):
114        d = { 'file': path,
115              'sym': self.sym,
116              'dep': self.dep }
117        if None not in self.cflags:
118            d['cflags'] = self.cflags
119        if None not in self.includes:
120            d['includes'] = self.includes
121        return ['lex', path], d
122
123class YaccFragmentComposer(builder.BuildSystemFragmentComposer):
124
125    def __init__(self, sym, header, cflags = None, includes = None):
126        self.sym = sym
127        self.header = header
128        self.cflags, self.includes = _clfags_includes(cflags, includes)
129
130    def compose(self, path):
131        d = { 'file': path,
132              'sym': self.sym,
133              'header': self.header }
134        if None not in self.cflags:
135            d['cflags'] = self.cflags
136        if None not in self.includes:
137            d['includes'] = self.includes
138        return ['yacc', path], d
139
140# Module Manager - Collection of Modules
141class ModuleManager(builder.ModuleManager):
142
143    def restart(self):
144        self.script = ''
145
146    def add(self, line = ''):
147        self.script += line + os.linesep
148
149    def write(self):
150        try:
151            out = tempfile.NamedTemporaryFile(delete = False)
152            try:
153                out.write(bytes(self.script, sys.stdin.encoding))
154            except:
155                out.write(self.script)
156            out.close()
157            wscript = builder.RTEMS_DIR + '/libbsd_waf.py'
158            builder.processIfDifferent(out.name, wscript, "libbsd_waf.py")
159        finally:
160            try:
161                os.remove(out.name)
162            except:
163                pass
164
165    def setGenerators(self):
166        self.generator['convert'] = builder.Converter
167        self.generator['no-convert'] = builder.NoConverter
168
169        self.generator['file'] = builder.File
170
171        self.generator['path'] = builder.PathComposer
172        self.generator['freebsd-path'] = builder.FreeBSDPathComposer
173        self.generator['rtems-path'] = builder.RTEMSPathComposer
174        self.generator['cpu-path'] = builder.CPUDependentPathComposer
175        self.generator['target-src-cpu--path'] = builder.TargetSourceCPUDependentPathComposer
176
177        self.generator['source'] = SourceFileFragmentComposer
178        self.generator['test'] = TestFragementComposer
179        self.generator['kvm-symbols'] = KVMSymbolsFragmentComposer
180        self.generator['rpc-gen'] = RPCGENFragmentComposer
181        self.generator['route-keywords'] = RouteKeywordsFragmentComposer
182        self.generator['lex'] = LexFragmentComposer
183        self.generator['yacc'] = YaccFragmentComposer
184
185    def generate(self, rtems_version):
186
187        def _source_list(lhs, files, append = False):
188            if append:
189                adder = '+'
190                adder_space = ' '
191            else:
192                adder = ''
193                adder_space = ''
194            ll = len(lhs)
195            if len(files) == 1:
196                self.add('%s %s= [%r]' % (lhs, adder, files[0]))
197            elif len(files) == 2:
198                self.add('%s %s= [%r,' % (lhs, adder, files[0]))
199                self.add('%s %s   %r]' % (' ' * ll, adder_space, files[-1]))
200            elif len(files) > 0:
201                self.add('%s %s= [%r,' % (lhs, adder, files[0]))
202                for f in files[1:-1]:
203                    self.add('%s %s   %r,' % (' ' * ll, adder_space, f))
204                self.add('%s %s   %r]' % (' ' * ll, adder_space, files[-1]))
205
206        def _data_insert(data, cpu, frag):
207            #
208            # The default handler returns an empty string. Skip it.
209            #
210            if type(frag) is not str:
211                # Start at the top of the tree
212                d = data
213                path = frag[0]
214                if path[0] not in d:
215                    d[path[0]] = {}
216                # Select the sub-part of the tree as the compile options
217                # specialise how files are built.
218                d = d[path[0]]
219                if type(path[1]) is list:
220                    p = ' '.join(path[1])
221                else:
222                    p = path[1]
223                if p not in d:
224                    d[p] = {}
225                d = d[p]
226                if cpu not in d:
227                    d[cpu] = []
228                if type(frag[1]) is list:
229                    d[cpu] += frag[1]
230                else:
231                    d[cpu] = frag[1]
232                if len(frag) > 3:
233                    if 'cflags' not in d[cpu]:
234                        d['cflags'] = []
235                    d['cflags'] += frag[2]
236                if len(frag) >= 3 and None not in frag[-1]:
237                    if 'includes' not in d[cpu]:
238                        d['includes'] = []
239                    d['includes'] += frag[-1]
240
241        data = { }
242
243        for mn in self.getModules():
244            m = self[mn]
245            if m.conditionalOn == "none":
246                for f in m.files:
247                    _data_insert(data, 'all', f.getFragment())
248            for cpu, files in sorted(m.cpuDependentSourceFiles.items()):
249                for f in files:
250                    _data_insert(data, cpu, f.getFragment())
251
252        if trace:
253            import pprint
254            pprint.pprint(data)
255
256        self.restart()
257
258        self.add('#')
259        self.add('# RTEMS Project (https://www.rtems.org)')
260        self.add('#')
261        self.add('# Generated waf script. Do not edit, run ./freebsd-to-rtems.py -m')
262        self.add('#')
263        self.add('# To use see README.waf shipped with this file.')
264        self.add('#')
265        self.add('')
266        self.add('from __future__ import print_function')
267        self.add('')
268        self.add('import os.path')
269        self.add('import rtems_waf.rtems as rtems')
270        self.add('')
271        self.add('def init(ctx):')
272        self.add('    pass')
273        self.add('')
274        self.add('def options(opt):')
275        self.add('    pass')
276        self.add('')
277        self.add('def bsp_configure(conf, arch_bsp):')
278        self.add('    pass')
279        self.add('')
280        self.add('def configure(conf):')
281        self.add('    pass')
282        self.add('')
283        self.add('def build(bld):')
284        self.add('    # C/C++ flags')
285        self.add('    common_flags = []')
286        for f in builder.common_flags():
287            self.add('    common_flags += ["%s"]' % (f))
288        self.add('    if bld.env.WARNINGS:')
289        for f in builder.common_warnings():
290            self.add('        common_flags += ["%s"]' % (f))
291        self.add('    else:')
292        for f in builder.common_no_warnings():
293            self.add('        common_flags += ["%s"]' % (f))
294        self.add('    cflags = %r + common_flags' % (builder.cflags()))
295        self.add('    cxxflags = %r + common_flags' % (builder.cxxflags()))
296        self.add('')
297        self.add('    # Defines')
298        self.add('    defines = []')
299        self.add('    for o in bld.env.FREEBSD_OPTIONS.split(","):')
300        self.add('        defines += ["%s=1" % (o.strip().upper())]')
301        self.add('')
302        self.add('    # Include paths')
303        self.add('    includes = []')
304        self.add('    for i in %r:' % (builder.cpu_includes()))
305        self.add('        includes += ["%s" % (i[2:].replace("@CPU@", bld.get_env()["RTEMS_ARCH"]))]')
306        self.add('    if bld.get_env()["RTEMS_ARCH"] == "i386":')
307        self.add('        for i in %r:' % (builder.cpu_includes()))
308        self.add('            includes += ["%s" % (i[2:].replace("@CPU@", "x86"))]')
309        for i in builder.includes():
310            self.add('    includes += ["%s"]' % (i[2:]))
311        self.add('')
312        self.add('    # Support dummy PIC IRQ includes')
313        self.add('    if bld.get_env()["RTEMS_ARCH"] not in ("arm", "i386", "lm32", "mips", "powerpc", "sparc", "m68k"):')
314        self.add('        includes += ["rtems-dummy-pic-irq/include"]')
315        self.add('')
316
317        self.add('    # Collect the libbsd uses')
318        self.add('    libbsd_use = []')
319        self.add('')
320
321        #
322        # Support the existing Makefile based network configuration file.
323        #
324        self.add('    # Network test configuration')
325        self.add('    if not os.path.exists(bld.env.NET_CONFIG):')
326        self.add('        bld.fatal("network configuraiton \'%s\' not found" % (bld.env.NET_CONFIG))')
327        self.add('    net_cfg_self_ip = None')
328        self.add('    net_cfg_netmask = None')
329        self.add('    net_cfg_peer_ip = None')
330        self.add('    net_cfg_gateway_ip = None')
331        self.add('    net_tap_interface = None')
332        self.add('    try:')
333        self.add('        net_cfg_lines = open(bld.env.NET_CONFIG).readlines()')
334        self.add('    except:')
335        self.add('        bld.fatal("network configuraiton \'%s\' read failed" % (bld.env.NET_CONFIG))')
336        self.add('    lc = 0')
337        self.add('    for l in net_cfg_lines:')
338        self.add('        lc += 1')
339        self.add('        if l.strip().startswith("NET_CFG_"):')
340        self.add('            ls = l.split("=")')
341        self.add('            if len(ls) != 2:')
342        self.add('                bld.fatal("network configuraiton \'%s\' parse error: %d: %s" % ' + \
343                 '(bld.env.NET_CONFIG, lc, l))')
344        self.add('            lhs = ls[0].strip()')
345        self.add('            rhs = ls[1].strip()')
346        self.add('            if lhs == "NET_CFG_SELF_IP":')
347        self.add('                net_cfg_self_ip = rhs')
348        self.add('            if lhs == "NET_CFG_NETMASK":')
349        self.add('                net_cfg_netmask = rhs')
350        self.add('            if lhs == "NET_CFG_PEER_IP":')
351        self.add('                net_cfg_peer_ip = rhs')
352        self.add('            if lhs == "NET_CFG_GATEWAY_IP":')
353        self.add('                net_cfg_gateway_ip = rhs')
354        self.add('            if lhs == "NET_TAP_INTERFACE":')
355        self.add('                net_tap_interface = rhs')
356        self.add('    bld(target = "testsuite/include/rtems/bsd/test/network-config.h",')
357        self.add('        source = "testsuite/include/rtems/bsd/test/network-config.h.in",')
358        self.add('        rule = "sed -e \'s/@NET_CFG_SELF_IP@/%s/\' ' + \
359                 '-e \'s/@NET_CFG_NETMASK@/%s/\' ' + \
360                 '-e \'s/@NET_CFG_PEER_IP@/%s/\' ' + \
361                 '-e \'s/@NET_CFG_GATEWAY_IP@/%s/\' < ${SRC} > ${TGT}" % ' + \
362                 '(net_cfg_self_ip, net_cfg_netmask, net_cfg_peer_ip, net_cfg_gateway_ip),')
363        self.add('        update_outputs = True)')
364        self.add('')
365
366        #
367        # Add the specific rule based builders for generating files.
368        #
369        if 'KVMSymbols' in data:
370            kvmsymbols = data['KVMSymbols']
371            if 'includes' in kvmsymbols['files']:
372                includes = kvmsymbols['files']['includes']
373            else:
374                includes = []
375            self.add('    # KVM Symbols')
376            self.add('    bld(target = "%s",' % (kvmsymbols['files']['all'][0]))
377            self.add('        source = "rtemsbsd/rtems/generate_kvm_symbols",')
378            self.add('        rule = "./${SRC} > ${TGT}",')
379            self.add('        update_outputs = True)')
380            self.add('    bld.objects(target = "kvmsymbols",')
381            self.add('                features = "c",')
382            self.add('                cflags = cflags,')
383            self.add('                includes = %r + includes,' % (includes))
384            self.add('                source = "%s")' % (kvmsymbols['files']['all'][0]))
385            self.add('    libbsd_use += ["kvmsymbols"]')
386            self.add('')
387
388        self.add('    bld.add_group()')
389
390        if 'RPCGen' in data:
391            rpcgen = data['RPCGen']
392            rpcname = rpcgen['files']['all'][0][:-2]
393            self.add('    # RPC Generation')
394            self.add('    if bld.env.AUTO_REGEN:')
395            self.add('        bld(target = "%s.h",' % (rpcname))
396            self.add('            source = "%s.x",' % (rpcname))
397            self.add('            rule = "${RPCGEN} -h -o ${TGT} ${SRC}")')
398            self.add('')
399
400        if 'RouteKeywords' in data:
401            routekw = data['RouteKeywords']
402            rkwname = routekw['files']['all'][0]
403            self.add('    # Route keywords')
404            self.add('    if bld.env.AUTO_REGEN:')
405            self.add('        rkw_rule = "cat ${SRC} | ' + \
406                     'awk \'BEGIN { r = 0 } { if (NF == 1) ' + \
407                     'printf \\"#define\\\\tK_%%s\\\\t%%d\\\\n\\\\t{\\\\\\"%%s\\\\\\", K_%%s},\\\\n\\", ' + \
408                     'toupper($1), ++r, $1, toupper($1)}\' > ${TGT}"')
409            self.add('        bld(target = "%s.h",' % (rkwname))
410            self.add('            source = "%s",' % (rkwname))
411            self.add('            rule = rkw_rule)')
412            self.add('')
413
414        if 'lex' in data:
415            lexes = data['lex']
416            self.add('    # Lex')
417            for l in sorted(lexes.keys()):
418                lex = lexes[l]['all']
419                if 'cflags' in lex:
420                    lex_defines = [d[2:] for d in lex['cflags']]
421                else:
422                    lex_defines = []
423                if 'includes' in lex:
424                    lex_includes = lex['includes']
425                else:
426                    lex_includes = []
427                self.add('    if bld.env.AUTO_REGEN:')
428                self.add('        bld(target = "%s.c",' % (lex['file'][:-2]))
429                self.add('            source = "%s",' % (lex['file']))
430                self.add('            rule = "${LEX} -P %s -t ${SRC} | ' % (lex['sym']) + \
431                         'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' > ${TGT}")')
432                self.add('    bld.objects(target = "lex_%s",' % (lex['sym']))
433                self.add('                features = "c",')
434                self.add('                cflags = cflags,')
435                self.add('                includes = %r + includes,' % (lex_includes))
436                self.add('                defines = defines + %r,' % (lex_defines))
437                self.add('                source = "%s.c")' % (lex['file'][:-2]))
438                self.add('    libbsd_use += ["lex_%s"]' % (lex['sym']))
439                self.add('')
440
441        if 'yacc' in data:
442            yaccs = data['yacc']
443            self.add('    # Yacc')
444            for y in sorted(yaccs.keys()):
445                yacc = yaccs[y]['all']
446                yacc_file = yacc['file']
447                if yacc['sym'] is not None:
448                    yacc_sym = yacc['sym']
449                else:
450                    yacc_sym = os.path.basename(yacc_file)[:-2]
451                yacc_header = '%s/%s' % (os.path.dirname(yacc_file), yacc['header'])
452                if 'cflags' in yacc:
453                    yacc_defines = [d[2:] for d in yacc['cflags']]
454                else:
455                    yacc_defines = []
456                if 'includes' in yacc:
457                    yacc_includes = yacc['includes']
458                else:
459                    yacc_includes = []
460                self.add('    if bld.env.AUTO_REGEN:')
461                self.add('        bld(target = "%s.c",' % (yacc_file[:-2]))
462                self.add('            source = "%s",' % (yacc_file))
463                self.add('            rule = "${YACC} -b %s -d -p %s ${SRC} && ' % (yacc_sym, yacc_sym) + \
464                         'sed -e \'/YY_BUF_SIZE/s/16384/1024/\' < %s.tab.c > ${TGT} && ' % (yacc_sym) + \
465                         'rm -f %s.tab.c && mv %s.tab.h %s")' % (yacc_sym, yacc_sym, yacc_header))
466                self.add('    bld.objects(target = "yacc_%s",' % (yacc_sym))
467                self.add('                features = "c",')
468                self.add('                cflags = cflags,')
469                self.add('                includes = %r + includes,' % (yacc_includes))
470                self.add('                defines = defines + %r,' % (yacc_defines))
471                self.add('                source = "%s.c")' % (yacc_file[:-2]))
472                self.add('    libbsd_use += ["yacc_%s"]' % (yacc_sym))
473            self.add('')
474
475        #
476        # We have 'm' different sets of flags and there can be 'n' cpus
477        # specific files for those flags.
478        #
479        objs = 0
480        self.add('    # Objects built with different CFLAGS')
481        for flags in sorted(data['sources']):
482            if flags is not 'default':
483                objs += 1
484                _source_list('    objs%02d_source' % objs, sorted(data['sources'][flags]['all']))
485                archs = sorted(data['sources'][flags])
486                for arch in archs:
487                    if arch not in ['all', 'cflags', 'includes']:
488                        self.add('    if bld.get_env()["RTEMS_ARCH"] == "%s":' % arch)
489                        _source_list('        objs%02d_source' % objs,
490                                     sorted(data['sources'][flags][arch]),
491                                     append = True)
492                if 'cflags' in data['sources'][flags]:
493                    defines = [d[2:] for d in data['sources'][flags]['cflags']]
494                else:
495                    defines = []
496                if 'includes' in data['sources'][flags]:
497                    includes = data['sources'][flags]['includes']
498                else:
499                    includes = []
500                self.add('    bld.objects(target = "objs%02d",' % (objs))
501                self.add('                features = "c",')
502                self.add('                cflags = cflags,')
503                self.add('                includes = %r + includes,' % (includes))
504                self.add('                defines = defines + %r,' % (defines))
505                self.add('                source = objs%02d_source)' % objs)
506                self.add('    libbsd_use += ["objs%02d"]' % (objs))
507                self.add('')
508
509        #
510        # We hold the 'default' cflags set of files to the end to create the
511        # static library with.
512        #
513        _source_list('    source', sorted(data['sources']['default']['all']))
514        archs = sorted(data['sources']['default'])
515        for arch in archs:
516            if arch is not 'all':
517                self.add('    if bld.get_env()["RTEMS_ARCH"] == "%s":' % arch)
518                _source_list('        source',
519                             sorted(data['sources']['default'][arch]),
520                             append = True)
521        self.add('    bld.stlib(target = "bsd",')
522        self.add('              features = "c cxx",')
523        self.add('              cflags = cflags,')
524        self.add('              cxxflags = cxxflags,')
525        self.add('              includes = includes,')
526        self.add('              defines = defines,')
527        self.add('              source = source,')
528        self.add('              use = libbsd_use)')
529        self.add('')
530
531        #
532        # Head file collector.
533        #
534        self.add('    # Installs.    ')
535        self.add('    bld.install_files("${PREFIX}/" + rtems.arch_bsp_lib_path(bld.env.RTEMS_VERSION, bld.env.RTEMS_ARCH_BSP), ["libbsd.a"])')
536        header_paths = builder.header_paths()
537        self.add('    header_paths = [%s,' % (str(header_paths[0])))
538        for hp in header_paths[1:-1]:
539            self.add('                     %s,' % (str(hp)))
540        self.add('                     %s]' % (str(header_paths[-1])))
541        self.add('    for headers in header_paths:')
542        self.add('        ipath = os.path.join(rtems.arch_bsp_include_path(bld.env.RTEMS_VERSION, bld.env.RTEMS_ARCH_BSP), headers[2])')
543        self.add('        start_dir = bld.path.find_dir(headers[0])')
544        self.add('        bld.install_files("${PREFIX}/" + ipath,')
545        self.add('                          start_dir.ant_glob("**/" + headers[1]),')
546        self.add('                          cwd = start_dir,')
547        self.add('                          relative_trick = True)')
548        self.add('')
549
550        self.add('    # Tests')
551        tests = data['tests']
552        for test_name in sorted(tests):
553            files = ['testsuite/%s/%s.c' % (test_name, f) for f in  data['tests'][test_name]['all']['files']]
554            _source_list('    test_%s' % (test_name), sorted(files))
555            self.add('    bld.program(target = "%s.exe",' % (test_name))
556            self.add('                features = "cprogram",')
557            self.add('                cflags = cflags,')
558            self.add('                includes = includes,')
559            self.add('                source = test_%s,' % (test_name))
560            self.add('                use = ["bsd"],')
561            self.add('                lib = ["m", "z"],')
562            self.add('                install_path = None)')
563            self.add('')
564
565        self.write()
Note: See TracBrowser for help on using the repository browser.