1#!/usr/bin/env python
2#
3# Copyright (c) 2013-2015 The Khronos Group Inc.
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and/or associated documentation files (the
7# "Materials"), to deal in the Materials without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Materials, and to
10# permit persons to whom the Materials are furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be included
14# in all copies or substantial portions of the Materials.
15#
16# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
23
24import sys, time, pdb, string, cProfile
25from reg import *
26from generator import write, CGeneratorOptions, COutputGenerator, DocGeneratorOptions, DocOutputGenerator, PyOutputGenerator, ValidityOutputGenerator, HostSynchronizationOutputGenerator, ThreadGeneratorOptions, ThreadOutputGenerator
27from generator import ParamCheckerGeneratorOptions, ParamCheckerOutputGenerator
28
29# debug - start header generation in debugger
30# dump - dump registry after loading
31# profile - enable Python profiling
32# protect - whether to use #ifndef protections
33# registry <filename> - use specified XML registry instead of gl.xml
34# target - string name of target header, or all targets if None
35# timeit - time length of registry loading & header generation
36# validate - validate return & parameter group tags against <group>
37debug   = False
38dump    = False
39profile = False
40protect = True
41target  = None
42timeit  = False
43validate= False
44# Default input / log files
45errFilename = None
46diagFilename = 'diag.txt'
47regFilename = 'vk.xml'
48
49if __name__ == '__main__':
50    i = 1
51    while (i < len(sys.argv)):
52        arg = sys.argv[i]
53        i = i + 1
54        if (arg == '-debug'):
55            write('Enabling debug (-debug)', file=sys.stderr)
56            debug = True
57        elif (arg == '-dump'):
58            write('Enabling dump (-dump)', file=sys.stderr)
59            dump = True
60        elif (arg == '-noprotect'):
61            write('Disabling inclusion protection in output headers', file=sys.stderr)
62            protect = False
63        elif (arg == '-profile'):
64            write('Enabling profiling (-profile)', file=sys.stderr)
65            profile = True
66        elif (arg == '-registry'):
67            regFilename = sys.argv[i]
68            i = i+1
69            write('Using registry ', regFilename, file=sys.stderr)
70        elif (arg == '-time'):
71            write('Enabling timing (-time)', file=sys.stderr)
72            timeit = True
73        elif (arg == '-validate'):
74            write('Enabling group validation (-validate)', file=sys.stderr)
75            validate = True
76        elif (arg[0:1] == '-'):
77            write('Unrecognized argument:', arg, file=sys.stderr)
78            exit(1)
79        else:
80            target = arg
81            write('Using target', target, file=sys.stderr)
82
83# Simple timer functions
84startTime = None
85def startTimer():
86    global startTime
87    startTime = time.clock()
88def endTimer(msg):
89    global startTime
90    endTime = time.clock()
91    if (timeit):
92        write(msg, endTime - startTime)
93        startTime = None
94
95# Load & parse registry
96reg = Registry()
97
98startTimer()
99tree = etree.parse(regFilename)
100endTimer('Time to make ElementTree =')
101
102startTimer()
103reg.loadElementTree(tree)
104endTimer('Time to parse ElementTree =')
105
106if (validate):
107    reg.validateGroups()
108
109if (dump):
110    write('***************************************')
111    write('Performing Registry dump to regdump.txt')
112    write('***************************************')
113    reg.dumpReg(filehandle = open('regdump.txt','w'))
114
115# Turn a list of strings into a regexp string matching exactly those strings
116def makeREstring(list):
117    return '^(' + '|'.join(list) + ')$'
118
119# Descriptive names for various regexp patterns used to select
120# versions and extensions
121allVersions     = allExtensions = '.*'
122noVersions      = noExtensions = None
123
124# Copyright text prefixing all headers (list of strings).
125prefixStrings = [
126    '/*',
127    '** Copyright (c) 2015 The Khronos Group Inc.',
128    '**',
129    '** Permission is hereby granted, free of charge, to any person obtaining a',
130    '** copy of this software and/or associated documentation files (the',
131    '** "Materials"), to deal in the Materials without restriction, including',
132    '** without limitation the rights to use, copy, modify, merge, publish,',
133    '** distribute, sublicense, and/or sell copies of the Materials, and to',
134    '** permit persons to whom the Materials are furnished to do so, subject to',
135    '** the following conditions:',
136    '**',
137    '** The above copyright notice and this permission notice shall be included',
138    '** in all copies or substantial portions of the Materials.',
139    '**',
140    '** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,',
141    '** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF',
142    '** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.',
143    '** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY',
144    '** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,',
145    '** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE',
146    '** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.',
147    '*/',
148    ''
149]
150
151# Text specific to Vulkan headers
152vkPrefixStrings = [
153    '/*',
154    '** This header is generated from the Khronos Vulkan XML API Registry.',
155    '**',
156    '*/',
157    ''
158]
159
160# Defaults for generating re-inclusion protection wrappers (or not)
161protectFile = protect
162protectFeature = protect
163protectProto = protect
164
165buildList = [
166    # Vulkan 1.0 - core API + extensions. To generate just the core API,
167    # change to 'defaultExtensions = None' below.
168    [ COutputGenerator,
169      CGeneratorOptions(
170        filename          = 'include/vulkan/vulkan.h',
171        apiname           = 'vulkan',
172        profile           = None,
173        versions          = allVersions,
174        emitversions      = allVersions,
175        defaultExtensions = 'vulkan',
176        addExtensions     = None,
177        removeExtensions  = None,
178        prefixText        = prefixStrings + vkPrefixStrings,
179        genFuncPointers   = True,
180        protectFile       = protectFile,
181        protectFeature    = False,
182        protectProto      = '#ifndef',
183        protectProtoStr   = 'VK_NO_PROTOTYPES',
184        apicall           = 'VKAPI_ATTR ',
185        apientry          = 'VKAPI_CALL ',
186        apientryp         = 'VKAPI_PTR *',
187        alignFuncParam    = 48)
188    ],
189    # Vulkan 1.0 draft - core API include files for spec
190    # Overwrites include subdirectories in spec source tree
191    # The generated include files do not include the calling convention
192    # macros (apientry etc.), unlike the header files.
193    [ DocOutputGenerator,
194      DocGeneratorOptions(
195        filename          = 'vulkan-docs',
196        apiname           = 'vulkan',
197        profile           = None,
198        versions          = allVersions,
199        emitversions      = allVersions,
200        defaultExtensions = 'vulkan',
201        addExtensions     = None,
202        removeExtensions  = None,
203        prefixText        = prefixStrings + vkPrefixStrings,
204        apicall           = '',
205        apientry          = '',
206        apientryp         = '*',
207        genDirectory      = '../../doc/specs/vulkan',
208        alignFuncParam    = 48,
209        expandEnumerants  = False)
210    ],
211    # Vulkan 1.0 draft - API names to validate man/api spec includes & links
212    #    filename          = 'vkapi.py',
213    [ PyOutputGenerator,
214      DocGeneratorOptions(
215        filename          = '../../doc/specs/vulkan/vkapi.py',
216        apiname           = 'vulkan',
217        profile           = None,
218        versions          = allVersions,
219        emitversions      = allVersions,
220        defaultExtensions = None,
221        addExtensions     = None,
222        removeExtensions  = None)
223    ],
224    # Vulkan 1.0 draft - core API include files for spec
225    # Overwrites include subdirectories in spec source tree
226    [ ValidityOutputGenerator,
227      DocGeneratorOptions(
228        filename          = 'validity',
229        apiname           = 'vulkan',
230        profile           = None,
231        versions          = allVersions,
232        emitversions      = allVersions,
233        defaultExtensions = None,
234        addExtensions     = None,
235        removeExtensions  = None,
236        genDirectory      = '../../doc/specs/vulkan')
237    ],
238    # Vulkan 1.0 draft - core API include files for spec
239    # Overwrites include subdirectories in spec source tree
240    [ HostSynchronizationOutputGenerator,
241      DocGeneratorOptions(
242        filename          = 'hostsynctable',
243        apiname           = 'vulkan',
244        profile           = None,
245        versions          = allVersions,
246        emitversions      = allVersions,
247        defaultExtensions = None,
248        addExtensions     = None,
249        removeExtensions  = None,
250        genDirectory      = '../../doc/specs/vulkan')
251    ],
252    # Vulkan 1.0 draft - thread checking layer
253    [ ThreadOutputGenerator,
254      ThreadGeneratorOptions(
255        filename          = 'thread_check.h',
256        apiname           = 'vulkan',
257        profile           = None,
258        versions          = allVersions,
259        emitversions      = allVersions,
260        defaultExtensions = 'vulkan',
261        addExtensions     = None,
262        removeExtensions  = None,
263        prefixText        = prefixStrings + vkPrefixStrings,
264        genFuncPointers   = True,
265        protectFile       = protectFile,
266        protectFeature    = False,
267        protectProto      = True,
268        protectProtoStr   = 'VK_PROTOTYPES',
269        apicall           = '',
270        apientry          = 'VKAPI_CALL ',
271        apientryp         = 'VKAPI_PTR *',
272        alignFuncParam    = 48)
273    ],
274    [ ParamCheckerOutputGenerator,
275      ParamCheckerGeneratorOptions(
276        filename          = 'parameter_validation.h',
277        apiname           = 'vulkan',
278        profile           = None,
279        versions          = allVersions,
280        emitversions      = allVersions,
281        defaultExtensions = 'vulkan',
282        addExtensions     = None,
283        removeExtensions  = None,
284        prefixText        = prefixStrings + vkPrefixStrings,
285        genFuncPointers   = True,
286        protectFile       = protectFile,
287        protectFeature    = False,
288        protectProto      = None,
289        protectProtoStr   = 'VK_NO_PROTOTYPES',
290        apicall           = 'VKAPI_ATTR ',
291        apientry          = 'VKAPI_CALL ',
292        apientryp         = 'VKAPI_PTR *',
293        alignFuncParam    = 48)
294    ],
295    None
296]
297
298# create error/warning & diagnostic files
299if (errFilename):
300    errWarn = open(errFilename,'w')
301else:
302    errWarn = sys.stderr
303diag = open(diagFilename, 'w')
304
305def genHeaders():
306    # Loop over targets, building each
307    generated = 0
308    for item in buildList:
309        if (item == None):
310            break
311        createGenerator = item[0]
312        genOpts = item[1]
313        if (target and target != genOpts.filename):
314            # write('*** Skipping', genOpts.filename)
315            continue
316        write('*** Building', genOpts.filename)
317        generated = generated + 1
318        startTimer()
319        gen = createGenerator(errFile=errWarn,
320                              warnFile=errWarn,
321                              diagFile=diag)
322        reg.setGenerator(gen)
323        reg.apiGen(genOpts)
324        write('** Generated', genOpts.filename)
325        endTimer('Time to generate ' + genOpts.filename + ' =')
326    if (target and generated == 0):
327        write('Failed to generate target:', target)
328
329if (debug):
330    pdb.run('genHeaders()')
331elif (profile):
332    import cProfile, pstats
333    cProfile.run('genHeaders()', 'profile.txt')
334    p = pstats.Stats('profile.txt')
335    p.strip_dirs().sort_stats('time').print_stats(50)
336else:
337    genHeaders()
338