// Copyright 2014 The go-gl/mathgl Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build ignore // codegen generates go code from templates. Intended to be // used with go generate; Also makes mgl64 from mgl32. // See the invocation in mgl32/util.go for details. // To use it, just run "go generate github.com/go-gl/mathgl/mgl32" // (or "go generate" in mgl32 directory). package main import ( "bytes" "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "text/template" ) type Context struct { Comment string TemplateName string } type MatrixIter struct { M int // row N int // column index int } var mgl64RewriteRules = []string{ "mgl32 -> mgl64", "float32 -> float64", "f32 -> f64", "a.Float32 -> a.Float64", "math.MaxFloat32 -> math.MaxFloat64", "math.SmallestNonzeroFloat32 -> math.SmallestNonzeroFloat64", } func main() { flag.Usage = func() { fmt.Println("Usage: codegen -template file.tmpl -output file.go") fmt.Println("Usage: codegen -mgl64 [-dir ../mgl64]") flag.PrintDefaults() } tmplPath := flag.String("template", "file.tmpl", "template path") oPath := flag.String("output", "file.go", "output path") mgl64 := flag.Bool("mgl64", false, "make mgl64") mgl64Path := flag.String("dir", "../mgl64", "path to mgl64 location") flag.Parse() if flag.NArg() > 0 || flag.NFlag() == 0 { flag.Usage() os.Exit(2) } if *mgl64 { genMgl64(*mgl64Path) return } tmpl := template.New("").Delims("<<", ">>").Funcs(template.FuncMap{ "typename": typenameHelper, "elementname": elementNameHelper, "iter": iterHelper, "matiter": matrixIterHelper, "enum": enumHelper, "sep": separatorHelper, "repeat": repeatHelper, "add": addHelper, "mul": mulHelper, }) tmpl = template.Must(tmpl.ParseFiles(*tmplPath)) tmplName := filepath.Base(*tmplPath) oFile, err := os.Create(*oPath) if err != nil { panic(err) } context := Context{ Comment: "This file is generated by codegen.go; DO NOT EDIT", TemplateName: tmplName, } if err = tmpl.ExecuteTemplate(oFile, tmplName, context); err != nil { panic(err) } oFile.Close() if err = rungofmt(*oPath, false, nil); err != nil { panic(err) } } func genMgl64(destPath string) { HandleFile := func(source string, info os.FileInfo, err error) error { if err != nil { return err } dest := filepath.Join(destPath, source) if info.IsDir() { return os.MkdirAll(dest, info.Mode()) } if !strings.HasSuffix(source, ".go") || info.Name() == "codegen.go" { return nil } if !info.Mode().IsRegular() { fmt.Println("Ignored, not a regular file:", source) return nil } in, err := ioutil.ReadFile(source) if err != nil { return err } out, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode()) if err != nil { return err } defer out.Close() comment := fmt.Sprintf( "// This file is generated from mgl32/%s; DO NOT EDIT\n\n", source) if _, err = out.WriteString(comment); err != nil { return err } r := strings.NewReplacer("//go:generate ", "//#go:generate ") // We don't want go generate directives in mgl64 package. if _, err = r.WriteString(out, string(in)); err != nil { return err } return rungofmt(dest, true, mgl64RewriteRules) } if err := filepath.Walk(".", HandleFile); err != nil { panic(err) } } func rungofmt(path string, fiximports bool, rewriteRules []string) error { args := []string{"-w", path} output, err := exec.Command("gofmt", args...).CombinedOutput() for i := 0; err == nil && i < len(rewriteRules); i++ { args = []string{"-w", "-r", rewriteRules[i], path} output, err = exec.Command("gofmt", args...).CombinedOutput() } if fiximports && err == nil { args = []string{"-w", path} output, err = exec.Command("goimports", args...).CombinedOutput() } if err != nil { fmt.Println("Error executing gofmt", strings.Join(args, " ")) os.Stdout.Write(output) } return err } func typenameHelper(m, n int) string { if m == 1 { return fmt.Sprintf("Vec%d", n) } if n == 1 { return fmt.Sprintf("Vec%d", m) } if m == n { return fmt.Sprintf("Mat%d", m) } return fmt.Sprintf("Mat%dx%d", m, n) } func elementNameHelper(m int) string { switch m { case 0: return "X" case 1: return "Y" case 2: return "Z" case 3: return "W" default: panic("Can't generate element name") } } func iterHelper(start, end int) []int { iter := make([]int, end-start) for i := start; i < end; i++ { iter[i] = i } return iter } func matrixIterHelper(rows, cols int) []MatrixIter { res := make([]MatrixIter, 0, rows*cols) for n := 0; n < cols; n++ { for m := 0; m < rows; m++ { res = append(res, MatrixIter{ M: m, N: n, index: n*rows + m, }) } } return res } // Template function that returns slice from its arguments. Indended to be used // in range loops. func enumHelper(args ...int) []int { return args } // Template function to insert commas and '+' in range loops. func separatorHelper(sep string, iterCond int) string { if iterCond > 0 { return sep } return "" } // Template function to repeat string 'count' times. Inserting 'sep' between // repetitions. Also changes all occurrences of '%d' to repetition number. // For example, repeatHelper(3, "col%d", ",") will output "col0, col1, col2" func repeatHelper(count int, text string, sep string) string { var res bytes.Buffer for i := 0; i < count; i++ { if i > 0 { res.WriteString(sep) } res.WriteString(strings.Replace(text, "%d", fmt.Sprintf("%d", i), -1)) } return res.String() } func addHelper(args ...int) int { res := 0 for _, a := range args { res += a } return res } func mulHelper(args ...int) int { res := 1 for _, a := range args { res *= a } return res } func (i MatrixIter) String() string { return fmt.Sprintf("%d", i.index) }