1e5c31af7Sopenharmony_ci#!/usr/bin/env python3
2e5c31af7Sopenharmony_ci#
3e5c31af7Sopenharmony_ci# Copyright 2020-2024 The Khronos Group Inc.
4e5c31af7Sopenharmony_ci#
5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0
6e5c31af7Sopenharmony_ci
7e5c31af7Sopenharmony_ci# Build a spec with requested extension sets and options.
8e5c31af7Sopenharmony_ci#
9e5c31af7Sopenharmony_ci# Usage: makeSpec script-options make-options
10e5c31af7Sopenharmony_ci# Script options are parsed by this script before invoking 'make':
11e5c31af7Sopenharmony_ci#   -genpath path - directory for generated files and outputs
12e5c31af7Sopenharmony_ci#   -spec core - make a spec with no extensions (default)
13e5c31af7Sopenharmony_ci#   -spec khr - make a spec with all KHR extensions
14e5c31af7Sopenharmony_ci#   -spec ratified - make a spec with all ratified (KHR + some EXT) extensions
15e5c31af7Sopenharmony_ci#   -spec all - make a spec with all registered extensions
16e5c31af7Sopenharmony_ci#   -version {1.0 | 1.1 | 1.2 | 1.3 | sc1.0} - make a spec with this core version
17e5c31af7Sopenharmony_ci#   -ext name - add specified extension and its dependencies
18e5c31af7Sopenharmony_ci#   -clean - clean generated files before building
19e5c31af7Sopenharmony_ci#   -registry path - API XML to use instead of default
20e5c31af7Sopenharmony_ci#   -apiname name - API name to use instead of default
21e5c31af7Sopenharmony_ci#   -test - Build the test spec instead
22e5c31af7Sopenharmony_ci#   -v - verbose, print actions before executing  them
23e5c31af7Sopenharmony_ci#   -n - dry-run, print actions instead of executing them
24e5c31af7Sopenharmony_ci# make-options - all other options are passed to 'make', including
25e5c31af7Sopenharmony_ci# requested build targets
26e5c31af7Sopenharmony_ci
27e5c31af7Sopenharmony_ciimport argparse, copy, io, os, re, string, subprocess, sys
28e5c31af7Sopenharmony_ci
29e5c31af7Sopenharmony_cidef execute(args, results):
30e5c31af7Sopenharmony_ci    if results.verbose or results.dryrun:
31e5c31af7Sopenharmony_ci        print("'" + "' '".join(args) + "'")
32e5c31af7Sopenharmony_ci    if not results.dryrun:
33e5c31af7Sopenharmony_ci        subprocess.check_call(args)
34e5c31af7Sopenharmony_ci
35e5c31af7Sopenharmony_ciif __name__ == '__main__':
36e5c31af7Sopenharmony_ci    parser = argparse.ArgumentParser()
37e5c31af7Sopenharmony_ci
38e5c31af7Sopenharmony_ci    parser.add_argument('-clean', action='store_true',
39e5c31af7Sopenharmony_ci                        help='Clean generated files before building')
40e5c31af7Sopenharmony_ci    parser.add_argument('-extension', action='append',
41e5c31af7Sopenharmony_ci                        default=[],
42e5c31af7Sopenharmony_ci                        help='Specify a required extension or extensions to add to targets')
43e5c31af7Sopenharmony_ci    parser.add_argument('-genpath', action='store',
44e5c31af7Sopenharmony_ci                        default='gen',
45e5c31af7Sopenharmony_ci                        help='Path to directory containing generated files')
46e5c31af7Sopenharmony_ci    parser.add_argument('-spec', action='store',
47e5c31af7Sopenharmony_ci                        choices=[ 'core', 'khr', 'ratified', 'all' ],
48e5c31af7Sopenharmony_ci                        default='core',
49e5c31af7Sopenharmony_ci                        help='Type of spec to generate')
50e5c31af7Sopenharmony_ci    parser.add_argument('-version', action='store',
51e5c31af7Sopenharmony_ci                        choices=[ '1.0', '1.1', '1.2', '1.3', 'sc1.0' ],
52e5c31af7Sopenharmony_ci                        default='1.3',
53e5c31af7Sopenharmony_ci                        help='Type of spec to generate')
54e5c31af7Sopenharmony_ci    parser.add_argument('-registry', action='store',
55e5c31af7Sopenharmony_ci                        default=None,
56e5c31af7Sopenharmony_ci                        help='Path to API XML registry file specifying version and extension dependencies')
57e5c31af7Sopenharmony_ci    parser.add_argument('-apiname', action='store',
58e5c31af7Sopenharmony_ci                        default=None,
59e5c31af7Sopenharmony_ci                        help='API name to generate')
60e5c31af7Sopenharmony_ci    parser.add_argument('-test', action='store_true',
61e5c31af7Sopenharmony_ci                        help='Build the test spec instead of the Vulkan spec')
62e5c31af7Sopenharmony_ci    parser.add_argument('-n', action='store_true', dest='dryrun',
63e5c31af7Sopenharmony_ci                        help='Only prints actions, do not execute them')
64e5c31af7Sopenharmony_ci    parser.add_argument('-v', action='store_true', dest='verbose',
65e5c31af7Sopenharmony_ci                        help='Print actions before executing them')
66e5c31af7Sopenharmony_ci
67e5c31af7Sopenharmony_ci    (results, options) = parser.parse_known_args()
68e5c31af7Sopenharmony_ci
69e5c31af7Sopenharmony_ci    # Ensure genpath is an absolute path, not relative
70e5c31af7Sopenharmony_ci    if results.genpath[0] != '/':
71e5c31af7Sopenharmony_ci        results.genpath = os.getcwd() + '/' + results.genpath
72e5c31af7Sopenharmony_ci
73e5c31af7Sopenharmony_ci    # Look for scripts/extdependency.py
74e5c31af7Sopenharmony_ci    # This requires makeSpec to be invoked from the repository root, but we
75e5c31af7Sopenharmony_ci    # could derive that path.
76e5c31af7Sopenharmony_ci    sys.path.insert(0, 'scripts')
77e5c31af7Sopenharmony_ci    from extdependency import ApiDependencies
78e5c31af7Sopenharmony_ci    deps = ApiDependencies(results.registry, results.apiname)
79e5c31af7Sopenharmony_ci
80e5c31af7Sopenharmony_ci    # List of versions to build with from the requested -version
81e5c31af7Sopenharmony_ci    # This should come from the extdependency module as well, eventually
82e5c31af7Sopenharmony_ci    #@ Note that at present, building sc1.0 does *not* include Vulkan 1.3 automatically.
83e5c31af7Sopenharmony_ci    versionDict = {
84e5c31af7Sopenharmony_ci        '1.0' : [ 'VK_VERSION_1_0' ],
85e5c31af7Sopenharmony_ci        '1.1' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1' ],
86e5c31af7Sopenharmony_ci        '1.2' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2' ],
87e5c31af7Sopenharmony_ci        '1.3' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2', 'VK_VERSION_1_3' ],
88e5c31af7Sopenharmony_ci        'sc1.0' : [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2', 'VKSC_VERSION_1_0' ],
89e5c31af7Sopenharmony_ci    }
90e5c31af7Sopenharmony_ci    versions = 'VERSIONS={}'.format(' '.join(versionDict[results.version]))
91e5c31af7Sopenharmony_ci
92e5c31af7Sopenharmony_ci    # List of extensions to build with from the requested -spec
93e5c31af7Sopenharmony_ci    # Also construct a spec title
94e5c31af7Sopenharmony_ci    # This should respect version dependencies as well
95e5c31af7Sopenharmony_ci    if results.spec == 'core':
96e5c31af7Sopenharmony_ci        title = ''
97e5c31af7Sopenharmony_ci        exts = set()
98e5c31af7Sopenharmony_ci    elif results.spec == 'khr':
99e5c31af7Sopenharmony_ci        title = 'with all KHR extensions'
100e5c31af7Sopenharmony_ci        exts = set(deps.khrExtensions())
101e5c31af7Sopenharmony_ci    elif results.spec == 'ratified':
102e5c31af7Sopenharmony_ci        title = 'with all ratified extensions'
103e5c31af7Sopenharmony_ci        exts = set(deps.ratifiedExtensions())
104e5c31af7Sopenharmony_ci    elif results.spec == 'all':
105e5c31af7Sopenharmony_ci        title = 'with all registered extensions'
106e5c31af7Sopenharmony_ci        exts = set(deps.allExtensions())
107e5c31af7Sopenharmony_ci
108e5c31af7Sopenharmony_ci    # List of explicitly requested extension and all its supported dependencies
109e5c31af7Sopenharmony_ci    extraexts = set()
110e5c31af7Sopenharmony_ci    for name in results.extension:
111e5c31af7Sopenharmony_ci        if name in deps.allExtensions():
112e5c31af7Sopenharmony_ci            extraexts.add(name)
113e5c31af7Sopenharmony_ci            for dep in deps.children(name):
114e5c31af7Sopenharmony_ci                if dep in deps.allExtensions():
115e5c31af7Sopenharmony_ci                    extraexts.update({dep})
116e5c31af7Sopenharmony_ci        else:
117e5c31af7Sopenharmony_ci            raise Exception(f'ERROR: unknown extension {name}')
118e5c31af7Sopenharmony_ci
119e5c31af7Sopenharmony_ci    # See if any explicitly requested extensions are not implicitly requested
120e5c31af7Sopenharmony_ci    # Add any such extensions to the spec title
121e5c31af7Sopenharmony_ci    extraexts -= exts
122e5c31af7Sopenharmony_ci    if len(extraexts) > 0:
123e5c31af7Sopenharmony_ci        exts.update(extraexts)
124e5c31af7Sopenharmony_ci        if title != '':
125e5c31af7Sopenharmony_ci            title += ' and ' + ', '.join(sorted(extraexts))
126e5c31af7Sopenharmony_ci        else:
127e5c31af7Sopenharmony_ci            title += 'with ' + ', '.join(sorted(extraexts))
128e5c31af7Sopenharmony_ci
129e5c31af7Sopenharmony_ci    if title != '':
130e5c31af7Sopenharmony_ci        title = '(' + title + ')'
131e5c31af7Sopenharmony_ci
132e5c31af7Sopenharmony_ci    # Finally, actually invoke make as needed for the targets
133e5c31af7Sopenharmony_ci    args = [ 'make', 'GENERATED=' + results.genpath ]
134e5c31af7Sopenharmony_ci
135e5c31af7Sopenharmony_ci    if results.clean:
136e5c31af7Sopenharmony_ci        # If OUTDIR is set on the command line, pass it to the 'clean'
137e5c31af7Sopenharmony_ci        # target so it is cleaned as well.
138e5c31af7Sopenharmony_ci        cleanopts = ['clean']
139e5c31af7Sopenharmony_ci        for opt in options:
140e5c31af7Sopenharmony_ci            if opt[:7] == 'OUTDIR=':
141e5c31af7Sopenharmony_ci                cleanopts.append(opt)
142e5c31af7Sopenharmony_ci        try:
143e5c31af7Sopenharmony_ci            execute(args + cleanopts, results)
144e5c31af7Sopenharmony_ci        except:
145e5c31af7Sopenharmony_ci            sys.exit(1)
146e5c31af7Sopenharmony_ci
147e5c31af7Sopenharmony_ci    # Use the test spec if specified.  This is used solely by self tests.
148e5c31af7Sopenharmony_ci    rootdir = os.path.dirname(os.path.abspath(__file__))
149e5c31af7Sopenharmony_ci    if results.test:
150e5c31af7Sopenharmony_ci        # Set the spec source to the test spec
151e5c31af7Sopenharmony_ci        args.append(f'SPECSRC={rootdir}/build_tests/testspec.adoc')
152e5c31af7Sopenharmony_ci        args.append(f'SPECDIR={rootdir}/build_tests/')
153e5c31af7Sopenharmony_ci        # Make sure the build is invariant
154e5c31af7Sopenharmony_ci        args.append('SPECREVISION=1.2.3')
155e5c31af7Sopenharmony_ci        args.append('SPECDATE=\\"2100-11-22 00:33:44Z\\"')
156e5c31af7Sopenharmony_ci        args.append('SPECREMARK=\\"test build\\"')
157e5c31af7Sopenharmony_ci
158e5c31af7Sopenharmony_ci    args.append(versions)
159e5c31af7Sopenharmony_ci
160e5c31af7Sopenharmony_ci    # The actual target
161e5c31af7Sopenharmony_ci    if len(exts) > 0:
162e5c31af7Sopenharmony_ci        args.append(f'EXTENSIONS={" ".join(sorted(exts))}')
163e5c31af7Sopenharmony_ci    args.append(f'APITITLE={title}')
164e5c31af7Sopenharmony_ci    args += options
165e5c31af7Sopenharmony_ci
166e5c31af7Sopenharmony_ci    try:
167e5c31af7Sopenharmony_ci        execute(args, results)
168e5c31af7Sopenharmony_ci    except:
169e5c31af7Sopenharmony_ci        sys.exit(1)
170