blob: a9ca7ab97ae5580364808ea97d8126e1ec5f2721 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// +build ignore
// This file generates exceptions.gen.go.
// It embeds the text of all the license LREs in the subdirectory "exceptions"
// and constructs the data structures to represent them.
// Run by a "go:generate" comment in licenses.go.
// Modified from github.com/google/licensecheck/gen_data.go.
package main
import (
"bytes"
"flag"
"fmt"
"go/format"
"log"
"os"
"path/filepath"
"sort"
"strings"
"text/template"
"github.com/google/licensecheck"
)
func main() {
log.SetFlags(0)
log.SetPrefix("gen: ")
flag.Parse()
filesLRE, err := filepath.Glob(filepath.Join("exceptions", "*.lre"))
if err != nil {
log.Fatal(err)
}
if len(filesLRE) == 0 {
log.Fatal("no license files")
}
code := outputTemplate
out := new(bytes.Buffer)
builtLRE := buildLRE(filesLRE)
for _, file := range builtLRE {
fmt.Fprintf(out, "\t\t{ID: %q, %s LRE: %v},\n", file.Name, file.Type, varName(file.Name+".lre"))
}
code = strings.Replace(code, "FILES_LIST", out.String(), -1)
out.Reset()
for _, file := range builtLRE {
if len(file.Types) == 0 {
continue
}
var ss []string
for _, t := range file.Types {
ss = append(ss, fmt.Sprintf("%q", t))
}
fmt.Fprintf(out, "\t\t%q: {%s},\n", file.Name, strings.Join(ss, ", "))
}
code = strings.Replace(code, "TYPES_LIST", out.String(), -1)
out.Reset()
for _, file := range builtLRE {
fmt.Fprintf(out, "const %s = `%s`\n",
varName(file.Name+".lre"),
bytes.ReplaceAll(file.Data, []byte("`"), []byte("` + \"`\" + `")))
}
code += out.String()
src, err := format.Source([]byte(code))
if err != nil {
fd, err1 := io.TempFile("", "license-data")
if err1 == nil {
_, err1 = fd.Write([]byte(code))
if err1 == nil {
log.Fatalf("parsing output (written to %s): %v", fd.Name(), err)
}
fd.Close()
}
log.Fatal("parsing output:", err)
}
err = os.WriteFile("exceptions.gen.go", src, 0644)
if err != nil {
log.Fatal(err)
}
}
// varName returns the basename of the file, sanitized for use as a variable name,
// and given the prefix "license_".
func varName(file string) string {
mapping := func(r rune) rune {
if r == '-' || r == '.' || r == '+' {
return '_'
}
return r
}
return "license_" + strings.Map(mapping, filepath.Base(file))
}
const outputTemplate = `
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by gen_exceptions.go; DO NOT EDIT.
//lint:file-ignore ST1018 Ignore staticcheck message about Unicode control characters.
package licenses
import "github.com/google/licensecheck"
var exceptionLicenses = []licensecheck.License{
FILES_LIST
}
var exceptionTypes = map[string][]string{
TYPES_LIST
}
`
type fileData struct {
Name string
Type string // licensecheck Type, not used
Types []string // reported types
Data []byte
}
func buildLRE(filesLRE []string) []fileData {
// setType obtains a licensecheck.Type from the template.
// This is a bitset of license properties. Neither the licensecheck package
// nor this package uses it.
var typ licensecheck.Type
setType := func(s string) (string, error) {
t, err := licensecheck.ParseType(s)
if err != nil {
return "", err
}
typ = t
return "", nil
}
// setTypes sets the reported license types for this package.
var types []string
setTypes := func(s string) string {
types = strings.Fields(s)
return ""
}
var out []fileData
t := template.New("").Funcs(template.FuncMap{
"list": templateList,
"Type": setType,
"Types": setTypes,
})
t, err := t.ParseFiles(filesLRE...)
if err != nil {
log.Fatal("parsing LRE templates:", err)
}
for _, t := range t.Templates() {
if strings.HasSuffix(t.Name(), ".lre") {
var buf bytes.Buffer
typ = licensecheck.Unknown
types = nil
if err := t.Execute(&buf, nil); err != nil {
log.Fatalf("executing %s: %v", t.Name(), err)
}
if len(bytes.TrimSpace(buf.Bytes())) == 0 {
// Only contained useful definitions.
continue
}
tstr := ""
if typ != licensecheck.Unknown {
tstr = "Type: " + typ.String() + ","
}
out = append(out, fileData{strings.TrimSuffix(t.Name(), ".lre"), tstr, types, buf.Bytes()})
}
}
sort.Slice(out, func(i, j int) bool {
ni, nj := out[i].Name, out[j].Name
// Special case: BSD-4-Clause is a generalization of BSD-4-Clause-UC.
// In case of multiple matches, licensecheck always returns the one earlier in the list.
// Make BSD-4-Clause-UC the one earlier in the list.
if strings.HasPrefix(ni, "BSD-4-Clause") && strings.HasPrefix(nj, "BSD-4-Clause") {
ni, nj = nj, ni
}
// Special case: GFDL-1.[123]-invariants-* is a generalization of GFDL-1.[123]-no-invariants-*.
// Reverse that order too.
if strings.HasPrefix(ni, "GFDL-") && strings.HasPrefix(nj, "GFDL-") {
ni, nj = nj, ni
}
return ni < nj
})
return out
}
// templateList returns xs, but it flattens any nested []interface{} into the main list.
// Called from templates as "list", to pass multiple arguments to templates.
func templateList(xs ...interface{}) []interface{} {
var list []interface{}
for _, x := range xs {
switch x := x.(type) {
case []interface{}:
list = append(list, x...)
default:
list = append(list, x)
}
}
return list
}