• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// intrinsic-gen parses the <tint>/src/intrinsics.def file, then scans the
16// project directory for '<file>.tmpl' files, to produce '<file>' source code
17// files.
18package main
19
20import (
21	"flag"
22	"fmt"
23	"io/ioutil"
24	"os"
25	"path/filepath"
26	"strings"
27
28	"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/gen"
29	"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/parser"
30	"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/resolver"
31	"dawn.googlesource.com/tint/tools/src/fileutils"
32	"dawn.googlesource.com/tint/tools/src/glob"
33)
34
35const defProjectRelPath = "src/intrinsics.def"
36
37func main() {
38	if err := run(); err != nil {
39		fmt.Println(err)
40		os.Exit(1)
41	}
42}
43
44func showUsage() {
45	fmt.Println(`
46intrinsic-gen generates the intrinsic table for the Tint compiler
47
48intrinsic-gen parses the <tint>/src/intrinsics.def file, then scans the project
49directory for '<file>.tmpl' files, to produce '<file>' source code files.
50
51usage:
52  intrinsic-gen
53
54optional flags:`)
55	flag.PrintDefaults()
56	fmt.Println(``)
57	os.Exit(1)
58}
59
60func run() error {
61	// Load the intrinsics definition file
62	projectRoot := fileutils.ProjectRoot()
63	defPath := filepath.Join(projectRoot, defProjectRelPath)
64
65	defSource, err := ioutil.ReadFile(defPath)
66	if err != nil {
67		return err
68	}
69
70	// Parse the definition file to produce an AST
71	ast, err := parser.Parse(string(defSource), defProjectRelPath)
72	if err != nil {
73		return err
74	}
75
76	// Resolve the AST to produce the semantic info
77	sem, err := resolver.Resolve(ast)
78	if err != nil {
79		return err
80	}
81
82	// Recursively find all the template files in the <tint>/src directory
83	files, err := glob.Scan(projectRoot, glob.MustParseConfig(`{
84		"paths": [{"include": [
85			"src/**.tmpl",
86			"test/**.tmpl"
87		]}]
88	}`))
89	if err != nil {
90		return err
91	}
92
93	// For each template file...
94	for _, relTmplPath := range files {
95		// Make tmplPath absolute
96		tmplPath := filepath.Join(projectRoot, relTmplPath)
97
98		// Read the template file
99		tmpl, err := ioutil.ReadFile(tmplPath)
100		if err != nil {
101			return fmt.Errorf("failed to open '%v': %w", tmplPath, err)
102		}
103
104		// Create or update the file at relpath if the file content has changed
105		// relpath is a path relative to the template
106		writeFile := func(relpath, body string) error {
107			// Write the common file header
108			sb := strings.Builder{}
109			sb.WriteString(fmt.Sprintf(header, filepath.ToSlash(relTmplPath), filepath.ToSlash(defProjectRelPath)))
110			sb.WriteString(body)
111			content := sb.String()
112			abspath := filepath.Join(filepath.Dir(tmplPath), relpath)
113			return writeFileIfChanged(abspath, content)
114		}
115
116		// Write the content generated using the template and semantic info
117		sb := strings.Builder{}
118		if err := gen.Generate(sem, string(tmpl), &sb, writeFile); err != nil {
119			return fmt.Errorf("while processing '%v': %w", tmplPath, err)
120		}
121
122		if body := sb.String(); body != "" {
123			_, tmplFileName := filepath.Split(tmplPath)
124			outFileName := strings.TrimSuffix(tmplFileName, ".tmpl")
125			if err := writeFile(outFileName, body); err != nil {
126				return err
127			}
128		}
129	}
130
131	return nil
132}
133
134// writes content to path if the file has changed
135func writeFileIfChanged(path, content string) error {
136	existing, err := ioutil.ReadFile(path)
137	if err == nil && string(existing) == content {
138		return nil // Not changed
139	}
140	if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
141		return fmt.Errorf("failed to create directory for '%v': %w", path, err)
142	}
143	if err := ioutil.WriteFile(path, []byte(content), 0666); err != nil {
144		return fmt.Errorf("failed to write file '%v': %w", path, err)
145	}
146	return nil
147}
148
149const header = `// Copyright 2021 The Tint Authors.
150//
151// Licensed under the Apache License, Version 2.0 (the "License");
152// you may not use this file except in compliance with the License.
153// You may obtain a copy of the License at
154//
155//     http://www.apache.org/licenses/LICENSE-2.0
156//
157// Unless required by applicable law or agreed to in writing, software
158// distributed under the License is distributed on an "AS IS" BASIS,
159// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
160// See the License for the specific language governing permissions and
161// limitations under the License.
162
163////////////////////////////////////////////////////////////////////////////////
164// File generated by tools/intrinsic-gen
165// using the template:
166//   %v
167// and the intrinsic defintion file:
168//   %v
169//
170// Do not modify this file directly
171////////////////////////////////////////////////////////////////////////////////
172
173`
174