blob: 8c9cbb4e702070ec98dd3dfc94ddadc79005f33d [file] [log] [blame]
// Copyright 2020 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.
// +build ignore
// This file generates exceptions.gen.go.
// It builds a map from the files in the subdirectory "exception-files".
// Run by a "go:generate" comment in licenses.go.
package main
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
"go/format"
"io/ioutil"
"log"
"path/filepath"
"strings"
)
const outfile = "exceptions.gen.go"
type fileData struct {
key [sha256.Size]byte
source string
types []string
}
func main() {
files, err := filepath.Glob(filepath.Join("exception-files", "*"))
if err != nil {
log.Fatal(err)
}
if len(files) == 0 {
log.Fatal("no files")
}
out := new(bytes.Buffer)
fmt.Fprint(out, `
// Copyright 2020 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_data.go; DO NOT EDIT.
package licenses
import (
"bytes"
"crypto/sha256"
)
// exceptionFileTypesMap maps SHAs of normalized licenses (see the exceptionKey function)
// to a list of types for each license.
var exceptionFileTypesMap = map[[sha256.Size]byte][]string{
`)
for _, file := range files {
base := filepath.Base(file)
if base == "README" {
continue
}
// Ignore common editor backup files.
if strings.HasSuffix(base, "~") || strings.HasSuffix(base, ".swp") {
continue
}
data, err := readFile(file)
if err != nil {
log.Fatalf("%s: %v", file, err)
}
fmt.Fprintf(out, "\n// file %s\n", base)
fmt.Fprintf(out, "// from %s\n", data.source)
fmt.Fprintf(out, "\t%#v: %#v,\n", data.key, data.types)
}
fmt.Fprintf(out, "}\n")
fmt.Fprintln(out, exceptionKeyText)
src, err := format.Source(out.Bytes())
if err != nil {
fmt.Println(string(out.Bytes()))
log.Fatalf("format.Source: %v", err)
}
if err := ioutil.WriteFile(outfile, src, 0640); err != nil {
log.Fatal(err)
}
fmt.Printf("wrote %s\n", outfile)
}
func readFile(file string) (_ *fileData, err error) {
data, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
parts := bytes.SplitN(data, []byte("\n\n"), 2)
if len(parts) != 2 {
return nil, errors.New("missing blank line separating header")
}
header, err := parseHeader(parts[0])
if err != nil {
return nil, err
}
source := header["source"]
types := header["types"]
if source == "" || types == "" {
log.Fatal("%s: missing header: source=%q, types=%q", file, source, types)
}
return &fileData{
key: exceptionKey(parts[1]),
source: source,
types: strings.Fields(types),
}, nil
}
func parseHeader(h []byte) (map[string]string, error) {
m := map[string]string{}
lines := strings.Split(string(h), "\n")
for _, l := range lines {
kv := strings.SplitN(l, ":", 2)
if len(kv) != 2 {
return nil, fmt.Errorf("malformed header line %q", l)
}
m[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
}
return m, nil
}
// exceptionKey computes the key for the exceptionFileTypesMap.
// Keep in sync with exceptionKeyText, below.
func exceptionKey(data []byte) [sha256.Size]byte {
norm := bytes.Join(bytes.Fields(bytes.ToLower(data)), []byte{' '})
return sha256.Sum256(norm)
}
const exceptionKeyText = `
// exceptionKey computes the key for the exceptionFileTypesMap.
func exceptionKey(data []byte) [sha256.Size]byte {
norm := bytes.Join(bytes.Fields(bytes.ToLower(data)), []byte{' '})
return sha256.Sum256(norm)
}
`