1#!/usr/bin/env python
2#
3# Copyright (c) 2013-2016 The Khronos Group Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import sys, time, pdb, string, cProfile
18from reg import *
19from generator import write, CGeneratorOptions, COutputGenerator, DocGeneratorOptions, DocOutputGenerator, PyOutputGenerator, ValidityOutputGenerator, HostSynchronizationOutputGenerator, ThreadGeneratorOptions, ThreadOutputGenerator
20from generator import ParamCheckerGeneratorOptions, ParamCheckerOutputGenerator
21
22# debug - start header generation in debugger
23# dump - dump registry after loading
24# profile - enable Python profiling
25# protect - whether to use #ifndef protections
26# registry <filename> - use specified XML registry instead of gl.xml
27# target - string name of target header, or all targets if None
28# timeit - time length of registry loading & header generation
29# validate - validate return & parameter group tags against <group>
30debug   = False
31dump    = False
32profile = False
33protect = True
34target  = None
35timeit  = False
36validate= False
37# Default input / log files
38errFilename = None
39diagFilename = 'diag.txt'
40regFilename = 'vk.xml'
41outDir = '.'
42
43if __name__ == '__main__':
44    i = 1
45    while (i < len(sys.argv)):
46        arg = sys.argv[i]
47        i = i + 1
48        if (arg == '-debug'):
49            write('Enabling debug (-debug)', file=sys.stderr)
50            debug = True
51        elif (arg == '-dump'):
52            write('Enabling dump (-dump)', file=sys.stderr)
53            dump = True
54        elif (arg == '-noprotect'):
55            write('Disabling inclusion protection in output headers', file=sys.stderr)
56            protect = False
57        elif (arg == '-profile'):
58            write('Enabling profiling (-profile)', file=sys.stderr)
59            profile = True
60        elif (arg == '-registry'):
61            regFilename = sys.argv[i]
62            i = i+1
63            write('Using registry ', regFilename, file=sys.stderr)
64        elif (arg == '-time'):
65            write('Enabling timing (-time)', file=sys.stderr)
66            timeit = True
67        elif (arg == '-validate'):
68            write('Enabling group validation (-validate)', file=sys.stderr)
69            validate = True
70        elif (arg == '-outdir'):
71            outDir = sys.argv[i]
72            i = i+1
73            write('Using output directory ', outDir, file=sys.stderr)
74        elif (arg[0:1] == '-'):
75            write('Unrecognized argument:', arg, file=sys.stderr)
76            exit(1)
77        else:
78            target = arg
79            write('Using target', target, file=sys.stderr)
80
81# Simple timer functions
82startTime = None
83def startTimer():
84    global startTime
85    startTime = time.clock()
86def endTimer(msg):
87    global startTime
88    endTime = time.clock()
89    if (timeit):
90        write(msg, endTime - startTime)
91        startTime = None
92
93# Load & parse registry
94reg = Registry()
95
96startTimer()
97tree = etree.parse(regFilename)
98endTimer('Time to make ElementTree =')
99
100startTimer()
101reg.loadElementTree(tree)
102endTimer('Time to parse ElementTree =')
103
104if (validate):
105    reg.validateGroups()
106
107if (dump):
108    write('***************************************')
109    write('Performing Registry dump to regdump.txt')
110    write('***************************************')
111    reg.dumpReg(filehandle = open('regdump.txt','w'))
112
113# Turn a list of strings into a regexp string matching exactly those strings
114def makeREstring(list):
115    return '^(' + '|'.join(list) + ')$'
116
117# Descriptive names for various regexp patterns used to select
118# versions and extensions
119allVersions     = allExtensions = '.*'
120noVersions      = noExtensions = None
121
122# Copyright text prefixing all headers (list of strings).
123prefixStrings = [
124    '/*',
125    '** Copyright (c) 2015-2016 The Khronos Group Inc.',
126    '**',
127    '** Licensed under the Apache License, Version 2.0 (the "License");',
128    '** you may not use this file except in compliance with the License.',
129    '** You may obtain a copy of the License at',
130    '**',
131    '**     http://www.apache.org/licenses/LICENSE-2.0',
132    '**',
133    '** Unless required by applicable law or agreed to in writing, software',
134    '** distributed under the License is distributed on an "AS IS" BASIS,',
135    '** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
136    '** See the License for the specific language governing permissions and',
137    '** limitations under the License.',
138    '*/',
139    ''
140]
141
142# Text specific to Vulkan headers
143vkPrefixStrings = [
144    '/*',
145    '** This header is generated from the Khronos Vulkan XML API Registry.',
146    '**',
147    '*/',
148    ''
149]
150
151# Defaults for generating re-inclusion protection wrappers (or not)
152protectFile = protect
153protectFeature = protect
154protectProto = protect
155
156buildList = [
157    # Vulkan 1.0 - header for core API + extensions.
158    # To generate just the core API,
159    # change to 'defaultExtensions = None' below.
160    [ COutputGenerator,
161      CGeneratorOptions(
162        filename          = 'include/vulkan/vulkan.h',
163        apiname           = 'vulkan',
164        profile           = None,
165        versions          = allVersions,
166        emitversions      = allVersions,
167        defaultExtensions = 'vulkan',
168        addExtensions     = None,
169        removeExtensions  = None,
170        prefixText        = prefixStrings + vkPrefixStrings,
171        genFuncPointers   = True,
172        protectFile       = protectFile,
173        protectFeature    = False,
174        protectProto      = '#ifndef',
175        protectProtoStr   = 'VK_NO_PROTOTYPES',
176        apicall           = 'VKAPI_ATTR ',
177        apientry          = 'VKAPI_CALL ',
178        apientryp         = 'VKAPI_PTR *',
179        alignFuncParam    = 48)
180    ],
181    # Vulkan 1.0 draft - API include files for spec and ref pages
182    # Overwrites include subdirectories in spec source tree
183    # The generated include files do not include the calling convention
184    # macros (apientry etc.), unlike the header files.
185    # Because the 1.0 core branch includes ref pages for extensions,
186    # all the extension interfaces need to be generated, even though
187    # none are used by the core spec itself.
188    [ DocOutputGenerator,
189      DocGeneratorOptions(
190        filename          = 'vulkan-docs',
191        apiname           = 'vulkan',
192        profile           = None,
193        versions          = allVersions,
194        emitversions      = allVersions,
195        defaultExtensions = None,
196        addExtensions     =
197            makeREstring([
198                'VK_KHR_sampler_mirror_clamp_to_edge',
199            ]),
200        removeExtensions  =
201            makeREstring([
202            ]),
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    [ PyOutputGenerator,
213      DocGeneratorOptions(
214        filename          = '../../doc/specs/vulkan/vkapi.py',
215        apiname           = 'vulkan',
216        profile           = None,
217        versions          = allVersions,
218        emitversions      = allVersions,
219        defaultExtensions = None,
220        addExtensions     =
221            makeREstring([
222                'VK_KHR_sampler_mirror_clamp_to_edge',
223            ]),
224        removeExtensions  =
225            makeREstring([
226            ]))
227    ],
228    # Vulkan 1.0 draft - core API validity files for spec
229    # Overwrites validity subdirectories in spec source tree
230    [ ValidityOutputGenerator,
231      DocGeneratorOptions(
232        filename          = 'validity',
233        apiname           = 'vulkan',
234        profile           = None,
235        versions          = allVersions,
236        emitversions      = allVersions,
237        defaultExtensions = None,
238        addExtensions     =
239            makeREstring([
240                'VK_KHR_sampler_mirror_clamp_to_edge',
241            ]),
242        removeExtensions  =
243            makeREstring([
244            ]),
245        genDirectory      = '../../doc/specs/vulkan')
246    ],
247    # Vulkan 1.0 draft - core API host sync table files for spec
248    # Overwrites subdirectory in spec source tree
249    [ HostSynchronizationOutputGenerator,
250      DocGeneratorOptions(
251        filename          = 'hostsynctable',
252        apiname           = 'vulkan',
253        profile           = None,
254        versions          = allVersions,
255        emitversions      = allVersions,
256        defaultExtensions = None,
257        addExtensions     =
258            makeREstring([
259                'VK_KHR_sampler_mirror_clamp_to_edge',
260            ]),
261        removeExtensions  =
262            makeREstring([
263            ]),
264        genDirectory      = '../../doc/specs/vulkan')
265    ],
266    # Vulkan 1.0 draft - thread checking layer
267    [ ThreadOutputGenerator,
268      ThreadGeneratorOptions(
269        filename          = 'thread_check.h',
270        apiname           = 'vulkan',
271        profile           = None,
272        versions          = allVersions,
273        emitversions      = allVersions,
274        defaultExtensions = 'vulkan',
275        addExtensions     = None,
276        removeExtensions  = None,
277        prefixText        = prefixStrings + vkPrefixStrings,
278        genFuncPointers   = True,
279        protectFile       = protectFile,
280        protectFeature    = False,
281        protectProto      = True,
282        protectProtoStr   = 'VK_PROTOTYPES',
283        apicall           = 'VKAPI_ATTR ',
284        apientry          = 'VKAPI_CALL ',
285        apientryp         = 'VKAPI_PTR *',
286        alignFuncParam    = 48,
287        genDirectory      = outDir)
288    ],
289    [ ParamCheckerOutputGenerator,
290      ParamCheckerGeneratorOptions(
291        filename          = 'parameter_validation.h',
292        apiname           = 'vulkan',
293        profile           = None,
294        versions          = allVersions,
295        emitversions      = allVersions,
296        defaultExtensions = 'vulkan',
297        addExtensions     = None,
298        removeExtensions  = None,
299        prefixText        = prefixStrings + vkPrefixStrings,
300        genFuncPointers   = True,
301        protectFile       = protectFile,
302        protectFeature    = False,
303        protectProto      = None,
304        protectProtoStr   = 'VK_NO_PROTOTYPES',
305        apicall           = 'VKAPI_ATTR ',
306        apientry          = 'VKAPI_CALL ',
307        apientryp         = 'VKAPI_PTR *',
308        alignFuncParam    = 48,
309        genDirectory      = outDir)
310    ],
311    None
312]
313
314# create error/warning & diagnostic files
315if (errFilename):
316    errWarn = open(errFilename,'w')
317else:
318    errWarn = sys.stderr
319diag = open(diagFilename, 'w')
320
321# check that output directory exists
322if (not os.path.isdir(outDir)):
323    write('Output directory does not exist: ', outDir)
324    raise
325
326def genHeaders():
327    # Loop over targets, building each
328    generated = 0
329    for item in buildList:
330        if (item == None):
331            break
332        createGenerator = item[0]
333        genOpts = item[1]
334        if (target and target != genOpts.filename):
335            # write('*** Skipping', genOpts.filename)
336            continue
337        write('*** Building', genOpts.filename)
338        generated = generated + 1
339        startTimer()
340        gen = createGenerator(errFile=errWarn,
341                              warnFile=errWarn,
342                              diagFile=diag)
343        reg.setGenerator(gen)
344        reg.apiGen(genOpts)
345        write('** Generated', genOpts.filename)
346        endTimer('Time to generate ' + genOpts.filename + ' =')
347    if (target and generated == 0):
348        write('Failed to generate target:', target)
349
350if (debug):
351    pdb.run('genHeaders()')
352elif (profile):
353    import cProfile, pstats
354    cProfile.run('genHeaders()', 'profile.txt')
355    p = pstats.Stats('profile.txt')
356    p.strip_dirs().sort_stats('time').print_stats(50)
357else:
358    genHeaders()
359