1cb93a386Sopenharmony_ci// Copyright 2019 The Chromium Authors. All rights reserved.
2cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
3cb93a386Sopenharmony_ci// found in the LICENSE file.
4cb93a386Sopenharmony_ci
5cb93a386Sopenharmony_cipackage main
6cb93a386Sopenharmony_ci
7cb93a386Sopenharmony_ci// gen_interface creates the assemble/validate cpp files given the
8cb93a386Sopenharmony_ci// interface.json5 file.
9cb93a386Sopenharmony_ci// See README for more details.
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ciimport (
12cb93a386Sopenharmony_ci	"flag"
13cb93a386Sopenharmony_ci	"fmt"
14cb93a386Sopenharmony_ci	"io/ioutil"
15cb93a386Sopenharmony_ci	"os"
16cb93a386Sopenharmony_ci	"path/filepath"
17cb93a386Sopenharmony_ci	"sort"
18cb93a386Sopenharmony_ci	"strings"
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_ci	"github.com/flynn/json5"
21cb93a386Sopenharmony_ci)
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_civar (
24cb93a386Sopenharmony_ci	outDir  = flag.String("out_dir", "../../src/gpu/gl", "Where to output the GrGlAssembleInterface_* and GrGlInterface.cpp files")
25cb93a386Sopenharmony_ci	inTable = flag.String("in_table", "./interface.json5", "The JSON5 table to read in")
26cb93a386Sopenharmony_ci	dryRun  = flag.Bool("dryrun", false, "Print the outputs, don't write to file")
27cb93a386Sopenharmony_ci)
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ciconst (
30cb93a386Sopenharmony_ci	CORE_FEATURE        = "<core>"
31cb93a386Sopenharmony_ci	SPACER              = "    "
32cb93a386Sopenharmony_ci	GLES_FILE_NAME      = "GrGLAssembleGLESInterfaceAutogen.cpp"
33cb93a386Sopenharmony_ci	GL_FILE_NAME        = "GrGLAssembleGLInterfaceAutogen.cpp"
34cb93a386Sopenharmony_ci	WEBGL_FILE_NAME     = "GrGLAssembleWebGLInterfaceAutogen.cpp"
35cb93a386Sopenharmony_ci	INTERFACE_FILE_NAME = "GrGLInterfaceAutogen.cpp"
36cb93a386Sopenharmony_ci)
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci// FeatureSet represents one set of requirements for each of the GL "standards" that
39cb93a386Sopenharmony_ci// Skia supports.  This is currently OpenGL, OpenGL ES and WebGL.
40cb93a386Sopenharmony_ci// OpenGL is typically abbreviated as just "GL".
41cb93a386Sopenharmony_ci// https://www.khronos.org/registry/OpenGL/index_gl.php
42cb93a386Sopenharmony_ci// https://www.khronos.org/opengles/
43cb93a386Sopenharmony_ci// https://www.khronos.org/registry/webgl/specs/1.0/
44cb93a386Sopenharmony_citype FeatureSet struct {
45cb93a386Sopenharmony_ci	GLReqs    []Requirement `json:"GL"`
46cb93a386Sopenharmony_ci	GLESReqs  []Requirement `json:"GLES"`
47cb93a386Sopenharmony_ci	WebGLReqs []Requirement `json:"WebGL"`
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci	Functions         []string           `json:"functions"`
50cb93a386Sopenharmony_ci	HardCodeFunctions []HardCodeFunction `json:"hardcode_functions"`
51cb93a386Sopenharmony_ci	OptionalFunctions []string           `json:"optional"` // not checked in validate
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci	// only assembled/validated when testing
54cb93a386Sopenharmony_ci	TestOnlyFunctions []string `json:"test_functions"`
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci	Required bool `json:"required"`
57cb93a386Sopenharmony_ci}
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ci// Requirement lists how we know if a function exists. Extension is the
60cb93a386Sopenharmony_ci// GL extension (or the string CORE_FEATURE if it's part of the core functionality).
61cb93a386Sopenharmony_ci// MinVersion optionally indicates the minimum version of a standard
62cb93a386Sopenharmony_ci// that has the function.
63cb93a386Sopenharmony_ci// SuffixOverride allows the extension suffix to be manually specified instead
64cb93a386Sopenharmony_ci// of automatically derived from the extension name.
65cb93a386Sopenharmony_ci// (for example, if an NV extension specifies some EXT extensions)
66cb93a386Sopenharmony_citype Requirement struct {
67cb93a386Sopenharmony_ci	Extension      string     `json:"ext"` // required
68cb93a386Sopenharmony_ci	MinVersion     *GLVersion `json:"min_version"`
69cb93a386Sopenharmony_ci	SuffixOverride *string    `json:"suffix"`
70cb93a386Sopenharmony_ci}
71cb93a386Sopenharmony_ci
72cb93a386Sopenharmony_ci// HardCodeFunction indicates to not use the C++ macro and just directly
73cb93a386Sopenharmony_ci// adds a given function ptr to the struct.
74cb93a386Sopenharmony_citype HardCodeFunction struct {
75cb93a386Sopenharmony_ci	PtrName  string `json:"ptr_name"`
76cb93a386Sopenharmony_ci	CastName string `json:"cast_name"`
77cb93a386Sopenharmony_ci	GetName  string `json:"get_name"`
78cb93a386Sopenharmony_ci}
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_civar CORE_REQUIREMENT = Requirement{Extension: CORE_FEATURE, MinVersion: nil}
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_citype GLVersion [2]int
83cb93a386Sopenharmony_ci
84cb93a386Sopenharmony_ci// RequirementGetter functions allows us to "iterate" over the requirements
85cb93a386Sopenharmony_ci// of the different standards which are stored as keys in FeatureSet and
86cb93a386Sopenharmony_ci// normally not easily iterable.
87cb93a386Sopenharmony_citype RequirementGetter func(FeatureSet) []Requirement
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_cifunc glRequirements(fs FeatureSet) []Requirement {
90cb93a386Sopenharmony_ci	return fs.GLReqs
91cb93a386Sopenharmony_ci}
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_cifunc glesRequirements(fs FeatureSet) []Requirement {
94cb93a386Sopenharmony_ci	return fs.GLESReqs
95cb93a386Sopenharmony_ci}
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_cifunc webglRequirements(fs FeatureSet) []Requirement {
98cb93a386Sopenharmony_ci	return fs.WebGLReqs
99cb93a386Sopenharmony_ci}
100cb93a386Sopenharmony_ci
101cb93a386Sopenharmony_ci// generateAssembleInterface creates one GrGLAssembleInterface_[type]_gen.cpp
102cb93a386Sopenharmony_ci// for each of the standards
103cb93a386Sopenharmony_cifunc generateAssembleInterface(features []FeatureSet) {
104cb93a386Sopenharmony_ci	gl := fillAssembleTemplate(ASSEMBLE_INTERFACE_GL, features, glRequirements)
105cb93a386Sopenharmony_ci	writeToFile(*outDir, GL_FILE_NAME, gl)
106cb93a386Sopenharmony_ci	gles := fillAssembleTemplate(ASSEMBLE_INTERFACE_GL_ES, features, glesRequirements)
107cb93a386Sopenharmony_ci	writeToFile(*outDir, GLES_FILE_NAME, gles)
108cb93a386Sopenharmony_ci	webgl := fillAssembleTemplate(ASSEMBLE_INTERFACE_WEBGL, features, webglRequirements)
109cb93a386Sopenharmony_ci	writeToFile(*outDir, WEBGL_FILE_NAME, webgl)
110cb93a386Sopenharmony_ci}
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci// fillAssembleTemplate returns a generated file given a template (for a single standard)
113cb93a386Sopenharmony_ci// to fill out and a slice of features with which to fill it.  getReqs is used to select
114cb93a386Sopenharmony_ci// the requirements for the standard we are working on.
115cb93a386Sopenharmony_cifunc fillAssembleTemplate(template string, features []FeatureSet, getReqs RequirementGetter) string {
116cb93a386Sopenharmony_ci	content := ""
117cb93a386Sopenharmony_ci	for _, feature := range features {
118cb93a386Sopenharmony_ci		// For each feature set, we are going to create a series of
119cb93a386Sopenharmony_ci		// if statements, which check for the requirements (e.g. extensions, version)
120cb93a386Sopenharmony_ci		// and inside those if branches, write the code to load the
121cb93a386Sopenharmony_ci		// correct function pointer to the interface. GET_PROC and
122cb93a386Sopenharmony_ci		// GET_PROC_SUFFIX are macros defined in C++ part of the template
123cb93a386Sopenharmony_ci		// to accomplish this (for a core feature and extensions, respectively).
124cb93a386Sopenharmony_ci		reqs := getReqs(feature)
125cb93a386Sopenharmony_ci		if len(reqs) == 0 {
126cb93a386Sopenharmony_ci			continue
127cb93a386Sopenharmony_ci		}
128cb93a386Sopenharmony_ci		// blocks holds all the if blocks generated - it will be joined with else
129cb93a386Sopenharmony_ci		// after and appended to content
130cb93a386Sopenharmony_ci		blocks := []string{}
131cb93a386Sopenharmony_ci		for i, req := range reqs {
132cb93a386Sopenharmony_ci			block := ""
133cb93a386Sopenharmony_ci			ifExpr := requirementIfExpression(req, true)
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ci			if ifExpr != "" {
136cb93a386Sopenharmony_ci				if strings.HasPrefix(ifExpr, "(") {
137cb93a386Sopenharmony_ci					ifExpr = "if " + ifExpr + " {"
138cb93a386Sopenharmony_ci				} else {
139cb93a386Sopenharmony_ci					ifExpr = "if (" + ifExpr + ") {"
140cb93a386Sopenharmony_ci				}
141cb93a386Sopenharmony_ci				// Indent the first if statement
142cb93a386Sopenharmony_ci				if i == 0 {
143cb93a386Sopenharmony_ci					block = addLine(block, ifExpr)
144cb93a386Sopenharmony_ci				} else {
145cb93a386Sopenharmony_ci					block += ifExpr + "\n"
146cb93a386Sopenharmony_ci				}
147cb93a386Sopenharmony_ci			}
148cb93a386Sopenharmony_ci			// sort for determinism
149cb93a386Sopenharmony_ci			sort.Strings(feature.Functions)
150cb93a386Sopenharmony_ci			for _, function := range feature.Functions {
151cb93a386Sopenharmony_ci				block = assembleFunction(block, ifExpr, function, req)
152cb93a386Sopenharmony_ci			}
153cb93a386Sopenharmony_ci			sort.Strings(feature.TestOnlyFunctions)
154cb93a386Sopenharmony_ci			if len(feature.TestOnlyFunctions) > 0 {
155cb93a386Sopenharmony_ci				block += "#if GR_TEST_UTILS\n"
156cb93a386Sopenharmony_ci				for _, function := range feature.TestOnlyFunctions {
157cb93a386Sopenharmony_ci					block = assembleFunction(block, ifExpr, function, req)
158cb93a386Sopenharmony_ci				}
159cb93a386Sopenharmony_ci				block += "#endif\n"
160cb93a386Sopenharmony_ci			}
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci			// a hard code function does not use the C++ macro
163cb93a386Sopenharmony_ci			for _, hcf := range feature.HardCodeFunctions {
164cb93a386Sopenharmony_ci				if ifExpr != "" {
165cb93a386Sopenharmony_ci					// extra tab for being in an if statement
166cb93a386Sopenharmony_ci					block += SPACER
167cb93a386Sopenharmony_ci				}
168cb93a386Sopenharmony_ci				line := fmt.Sprintf(`functions->%s =(%s*)get(ctx, "%s");`, hcf.PtrName, hcf.CastName, hcf.GetName)
169cb93a386Sopenharmony_ci				block = addLine(block, line)
170cb93a386Sopenharmony_ci			}
171cb93a386Sopenharmony_ci			if ifExpr != "" {
172cb93a386Sopenharmony_ci				block += SPACER + "}"
173cb93a386Sopenharmony_ci			}
174cb93a386Sopenharmony_ci			blocks = append(blocks, block)
175cb93a386Sopenharmony_ci		}
176cb93a386Sopenharmony_ci		content += strings.Join(blocks, " else ")
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_ci		if feature.Required && reqs[0] != CORE_REQUIREMENT {
179cb93a386Sopenharmony_ci			content += ` else {
180cb93a386Sopenharmony_ci        SkASSERT(false); // Required feature
181cb93a386Sopenharmony_ci        return nullptr;
182cb93a386Sopenharmony_ci    }`
183cb93a386Sopenharmony_ci		}
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ci		if !strings.HasSuffix(content, "\n") {
186cb93a386Sopenharmony_ci			content += "\n"
187cb93a386Sopenharmony_ci		}
188cb93a386Sopenharmony_ci		// Add an extra space between blocks for easier readability
189cb93a386Sopenharmony_ci		content += "\n"
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_ci	}
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci	return strings.Replace(template, "[[content]]", content, 1)
194cb93a386Sopenharmony_ci}
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci// assembleFunction is a little helper that will add a function to the interface
197cb93a386Sopenharmony_ci// using an appropriate macro (e.g. if the function is added)
198cb93a386Sopenharmony_ci// with an extension.
199cb93a386Sopenharmony_cifunc assembleFunction(block, ifExpr, function string, req Requirement) string {
200cb93a386Sopenharmony_ci	if ifExpr != "" {
201cb93a386Sopenharmony_ci		// extra tab for being in an if statement
202cb93a386Sopenharmony_ci		block += SPACER
203cb93a386Sopenharmony_ci	}
204cb93a386Sopenharmony_ci	suffix := deriveSuffix(req.Extension)
205cb93a386Sopenharmony_ci	// Some ARB extensions don't have ARB suffixes because they were written
206cb93a386Sopenharmony_ci	// for backwards compatibility simultaneous to adding them as required
207cb93a386Sopenharmony_ci	// in a new GL version.
208cb93a386Sopenharmony_ci	if suffix == "ARB" {
209cb93a386Sopenharmony_ci		suffix = ""
210cb93a386Sopenharmony_ci	}
211cb93a386Sopenharmony_ci	if req.SuffixOverride != nil {
212cb93a386Sopenharmony_ci		suffix = *req.SuffixOverride
213cb93a386Sopenharmony_ci	}
214cb93a386Sopenharmony_ci	if req.Extension == CORE_FEATURE || suffix == "" {
215cb93a386Sopenharmony_ci		block = addLine(block, fmt.Sprintf("GET_PROC(%s);", function))
216cb93a386Sopenharmony_ci	} else if req.Extension != "" {
217cb93a386Sopenharmony_ci		block = addLine(block, fmt.Sprintf("GET_PROC_SUFFIX(%s, %s);", function, suffix))
218cb93a386Sopenharmony_ci	}
219cb93a386Sopenharmony_ci	return block
220cb93a386Sopenharmony_ci}
221cb93a386Sopenharmony_ci
222cb93a386Sopenharmony_ci// requirementIfExpression returns a string that is an if expression
223cb93a386Sopenharmony_ci// Notably, there is no if expression if the function is a "core" function
224cb93a386Sopenharmony_ci// on all supported versions.
225cb93a386Sopenharmony_ci// The expressions are wrapped in parentheses so they can be safely
226cb93a386Sopenharmony_ci// joined together with && or ||.
227cb93a386Sopenharmony_cifunc requirementIfExpression(req Requirement, isLocal bool) string {
228cb93a386Sopenharmony_ci	mv := req.MinVersion
229cb93a386Sopenharmony_ci	if req == CORE_REQUIREMENT {
230cb93a386Sopenharmony_ci		return ""
231cb93a386Sopenharmony_ci	}
232cb93a386Sopenharmony_ci	if req.Extension == CORE_FEATURE && mv != nil {
233cb93a386Sopenharmony_ci		return fmt.Sprintf("(glVer >= GR_GL_VER(%d,%d))", mv[0], mv[1])
234cb93a386Sopenharmony_ci	}
235cb93a386Sopenharmony_ci	extVar := "fExtensions"
236cb93a386Sopenharmony_ci	if isLocal {
237cb93a386Sopenharmony_ci		extVar = "extensions"
238cb93a386Sopenharmony_ci	}
239cb93a386Sopenharmony_ci	// We know it has an extension
240cb93a386Sopenharmony_ci	if req.Extension != "" {
241cb93a386Sopenharmony_ci		if mv == nil {
242cb93a386Sopenharmony_ci			return fmt.Sprintf("%s.has(%q)", extVar, req.Extension)
243cb93a386Sopenharmony_ci		} else {
244cb93a386Sopenharmony_ci			return fmt.Sprintf("(glVer >= GR_GL_VER(%d,%d) && %s.has(%q))", mv[0], mv[1], extVar, req.Extension)
245cb93a386Sopenharmony_ci		}
246cb93a386Sopenharmony_ci	}
247cb93a386Sopenharmony_ci	abort("ERROR: requirement must have ext")
248cb93a386Sopenharmony_ci	return "ERROR"
249cb93a386Sopenharmony_ci}
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci// driveSuffix returns the suffix of the function associated with the given
252cb93a386Sopenharmony_ci// extension.
253cb93a386Sopenharmony_cifunc deriveSuffix(ext string) string {
254cb93a386Sopenharmony_ci	// Some extensions begin with GL_ and then have the actual
255cb93a386Sopenharmony_ci	// extension like KHR, EXT etc.
256cb93a386Sopenharmony_ci	ext = strings.TrimPrefix(ext, "GL_")
257cb93a386Sopenharmony_ci	return strings.Split(ext, "_")[0]
258cb93a386Sopenharmony_ci}
259cb93a386Sopenharmony_ci
260cb93a386Sopenharmony_ci// addLine is a little helper function which handles the newline and tab
261cb93a386Sopenharmony_cifunc addLine(str, line string) string {
262cb93a386Sopenharmony_ci	return str + SPACER + line + "\n"
263cb93a386Sopenharmony_ci}
264cb93a386Sopenharmony_ci
265cb93a386Sopenharmony_cifunc writeToFile(parent, file, content string) {
266cb93a386Sopenharmony_ci	p := filepath.Join(parent, file)
267cb93a386Sopenharmony_ci	if *dryRun {
268cb93a386Sopenharmony_ci		fmt.Printf("Writing to %s\n", p)
269cb93a386Sopenharmony_ci		fmt.Println(content)
270cb93a386Sopenharmony_ci	} else {
271cb93a386Sopenharmony_ci		if err := ioutil.WriteFile(p, []byte(content), 0644); err != nil {
272cb93a386Sopenharmony_ci			abort("Error while writing to file %s: %s", p, err)
273cb93a386Sopenharmony_ci		}
274cb93a386Sopenharmony_ci	}
275cb93a386Sopenharmony_ci}
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci// validationEntry is a helper struct that contains anything
278cb93a386Sopenharmony_ci// necessary to make validation code for a given standard.
279cb93a386Sopenharmony_citype validationEntry struct {
280cb93a386Sopenharmony_ci	StandardCheck string
281cb93a386Sopenharmony_ci	GetReqs       RequirementGetter
282cb93a386Sopenharmony_ci}
283cb93a386Sopenharmony_ci
284cb93a386Sopenharmony_cifunc generateValidateInterface(features []FeatureSet) {
285cb93a386Sopenharmony_ci	standards := []validationEntry{
286cb93a386Sopenharmony_ci		{
287cb93a386Sopenharmony_ci			StandardCheck: "GR_IS_GR_GL(fStandard)",
288cb93a386Sopenharmony_ci			GetReqs:       glRequirements,
289cb93a386Sopenharmony_ci		}, {
290cb93a386Sopenharmony_ci			StandardCheck: "GR_IS_GR_GL_ES(fStandard)",
291cb93a386Sopenharmony_ci			GetReqs:       glesRequirements,
292cb93a386Sopenharmony_ci		}, {
293cb93a386Sopenharmony_ci			StandardCheck: "GR_IS_GR_WEBGL(fStandard)",
294cb93a386Sopenharmony_ci			GetReqs:       webglRequirements,
295cb93a386Sopenharmony_ci		},
296cb93a386Sopenharmony_ci	}
297cb93a386Sopenharmony_ci	content := ""
298cb93a386Sopenharmony_ci	// For each feature, we are going to generate a series of
299cb93a386Sopenharmony_ci	// boolean expressions which check that the functions we thought
300cb93a386Sopenharmony_ci	// were gathered during the assemble phase actually were applied to
301cb93a386Sopenharmony_ci	// the interface (functionCheck). This check will be guarded
302cb93a386Sopenharmony_ci	// another set of if statements (one per standard) based
303cb93a386Sopenharmony_ci	// on the same requirements (standardChecks) that were used when
304cb93a386Sopenharmony_ci	// assembling the interface.
305cb93a386Sopenharmony_ci	for _, feature := range features {
306cb93a386Sopenharmony_ci		if allReqsAreCore(feature) {
307cb93a386Sopenharmony_ci			content += functionCheck(feature, 1)
308cb93a386Sopenharmony_ci		} else {
309cb93a386Sopenharmony_ci			content += SPACER
310cb93a386Sopenharmony_ci			standardChecks := []string{}
311cb93a386Sopenharmony_ci			for _, std := range standards {
312cb93a386Sopenharmony_ci				reqs := std.GetReqs(feature)
313cb93a386Sopenharmony_ci				if reqs == nil || len(reqs) == 0 {
314cb93a386Sopenharmony_ci					continue
315cb93a386Sopenharmony_ci				}
316cb93a386Sopenharmony_ci				expr := []string{}
317cb93a386Sopenharmony_ci				for _, r := range reqs {
318cb93a386Sopenharmony_ci					e := requirementIfExpression(r, false)
319cb93a386Sopenharmony_ci					if e != "" {
320cb93a386Sopenharmony_ci						expr = append(expr, e)
321cb93a386Sopenharmony_ci					}
322cb93a386Sopenharmony_ci				}
323cb93a386Sopenharmony_ci				check := ""
324cb93a386Sopenharmony_ci				if len(expr) == 0 {
325cb93a386Sopenharmony_ci					check = fmt.Sprintf("%s", std.StandardCheck)
326cb93a386Sopenharmony_ci				} else {
327cb93a386Sopenharmony_ci					lineBreak := "\n" + SPACER + "      "
328cb93a386Sopenharmony_ci					check = fmt.Sprintf("(%s && (%s%s))", std.StandardCheck, lineBreak, strings.Join(expr, " ||"+lineBreak))
329cb93a386Sopenharmony_ci				}
330cb93a386Sopenharmony_ci				standardChecks = append(standardChecks, check)
331cb93a386Sopenharmony_ci			}
332cb93a386Sopenharmony_ci			content += fmt.Sprintf("if (%s) {\n", strings.Join(standardChecks, " ||\n"+SPACER+"   "))
333cb93a386Sopenharmony_ci			content += functionCheck(feature, 2)
334cb93a386Sopenharmony_ci
335cb93a386Sopenharmony_ci			content += SPACER + "}\n"
336cb93a386Sopenharmony_ci		}
337cb93a386Sopenharmony_ci		// add additional line between each block
338cb93a386Sopenharmony_ci		content += "\n"
339cb93a386Sopenharmony_ci	}
340cb93a386Sopenharmony_ci	content = strings.Replace(VALIDATE_INTERFACE, "[[content]]", content, 1)
341cb93a386Sopenharmony_ci	writeToFile(*outDir, INTERFACE_FILE_NAME, content)
342cb93a386Sopenharmony_ci}
343cb93a386Sopenharmony_ci
344cb93a386Sopenharmony_ci// functionCheck returns an if statement that checks that all functions
345cb93a386Sopenharmony_ci// in the passed in slice are on the interface (that is, they are truthy
346cb93a386Sopenharmony_ci// on the fFunctions struct)
347cb93a386Sopenharmony_cifunc functionCheck(feature FeatureSet, indentLevel int) string {
348cb93a386Sopenharmony_ci	// sort for determinism
349cb93a386Sopenharmony_ci	sort.Strings(feature.Functions)
350cb93a386Sopenharmony_ci	indent := strings.Repeat(SPACER, indentLevel)
351cb93a386Sopenharmony_ci
352cb93a386Sopenharmony_ci	checks := []string{}
353cb93a386Sopenharmony_ci	for _, function := range feature.Functions {
354cb93a386Sopenharmony_ci		if in(function, feature.OptionalFunctions) {
355cb93a386Sopenharmony_ci			continue
356cb93a386Sopenharmony_ci		}
357cb93a386Sopenharmony_ci		checks = append(checks, "!fFunctions.f"+function)
358cb93a386Sopenharmony_ci	}
359cb93a386Sopenharmony_ci	testOnly := []string{}
360cb93a386Sopenharmony_ci	for _, function := range feature.TestOnlyFunctions {
361cb93a386Sopenharmony_ci		if in(function, feature.OptionalFunctions) {
362cb93a386Sopenharmony_ci			continue
363cb93a386Sopenharmony_ci		}
364cb93a386Sopenharmony_ci		testOnly = append(testOnly, "!fFunctions.f"+function)
365cb93a386Sopenharmony_ci	}
366cb93a386Sopenharmony_ci	for _, hcf := range feature.HardCodeFunctions {
367cb93a386Sopenharmony_ci		checks = append(checks, "!fFunctions."+hcf.PtrName)
368cb93a386Sopenharmony_ci	}
369cb93a386Sopenharmony_ci	preCheck := ""
370cb93a386Sopenharmony_ci	if len(testOnly) != 0 {
371cb93a386Sopenharmony_ci		preCheck = fmt.Sprintf(`#if GR_TEST_UTILS
372cb93a386Sopenharmony_ci%sif (%s) {
373cb93a386Sopenharmony_ci%s%sRETURN_FALSE_INTERFACE;
374cb93a386Sopenharmony_ci%s}
375cb93a386Sopenharmony_ci#endif
376cb93a386Sopenharmony_ci`, indent, strings.Join(testOnly, " ||\n"+indent+"    "), indent, SPACER, indent)
377cb93a386Sopenharmony_ci	}
378cb93a386Sopenharmony_ci
379cb93a386Sopenharmony_ci	if len(checks) == 0 {
380cb93a386Sopenharmony_ci		return preCheck + strings.Repeat(SPACER, indentLevel) + "// all functions were marked optional or test_only\n"
381cb93a386Sopenharmony_ci	}
382cb93a386Sopenharmony_ci
383cb93a386Sopenharmony_ci	return preCheck + fmt.Sprintf(`%sif (%s) {
384cb93a386Sopenharmony_ci%s%sRETURN_FALSE_INTERFACE;
385cb93a386Sopenharmony_ci%s}
386cb93a386Sopenharmony_ci`, indent, strings.Join(checks, " ||\n"+indent+"    "), indent, SPACER, indent)
387cb93a386Sopenharmony_ci}
388cb93a386Sopenharmony_ci
389cb93a386Sopenharmony_ci// allReqsAreCore returns true iff the FeatureSet is part of "core" for
390cb93a386Sopenharmony_ci// all standards
391cb93a386Sopenharmony_cifunc allReqsAreCore(feature FeatureSet) bool {
392cb93a386Sopenharmony_ci	if feature.GLReqs == nil || feature.GLESReqs == nil {
393cb93a386Sopenharmony_ci		return false
394cb93a386Sopenharmony_ci	}
395cb93a386Sopenharmony_ci	return feature.GLReqs[0] == CORE_REQUIREMENT && feature.GLESReqs[0] == CORE_REQUIREMENT && feature.WebGLReqs[0] == CORE_REQUIREMENT
396cb93a386Sopenharmony_ci}
397cb93a386Sopenharmony_ci
398cb93a386Sopenharmony_cifunc validateFeatures(features []FeatureSet) {
399cb93a386Sopenharmony_ci	seen := map[string]bool{}
400cb93a386Sopenharmony_ci	for _, feature := range features {
401cb93a386Sopenharmony_ci		for _, fn := range feature.Functions {
402cb93a386Sopenharmony_ci			if seen[fn] {
403cb93a386Sopenharmony_ci				abort("ERROR: Duplicate function %s", fn)
404cb93a386Sopenharmony_ci			}
405cb93a386Sopenharmony_ci			seen[fn] = true
406cb93a386Sopenharmony_ci		}
407cb93a386Sopenharmony_ci		for _, fn := range feature.TestOnlyFunctions {
408cb93a386Sopenharmony_ci			if seen[fn] {
409cb93a386Sopenharmony_ci				abort("ERROR: Duplicate function %s\n", fn)
410cb93a386Sopenharmony_ci			}
411cb93a386Sopenharmony_ci			seen[fn] = true
412cb93a386Sopenharmony_ci		}
413cb93a386Sopenharmony_ci	}
414cb93a386Sopenharmony_ci}
415cb93a386Sopenharmony_ci
416cb93a386Sopenharmony_ci// in returns true if |s| is *in* |a| slice.
417cb93a386Sopenharmony_cifunc in(s string, a []string) bool {
418cb93a386Sopenharmony_ci	for _, x := range a {
419cb93a386Sopenharmony_ci		if x == s {
420cb93a386Sopenharmony_ci			return true
421cb93a386Sopenharmony_ci		}
422cb93a386Sopenharmony_ci	}
423cb93a386Sopenharmony_ci	return false
424cb93a386Sopenharmony_ci}
425cb93a386Sopenharmony_ci
426cb93a386Sopenharmony_cifunc abort(fmtStr string, inputs ...interface{}) {
427cb93a386Sopenharmony_ci	fmt.Printf(fmtStr+"\n", inputs...)
428cb93a386Sopenharmony_ci	os.Exit(1)
429cb93a386Sopenharmony_ci}
430cb93a386Sopenharmony_ci
431cb93a386Sopenharmony_cifunc main() {
432cb93a386Sopenharmony_ci	flag.Parse()
433cb93a386Sopenharmony_ci	b, err := ioutil.ReadFile(*inTable)
434cb93a386Sopenharmony_ci	if err != nil {
435cb93a386Sopenharmony_ci		abort("Could not read file %s", err)
436cb93a386Sopenharmony_ci	}
437cb93a386Sopenharmony_ci
438cb93a386Sopenharmony_ci	dir, err := os.Open(*outDir)
439cb93a386Sopenharmony_ci	if err != nil {
440cb93a386Sopenharmony_ci		abort("Could not write to output dir %s", err)
441cb93a386Sopenharmony_ci	}
442cb93a386Sopenharmony_ci	defer dir.Close()
443cb93a386Sopenharmony_ci	if fi, err := dir.Stat(); err != nil {
444cb93a386Sopenharmony_ci		abort("Error getting info about %s: %s", *outDir, err)
445cb93a386Sopenharmony_ci	} else if !fi.IsDir() {
446cb93a386Sopenharmony_ci		abort("%s must be a directory", *outDir)
447cb93a386Sopenharmony_ci	}
448cb93a386Sopenharmony_ci
449cb93a386Sopenharmony_ci	features := []FeatureSet{}
450cb93a386Sopenharmony_ci
451cb93a386Sopenharmony_ci	err = json5.Unmarshal(b, &features)
452cb93a386Sopenharmony_ci	if err != nil {
453cb93a386Sopenharmony_ci		abort("Invalid JSON: %s", err)
454cb93a386Sopenharmony_ci	}
455cb93a386Sopenharmony_ci
456cb93a386Sopenharmony_ci	validateFeatures(features)
457cb93a386Sopenharmony_ci
458cb93a386Sopenharmony_ci	generateAssembleInterface(features)
459cb93a386Sopenharmony_ci	generateValidateInterface(features)
460cb93a386Sopenharmony_ci}
461