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