• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2#
3# Copyright (c) 2013-2020 The Khronos Group Inc.
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import argparse
8import pdb
9import re
10import sys
11import time
12import xml.etree.ElementTree as etree
13
14from cgenerator import CGeneratorOptions, COutputGenerator
15from cerealgenerator import CerealGenerator
16from docgenerator import DocGeneratorOptions, DocOutputGenerator
17from extensionmetadocgenerator import (ExtensionMetaDocGeneratorOptions,
18                                       ExtensionMetaDocOutputGenerator)
19from interfacedocgenerator import InterfaceDocGenerator
20from generator import write
21from spirvcapgenerator import SpirvCapabilityOutputGenerator
22from hostsyncgenerator import HostSynchronizationOutputGenerator
23from pygenerator import PyOutputGenerator
24from reflib import logDiag, logWarn, setLogFile
25from reg import Registry
26from validitygenerator import ValidityOutputGenerator
27from vkconventions import VulkanConventions
28
29
30# Simple timer functions
31startTime = None
32
33
34def startTimer(timeit):
35    global startTime
36    if timeit:
37        startTime = time.process_time()
38
39
40def endTimer(timeit, msg):
41    global startTime
42    if timeit:
43        endTime = time.process_time()
44        logDiag(msg, endTime - startTime)
45        startTime = None
46
47
48def makeREstring(strings, default=None, strings_are_regex=False):
49    """Turn a list of strings into a regexp string matching exactly those strings."""
50    if strings or default is None:
51        if not strings_are_regex:
52            strings = (re.escape(s) for s in strings)
53        return '^(' + '|'.join(strings) + ')$'
54    return default
55
56
57def makeGenOpts(args):
58    """Returns a directory of [ generator function, generator options ] indexed
59    by specified short names. The generator options incorporate the following
60    parameters:
61
62    args is an parsed argument object; see below for the fields that are used."""
63    global genOpts
64    genOpts = {}
65
66    # Default class of extensions to include, or None
67    defaultExtensions = args.defaultExtensions
68
69    # Additional extensions to include (list of extensions)
70    extensions = args.extension
71
72    # Extensions to remove (list of extensions)
73    removeExtensions = args.removeExtensions
74
75    # Extensions to emit (list of extensions)
76    emitExtensions = args.emitExtensions
77
78    # SPIR-V capabilities / features to emit (list of extensions & capabilities)
79    emitSpirv = args.emitSpirv
80
81    # Features to include (list of features)
82    features = args.feature
83
84    # Whether to disable inclusion protect in headers
85    protect = args.protect
86
87    # Output target directory
88    directory = args.directory
89
90    # Path to generated files, particularly api.py
91    genpath = args.genpath
92
93    # Descriptive names for various regexp patterns used to select
94    # versions and extensions
95    allSpirv = allFeatures = allExtensions = r'.*'
96
97    # Turn lists of names/patterns into matching regular expressions
98    addExtensionsPat     = makeREstring(extensions, None)
99    removeExtensionsPat  = makeREstring(removeExtensions, None)
100    emitExtensionsPat    = makeREstring(emitExtensions, allExtensions)
101    emitSpirvPat         = makeREstring(emitSpirv, allSpirv)
102    featuresPat          = makeREstring(features, allFeatures)
103
104    # Copyright text prefixing all headers (list of strings).
105    # The SPDX formatting below works around constraints of the 'reuse' tool
106    prefixStrings = [
107        '/*',
108        '** Copyright (c) 2015-2020 The Khronos Group Inc.',
109        '**',
110        '** SPDX' + '-License-Identifier: Apache-2.0',
111        '*/',
112        ''
113    ]
114
115    # Text specific to Vulkan headers
116    vkPrefixStrings = [
117        '/*',
118        '** This header is generated from the Khronos Vulkan XML API Registry.',
119        '**',
120        '*/',
121        ''
122    ]
123
124    # Defaults for generating re-inclusion protection wrappers (or not)
125    protectFile = protect
126
127    # An API style conventions object
128    conventions = VulkanConventions()
129
130    # API include files for spec and ref pages
131    # Overwrites include subdirectories in spec source tree
132    # The generated include files do not include the calling convention
133    # macros (apientry etc.), unlike the header files.
134    # Because the 1.0 core branch includes ref pages for extensions,
135    # all the extension interfaces need to be generated, even though
136    # none are used by the core spec itself.
137    genOpts['apiinc'] = [
138          DocOutputGenerator,
139          DocGeneratorOptions(
140            conventions       = conventions,
141            filename          = 'timeMarker',
142            directory         = directory,
143            genpath           = genpath,
144            apiname           = 'vulkan',
145            profile           = None,
146            versions          = featuresPat,
147            emitversions      = featuresPat,
148            defaultExtensions = None,
149            addExtensions     = addExtensionsPat,
150            removeExtensions  = removeExtensionsPat,
151            emitExtensions    = emitExtensionsPat,
152            prefixText        = prefixStrings + vkPrefixStrings,
153            apicall           = '',
154            apientry          = '',
155            apientryp         = '*',
156            alignFuncParam    = 48,
157            expandEnumerants  = False)
158        ]
159
160    # Python representation of API information, used by scripts that
161    # don't need to load the full XML.
162    genOpts['api.py'] = [
163          PyOutputGenerator,
164          DocGeneratorOptions(
165            conventions       = conventions,
166            filename          = 'api.py',
167            directory         = directory,
168            genpath           = None,
169            apiname           = 'vulkan',
170            profile           = None,
171            versions          = featuresPat,
172            emitversions      = featuresPat,
173            defaultExtensions = None,
174            addExtensions     = addExtensionsPat,
175            removeExtensions  = removeExtensionsPat,
176            emitExtensions    = emitExtensionsPat,
177            reparentEnums     = False)
178        ]
179
180    # API validity files for spec
181    genOpts['validinc'] = [
182          ValidityOutputGenerator,
183          DocGeneratorOptions(
184            conventions       = conventions,
185            filename          = 'timeMarker',
186            directory         = directory,
187            genpath           = None,
188            apiname           = 'vulkan',
189            profile           = None,
190            versions          = featuresPat,
191            emitversions      = featuresPat,
192            defaultExtensions = None,
193            addExtensions     = addExtensionsPat,
194            removeExtensions  = removeExtensionsPat,
195            emitExtensions    = emitExtensionsPat)
196        ]
197
198    # Serializer for spec
199    genOpts['cereal'] = [
200            CerealGenerator,
201            CGeneratorOptions(
202                conventions       = conventions,
203                filename          = "CMakeLists.txt",
204                directory         = directory,
205                apiname           = 'vulkan',
206                profile           = None,
207                versions          = featuresPat,
208                emitversions      = featuresPat,
209                defaultExtensions = defaultExtensions,
210                addExtensions     = None,
211                removeExtensions  = removeExtensionsPat,
212                emitExtensions    = emitExtensionsPat,
213                prefixText        = prefixStrings + vkPrefixStrings,
214                genFuncPointers   = True,
215                protectFile       = protectFile,
216                protectFeature    = False,
217                protectProto      = '#ifndef',
218                protectProtoStr   = 'VK_NO_PROTOTYPES',
219                apicall           = 'VKAPI_ATTR ',
220                apientry          = 'VKAPI_CALL ',
221                apientryp         = 'VKAPI_PTR *',
222                alignFuncParam    = 48)
223        ]
224
225    # API host sync table files for spec
226    genOpts['hostsyncinc'] = [
227          HostSynchronizationOutputGenerator,
228          DocGeneratorOptions(
229            conventions       = conventions,
230            filename          = 'timeMarker',
231            directory         = directory,
232            genpath           = None,
233            apiname           = 'vulkan',
234            profile           = None,
235            versions          = featuresPat,
236            emitversions      = featuresPat,
237            defaultExtensions = None,
238            addExtensions     = addExtensionsPat,
239            removeExtensions  = removeExtensionsPat,
240            emitExtensions    = emitExtensionsPat,
241            reparentEnums     = False)
242        ]
243
244    # Extension metainformation for spec extension appendices
245    # Includes all extensions by default, but only so that the generated
246    # 'promoted_extensions_*' files refer to all extensions that were
247    # promoted to a core version.
248    genOpts['extinc'] = [
249          ExtensionMetaDocOutputGenerator,
250          ExtensionMetaDocGeneratorOptions(
251            conventions       = conventions,
252            filename          = 'timeMarker',
253            directory         = directory,
254            genpath           = None,
255            apiname           = 'vulkan',
256            profile           = None,
257            versions          = featuresPat,
258            emitversions      = None,
259            defaultExtensions = defaultExtensions,
260            addExtensions     = addExtensionsPat,
261            removeExtensions  = None,
262            emitExtensions    = emitExtensionsPat)
263        ]
264
265    # Version and extension interface docs for version/extension appendices
266    # Includes all extensions by default.
267    genOpts['interfaceinc'] = [
268          InterfaceDocGenerator,
269          DocGeneratorOptions(
270            conventions       = conventions,
271            filename          = 'timeMarker',
272            directory         = directory,
273            genpath           = None,
274            apiname           = 'vulkan',
275            profile           = None,
276            versions          = featuresPat,
277            emitversions      = featuresPat,
278            defaultExtensions = None,
279            addExtensions     = addExtensionsPat,
280            removeExtensions  = removeExtensionsPat,
281            emitExtensions    = emitExtensionsPat,
282            reparentEnums     = False)
283        ]
284
285    genOpts['spirvcapinc'] = [
286          SpirvCapabilityOutputGenerator,
287          DocGeneratorOptions(
288            conventions       = conventions,
289            filename          = 'timeMarker',
290            directory         = directory,
291            genpath           = None,
292            apiname           = 'vulkan',
293            profile           = None,
294            versions          = featuresPat,
295            emitversions      = featuresPat,
296            defaultExtensions = None,
297            addExtensions     = addExtensionsPat,
298            removeExtensions  = removeExtensionsPat,
299            emitExtensions    = emitExtensionsPat,
300            emitSpirv         = emitSpirvPat,
301            reparentEnums     = False)
302        ]
303
304    # Platform extensions, in their own header files
305    # Each element of the platforms[] array defines information for
306    # generating a single platform:
307    #   [0] is the generated header file name
308    #   [1] is the set of platform extensions to generate
309    #   [2] is additional extensions whose interfaces should be considered,
310    #   but suppressed in the output, to avoid duplicate definitions of
311    #   dependent types like VkDisplayKHR and VkSurfaceKHR which come from
312    #   non-platform extensions.
313
314    # Track all platform extensions, for exclusion from vulkan_core.h
315    allPlatformExtensions = []
316
317    # Extensions suppressed for all WSI platforms (WSI extensions required
318    # by all platforms)
319    commonSuppressExtensions = [ 'VK_KHR_display', 'VK_KHR_swapchain' ]
320
321    # Extensions required and suppressed for beta "platform". This can
322    # probably eventually be derived from the requires= attributes of
323    # the extension blocks.
324    betaRequireExtensions = [ 'VK_KHR_portability_subset' ]
325    betaSuppressExtensions = []
326
327    platforms = [
328        [ 'vulkan_android.h',     [ 'VK_KHR_android_surface',
329                                    'VK_ANDROID_external_memory_android_hardware_buffer'
330                                                                  ], commonSuppressExtensions ],
331        [ 'vulkan_fuchsia.h',     [ 'VK_FUCHSIA_imagepipe_surface'], commonSuppressExtensions ],
332        [ 'vulkan_ggp.h',         [ 'VK_GGP_stream_descriptor_surface',
333                                    'VK_GGP_frame_token'          ], commonSuppressExtensions ],
334        [ 'vulkan_ios.h',         [ 'VK_MVK_ios_surface'          ], commonSuppressExtensions ],
335        [ 'vulkan_macos.h',       [ 'VK_MVK_macos_surface'        ], commonSuppressExtensions ],
336        [ 'vulkan_vi.h',          [ 'VK_NN_vi_surface'            ], commonSuppressExtensions ],
337        [ 'vulkan_wayland.h',     [ 'VK_KHR_wayland_surface'      ], commonSuppressExtensions ],
338        [ 'vulkan_win32.h',       [ 'VK_.*_win32(|_.*)', 'VK_EXT_full_screen_exclusive' ],
339                                                                     commonSuppressExtensions +
340                                                                     [ 'VK_KHR_external_semaphore',
341                                                                       'VK_KHR_external_memory_capabilities',
342                                                                       'VK_KHR_external_fence',
343                                                                       'VK_KHR_external_fence_capabilities',
344                                                                       'VK_KHR_get_surface_capabilities2',
345                                                                       'VK_NV_external_memory_capabilities',
346                                                                     ] ],
347        [ 'vulkan_xcb.h',         [ 'VK_KHR_xcb_surface'          ], commonSuppressExtensions ],
348        [ 'vulkan_xlib.h',        [ 'VK_KHR_xlib_surface'         ], commonSuppressExtensions ],
349        [ 'vulkan_directfb.h',    [ 'VK_EXT_directfb_surface'     ], commonSuppressExtensions ],
350        [ 'vulkan_xlib_xrandr.h', [ 'VK_EXT_acquire_xlib_display' ], commonSuppressExtensions ],
351        [ 'vulkan_metal.h',       [ 'VK_EXT_metal_surface'        ], commonSuppressExtensions ],
352        [ 'vulkan_beta.h',        betaRequireExtensions,             betaSuppressExtensions ],
353    ]
354
355    for platform in platforms:
356        headername = platform[0]
357
358        allPlatformExtensions += platform[1]
359
360        addPlatformExtensionsRE = makeREstring(
361            platform[1] + platform[2], strings_are_regex=True)
362        emitPlatformExtensionsRE = makeREstring(
363            platform[1], strings_are_regex=True)
364
365        opts = CGeneratorOptions(
366            conventions       = conventions,
367            filename          = headername,
368            directory         = directory,
369            genpath           = None,
370            apiname           = 'vulkan',
371            profile           = None,
372            versions          = featuresPat,
373            emitversions      = None,
374            defaultExtensions = None,
375            addExtensions     = addPlatformExtensionsRE,
376            removeExtensions  = None,
377            emitExtensions    = emitPlatformExtensionsRE,
378            prefixText        = prefixStrings + vkPrefixStrings,
379            genFuncPointers   = True,
380            protectFile       = protectFile,
381            protectFeature    = False,
382            protectProto      = '#ifndef',
383            protectProtoStr   = 'VK_NO_PROTOTYPES',
384            apicall           = 'VKAPI_ATTR ',
385            apientry          = 'VKAPI_CALL ',
386            apientryp         = 'VKAPI_PTR *',
387            alignFuncParam    = 48)
388
389        genOpts[headername] = [ COutputGenerator, opts ]
390
391    # Header for core API + extensions.
392    # To generate just the core API,
393    # change to 'defaultExtensions = None' below.
394    #
395    # By default this adds all enabled, non-platform extensions.
396    # It removes all platform extensions (from the platform headers options
397    # constructed above) as well as any explicitly specified removals.
398
399    removeExtensionsPat = makeREstring(
400        allPlatformExtensions + removeExtensions, None, strings_are_regex=True)
401
402    genOpts['vulkan_core.h'] = [
403          COutputGenerator,
404          CGeneratorOptions(
405            conventions       = conventions,
406            filename          = 'vulkan_core.h',
407            directory         = directory,
408            genpath           = None,
409            apiname           = 'vulkan',
410            profile           = None,
411            versions          = featuresPat,
412            emitversions      = featuresPat,
413            defaultExtensions = defaultExtensions,
414            addExtensions     = addExtensionsPat,
415            removeExtensions  = removeExtensionsPat,
416            emitExtensions    = emitExtensionsPat,
417            prefixText        = prefixStrings + vkPrefixStrings,
418            genFuncPointers   = True,
419            protectFile       = protectFile,
420            protectFeature    = False,
421            protectProto      = '#ifndef',
422            protectProtoStr   = 'VK_NO_PROTOTYPES',
423            apicall           = 'VKAPI_ATTR ',
424            apientry          = 'VKAPI_CALL ',
425            apientryp         = 'VKAPI_PTR *',
426            alignFuncParam    = 48)
427        ]
428
429    # Unused - vulkan10.h target.
430    # It is possible to generate a header with just the Vulkan 1.0 +
431    # extension interfaces defined, but since the promoted KHR extensions
432    # are now defined in terms of the 1.1 interfaces, such a header is very
433    # similar to vulkan_core.h.
434    genOpts['vulkan10.h'] = [
435          COutputGenerator,
436          CGeneratorOptions(
437            conventions       = conventions,
438            filename          = 'vulkan10.h',
439            directory         = directory,
440            genpath           = None,
441            apiname           = 'vulkan',
442            profile           = None,
443            versions          = 'VK_VERSION_1_0',
444            emitversions      = 'VK_VERSION_1_0',
445            defaultExtensions = None,
446            addExtensions     = None,
447            removeExtensions  = None,
448            emitExtensions    = None,
449            prefixText        = prefixStrings + vkPrefixStrings,
450            genFuncPointers   = True,
451            protectFile       = protectFile,
452            protectFeature    = False,
453            protectProto      = '#ifndef',
454            protectProtoStr   = 'VK_NO_PROTOTYPES',
455            apicall           = 'VKAPI_ATTR ',
456            apientry          = 'VKAPI_CALL ',
457            apientryp         = 'VKAPI_PTR *',
458            alignFuncParam    = 48)
459        ]
460
461    # Unused - vulkan11.h target.
462    # It is possible to generate a header with just the Vulkan 1.0 +
463    # extension interfaces defined, but since the promoted KHR extensions
464    # are now defined in terms of the 1.1 interfaces, such a header is very
465    # similar to vulkan_core.h.
466    genOpts['vulkan11.h'] = [
467          COutputGenerator,
468          CGeneratorOptions(
469            conventions       = conventions,
470            filename          = 'vulkan11.h',
471            directory         = directory,
472            genpath           = None,
473            apiname           = 'vulkan',
474            profile           = None,
475            versions          = '^VK_VERSION_1_[01]$',
476            emitversions      = '^VK_VERSION_1_[01]$',
477            defaultExtensions = None,
478            addExtensions     = None,
479            removeExtensions  = None,
480            emitExtensions    = None,
481            prefixText        = prefixStrings + vkPrefixStrings,
482            genFuncPointers   = True,
483            protectFile       = protectFile,
484            protectFeature    = False,
485            protectProto      = '#ifndef',
486            protectProtoStr   = 'VK_NO_PROTOTYPES',
487            apicall           = 'VKAPI_ATTR ',
488            apientry          = 'VKAPI_CALL ',
489            apientryp         = 'VKAPI_PTR *',
490            alignFuncParam    = 48)
491        ]
492
493    genOpts['alias.h'] = [
494          COutputGenerator,
495          CGeneratorOptions(
496            conventions       = conventions,
497            filename          = 'alias.h',
498            directory         = directory,
499            genpath           = None,
500            apiname           = 'vulkan',
501            profile           = None,
502            versions          = featuresPat,
503            emitversions      = featuresPat,
504            defaultExtensions = defaultExtensions,
505            addExtensions     = None,
506            removeExtensions  = removeExtensionsPat,
507            emitExtensions    = emitExtensionsPat,
508            prefixText        = None,
509            genFuncPointers   = False,
510            protectFile       = False,
511            protectFeature    = False,
512            protectProto      = '',
513            protectProtoStr   = '',
514            apicall           = '',
515            apientry          = '',
516            apientryp         = '',
517            alignFuncParam    = 36)
518        ]
519
520
521def genTarget(args):
522    """Create an API generator and corresponding generator options based on
523    the requested target and command line options.
524
525    This is encapsulated in a function so it can be profiled and/or timed.
526    The args parameter is an parsed argument object containing the following
527    fields that are used:
528
529    - target - target to generate
530    - directory - directory to generate it in
531    - protect - True if re-inclusion wrappers should be created
532    - extensions - list of additional extensions to include in generated interfaces"""
533
534    # Create generator options with parameters specified on command line
535    makeGenOpts(args)
536
537    # pdb.set_trace()
538
539    # Select a generator matching the requested target
540    if args.target in genOpts:
541        createGenerator = genOpts[args.target][0]
542        options = genOpts[args.target][1]
543
544        logDiag('* Building', options.filename)
545        logDiag('* options.versions          =', options.versions)
546        logDiag('* options.emitversions      =', options.emitversions)
547        logDiag('* options.defaultExtensions =', options.defaultExtensions)
548        logDiag('* options.addExtensions     =', options.addExtensions)
549        logDiag('* options.removeExtensions  =', options.removeExtensions)
550        logDiag('* options.emitExtensions    =', options.emitExtensions)
551
552        gen = createGenerator(errFile=errWarn,
553                              warnFile=errWarn,
554                              diagFile=diag)
555        return (gen, options)
556    else:
557        logErr('No generator options for unknown target:', args.target)
558        return None
559
560
561# -feature name
562# -extension name
563# For both, "name" may be a single name, or a space-separated list
564# of names, or a regular expression.
565if __name__ == '__main__':
566    parser = argparse.ArgumentParser()
567
568    parser.add_argument('-defaultExtensions', action='store',
569                        default='vulkan',
570                        help='Specify a single class of extensions to add to targets')
571    parser.add_argument('-extension', action='append',
572                        default=[],
573                        help='Specify an extension or extensions to add to targets')
574    parser.add_argument('-removeExtensions', action='append',
575                        default=[],
576                        help='Specify an extension or extensions to remove from targets')
577    parser.add_argument('-emitExtensions', action='append',
578                        default=[],
579                        help='Specify an extension or extensions to emit in targets')
580    parser.add_argument('-emitSpirv', action='append',
581                        default=[],
582                        help='Specify a SPIR-V extension or capability to emit in targets')
583    parser.add_argument('-feature', action='append',
584                        default=[],
585                        help='Specify a core API feature name or names to add to targets')
586    parser.add_argument('-debug', action='store_true',
587                        help='Enable debugging')
588    parser.add_argument('-dump', action='store_true',
589                        help='Enable dump to stderr')
590    parser.add_argument('-diagfile', action='store',
591                        default=None,
592                        help='Write diagnostics to specified file')
593    parser.add_argument('-errfile', action='store',
594                        default=None,
595                        help='Write errors and warnings to specified file instead of stderr')
596    parser.add_argument('-noprotect', dest='protect', action='store_false',
597                        help='Disable inclusion protection in output headers')
598    parser.add_argument('-profile', action='store_true',
599                        help='Enable profiling')
600    parser.add_argument('-registry', action='store',
601                        default='vk.xml',
602                        help='Use specified registry file instead of vk.xml')
603    parser.add_argument('-time', action='store_true',
604                        help='Enable timing')
605    parser.add_argument('-validate', action='store_true',
606                        help='Enable XML group validation')
607    parser.add_argument('-genpath', action='store', default='gen',
608                        help='Path to generated files')
609    parser.add_argument('-o', action='store', dest='directory',
610                        default='.',
611                        help='Create target and related files in specified directory')
612    parser.add_argument('target', metavar='target', nargs='?',
613                        help='Specify target')
614    parser.add_argument('-quiet', action='store_true', default=True,
615                        help='Suppress script output during normal execution.')
616    parser.add_argument('-verbose', action='store_false', dest='quiet', default=True,
617                        help='Enable script output during normal execution.')
618
619    args = parser.parse_args()
620
621    # This splits arguments which are space-separated lists
622    args.feature = [name for arg in args.feature for name in arg.split()]
623    args.extension = [name for arg in args.extension for name in arg.split()]
624
625    # create error/warning & diagnostic files
626    if args.errfile:
627        errWarn = open(args.errfile, 'w', encoding='utf-8')
628    else:
629        errWarn = sys.stderr
630
631    if args.diagfile:
632        diag = open(args.diagfile, 'w', encoding='utf-8')
633    else:
634        diag = None
635
636    # Create the API generator & generator options
637    (gen, options) = genTarget(args)
638
639    # Create the registry object with the specified generator and generator
640    # options. The options are set before XML loading as they may affect it.
641    reg = Registry(gen, options)
642
643    # Parse the specified registry XML into an ElementTree object
644    startTimer(args.time)
645    tree = etree.parse(args.registry)
646    endTimer(args.time, '* Time to make ElementTree =')
647
648    # Load the XML tree into the registry object
649    startTimer(args.time)
650    reg.loadElementTree(tree)
651    endTimer(args.time, '* Time to parse ElementTree =')
652
653    if args.validate:
654        reg.validateGroups()
655
656    if args.dump:
657        logDiag('* Dumping registry to regdump.txt')
658        reg.dumpReg(filehandle=open('regdump.txt', 'w', encoding='utf-8'))
659
660    # Finally, use the output generator to create the requested target
661    if args.debug:
662        pdb.run('reg.apiGen()')
663    else:
664        startTimer(args.time)
665        reg.apiGen()
666        endTimer(args.time, '* Time to generate ' + options.filename + ' =')
667
668    if not args.quiet:
669        logDiag('* Generated', options.filename)
670