summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge.go522
-rw-r--r--dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge_test.go506
2 files changed, 1028 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge.go b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge.go
new file mode 100644
index 0000000..3dda84d
--- /dev/null
+++ b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge.go
@@ -0,0 +1,522 @@
+// 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.
+
+// The mkmerge command parses generated source files and merges common
+// consts, funcs, and types into a common source file, per GOOS.
+//
+// Usage:
+//
+// $ mkmerge -out MERGED FILE [FILE ...]
+//
+// Example:
+//
+// # Remove all common consts, funcs, and types from zerrors_linux_*.go
+// # and write the common code into zerrors_linux.go
+// $ mkmerge -out zerrors_linux.go zerrors_linux_*.go
+//
+// mkmerge performs the merge in the following steps:
+// 1. Construct the set of common code that is identical in all
+// architecture-specific files.
+// 2. Write this common code to the merged file.
+// 3. Remove the common code from all architecture-specific files.
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+const validGOOS = "aix|darwin|dragonfly|freebsd|linux|netbsd|openbsd|solaris"
+
+// getValidGOOS returns GOOS, true if filename ends with a valid "_GOOS.go"
+func getValidGOOS(filename string) (string, bool) {
+ matches := regexp.MustCompile(`_(` + validGOOS + `)\.go$`).FindStringSubmatch(filename)
+ if len(matches) != 2 {
+ return "", false
+ }
+ return matches[1], true
+}
+
+// codeElem represents an ast.Decl in a comparable way.
+type codeElem struct {
+ tok token.Token // e.g. token.CONST, token.TYPE, or token.FUNC
+ src string // the declaration formatted as source code
+}
+
+// newCodeElem returns a codeElem based on tok and node, or an error is returned.
+func newCodeElem(tok token.Token, node ast.Node) (codeElem, error) {
+ var b strings.Builder
+ err := format.Node(&b, token.NewFileSet(), node)
+ if err != nil {
+ return codeElem{}, err
+ }
+ return codeElem{tok, b.String()}, nil
+}
+
+// codeSet is a set of codeElems
+type codeSet struct {
+ set map[codeElem]bool // true for all codeElems in the set
+}
+
+// newCodeSet returns a new codeSet
+func newCodeSet() *codeSet { return &codeSet{make(map[codeElem]bool)} }
+
+// add adds elem to c
+func (c *codeSet) add(elem codeElem) { c.set[elem] = true }
+
+// has returns true if elem is in c
+func (c *codeSet) has(elem codeElem) bool { return c.set[elem] }
+
+// isEmpty returns true if the set is empty
+func (c *codeSet) isEmpty() bool { return len(c.set) == 0 }
+
+// intersection returns a new set which is the intersection of c and a
+func (c *codeSet) intersection(a *codeSet) *codeSet {
+ res := newCodeSet()
+
+ for elem := range c.set {
+ if a.has(elem) {
+ res.add(elem)
+ }
+ }
+ return res
+}
+
+// keepCommon is a filterFn for filtering the merged file with common declarations.
+func (c *codeSet) keepCommon(elem codeElem) bool {
+ switch elem.tok {
+ case token.VAR:
+ // Remove all vars from the merged file
+ return false
+ case token.CONST, token.TYPE, token.FUNC, token.COMMENT:
+ // Remove arch-specific consts, types, functions, and file-level comments from the merged file
+ return c.has(elem)
+ case token.IMPORT:
+ // Keep imports, they are handled by filterImports
+ return true
+ }
+
+ log.Fatalf("keepCommon: invalid elem %v", elem)
+ return true
+}
+
+// keepArchSpecific is a filterFn for filtering the GOARC-specific files.
+func (c *codeSet) keepArchSpecific(elem codeElem) bool {
+ switch elem.tok {
+ case token.CONST, token.TYPE, token.FUNC:
+ // Remove common consts, types, or functions from the arch-specific file
+ return !c.has(elem)
+ }
+ return true
+}
+
+// srcFile represents a source file
+type srcFile struct {
+ name string
+ src []byte
+}
+
+// filterFn is a helper for filter
+type filterFn func(codeElem) bool
+
+// filter parses and filters Go source code from src, removing top
+// level declarations using keep as predicate.
+// For src parameter, please see docs for parser.ParseFile.
+func filter(src interface{}, keep filterFn) ([]byte, error) {
+ // Parse the src into an ast
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+ if err != nil {
+ return nil, err
+ }
+ cmap := ast.NewCommentMap(fset, f, f.Comments)
+
+ // Group const/type specs on adjacent lines
+ var groups specGroups = make(map[string]int)
+ var groupID int
+
+ decls := f.Decls
+ f.Decls = f.Decls[:0]
+ for _, decl := range decls {
+ switch decl := decl.(type) {
+ case *ast.GenDecl:
+ // Filter imports, consts, types, vars
+ specs := decl.Specs
+ decl.Specs = decl.Specs[:0]
+ for i, spec := range specs {
+ elem, err := newCodeElem(decl.Tok, spec)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create new group if there are empty lines between this and the previous spec
+ if i > 0 && fset.Position(specs[i-1].End()).Line < fset.Position(spec.Pos()).Line-1 {
+ groupID++
+ }
+
+ // Check if we should keep this spec
+ if keep(elem) {
+ decl.Specs = append(decl.Specs, spec)
+ groups.add(elem.src, groupID)
+ }
+ }
+ // Check if we should keep this decl
+ if len(decl.Specs) > 0 {
+ f.Decls = append(f.Decls, decl)
+ }
+ case *ast.FuncDecl:
+ // Filter funcs
+ elem, err := newCodeElem(token.FUNC, decl)
+ if err != nil {
+ return nil, err
+ }
+ if keep(elem) {
+ f.Decls = append(f.Decls, decl)
+ }
+ }
+ }
+
+ // Filter file level comments
+ if cmap[f] != nil {
+ commentGroups := cmap[f]
+ cmap[f] = cmap[f][:0]
+ for _, cGrp := range commentGroups {
+ if keep(codeElem{token.COMMENT, cGrp.Text()}) {
+ cmap[f] = append(cmap[f], cGrp)
+ }
+ }
+ }
+ f.Comments = cmap.Filter(f).Comments()
+
+ // Generate code for the filtered ast
+ var buf bytes.Buffer
+ if err = format.Node(&buf, fset, f); err != nil {
+ return nil, err
+ }
+
+ groupedSrc, err := groups.filterEmptyLines(&buf)
+ if err != nil {
+ return nil, err
+ }
+
+ return filterImports(groupedSrc)
+}
+
+// getCommonSet returns the set of consts, types, and funcs that are present in every file.
+func getCommonSet(files []srcFile) (*codeSet, error) {
+ if len(files) == 0 {
+ return nil, fmt.Errorf("no files provided")
+ }
+ // Use the first architecture file as the baseline
+ baseSet, err := getCodeSet(files[0].src)
+ if err != nil {
+ return nil, err
+ }
+
+ // Compare baseline set with other architecture files: discard any element,
+ // that doesn't exist in other architecture files.
+ for _, f := range files[1:] {
+ set, err := getCodeSet(f.src)
+ if err != nil {
+ return nil, err
+ }
+
+ baseSet = baseSet.intersection(set)
+ }
+ return baseSet, nil
+}
+
+// getCodeSet returns the set of all top-level consts, types, and funcs from src.
+// src must be string, []byte, or io.Reader (see go/parser.ParseFile docs)
+func getCodeSet(src interface{}) (*codeSet, error) {
+ set := newCodeSet()
+
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, decl := range f.Decls {
+ switch decl := decl.(type) {
+ case *ast.GenDecl:
+ // Add const, and type declarations
+ if !(decl.Tok == token.CONST || decl.Tok == token.TYPE) {
+ break
+ }
+
+ for _, spec := range decl.Specs {
+ elem, err := newCodeElem(decl.Tok, spec)
+ if err != nil {
+ return nil, err
+ }
+
+ set.add(elem)
+ }
+ case *ast.FuncDecl:
+ // Add func declarations
+ elem, err := newCodeElem(token.FUNC, decl)
+ if err != nil {
+ return nil, err
+ }
+
+ set.add(elem)
+ }
+ }
+
+ // Add file level comments
+ cmap := ast.NewCommentMap(fset, f, f.Comments)
+ for _, cGrp := range cmap[f] {
+ set.add(codeElem{token.COMMENT, cGrp.Text()})
+ }
+
+ return set, nil
+}
+
+// importName returns the identifier (PackageName) for an imported package
+func importName(iSpec *ast.ImportSpec) (string, error) {
+ if iSpec.Name == nil {
+ name, err := strconv.Unquote(iSpec.Path.Value)
+ if err != nil {
+ return "", err
+ }
+ return path.Base(name), nil
+ }
+ return iSpec.Name.Name, nil
+}
+
+// specGroups tracks grouped const/type specs with a map of line: groupID pairs
+type specGroups map[string]int
+
+// add spec source to group
+func (s specGroups) add(src string, groupID int) error {
+ srcBytes, err := format.Source(bytes.TrimSpace([]byte(src)))
+ if err != nil {
+ return err
+ }
+ s[string(srcBytes)] = groupID
+ return nil
+}
+
+// filterEmptyLines removes empty lines within groups of const/type specs.
+// Returns the filtered source.
+func (s specGroups) filterEmptyLines(src io.Reader) ([]byte, error) {
+ scanner := bufio.NewScanner(src)
+ var out bytes.Buffer
+
+ var emptyLines bytes.Buffer
+ prevGroupID := -1 // Initialize to invalid group
+ for scanner.Scan() {
+ line := bytes.TrimSpace(scanner.Bytes())
+
+ if len(line) == 0 {
+ fmt.Fprintf(&emptyLines, "%s\n", scanner.Bytes())
+ continue
+ }
+
+ // Discard emptyLines if previous non-empty line belonged to the same
+ // group as this line
+ if src, err := format.Source(line); err == nil {
+ groupID, ok := s[string(src)]
+ if ok && groupID == prevGroupID {
+ emptyLines.Reset()
+ }
+ prevGroupID = groupID
+ }
+
+ emptyLines.WriteTo(&out)
+ fmt.Fprintf(&out, "%s\n", scanner.Bytes())
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ return out.Bytes(), nil
+}
+
+// filterImports removes unused imports from fileSrc, and returns a formatted src.
+func filterImports(fileSrc []byte) ([]byte, error) {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "", fileSrc, parser.ParseComments)
+ if err != nil {
+ return nil, err
+ }
+ cmap := ast.NewCommentMap(fset, file, file.Comments)
+
+ // create set of references to imported identifiers
+ keepImport := make(map[string]bool)
+ for _, u := range file.Unresolved {
+ keepImport[u.Name] = true
+ }
+
+ // filter import declarations
+ decls := file.Decls
+ file.Decls = file.Decls[:0]
+ for _, decl := range decls {
+ importDecl, ok := decl.(*ast.GenDecl)
+
+ // Keep non-import declarations
+ if !ok || importDecl.Tok != token.IMPORT {
+ file.Decls = append(file.Decls, decl)
+ continue
+ }
+
+ // Filter the import specs
+ specs := importDecl.Specs
+ importDecl.Specs = importDecl.Specs[:0]
+ for _, spec := range specs {
+ iSpec := spec.(*ast.ImportSpec)
+ name, err := importName(iSpec)
+ if err != nil {
+ return nil, err
+ }
+
+ if keepImport[name] {
+ importDecl.Specs = append(importDecl.Specs, iSpec)
+ }
+ }
+ if len(importDecl.Specs) > 0 {
+ file.Decls = append(file.Decls, importDecl)
+ }
+ }
+
+ // filter file.Imports
+ imports := file.Imports
+ file.Imports = file.Imports[:0]
+ for _, spec := range imports {
+ name, err := importName(spec)
+ if err != nil {
+ return nil, err
+ }
+
+ if keepImport[name] {
+ file.Imports = append(file.Imports, spec)
+ }
+ }
+ file.Comments = cmap.Filter(file).Comments()
+
+ var buf bytes.Buffer
+ err = format.Node(&buf, fset, file)
+ if err != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+// merge extracts duplicate code from archFiles and merges it to mergeFile.
+// 1. Construct commonSet: the set of code that is idential in all archFiles.
+// 2. Write the code in commonSet to mergedFile.
+// 3. Remove the commonSet code from all archFiles.
+func merge(mergedFile string, archFiles ...string) error {
+ // extract and validate the GOOS part of the merged filename
+ goos, ok := getValidGOOS(mergedFile)
+ if !ok {
+ return fmt.Errorf("invalid GOOS in merged file name %s", mergedFile)
+ }
+
+ // Read architecture files
+ var inSrc []srcFile
+ for _, file := range archFiles {
+ src, err := ioutil.ReadFile(file)
+ if err != nil {
+ return fmt.Errorf("cannot read archfile %s: %w", file, err)
+ }
+
+ inSrc = append(inSrc, srcFile{file, src})
+ }
+
+ // 1. Construct the set of top-level declarations common for all files
+ commonSet, err := getCommonSet(inSrc)
+ if err != nil {
+ return err
+ }
+ if commonSet.isEmpty() {
+ // No common code => do not modify any files
+ return nil
+ }
+
+ // 2. Write the merged file
+ mergedSrc, err := filter(inSrc[0].src, commonSet.keepCommon)
+ if err != nil {
+ return err
+ }
+
+ f, err := os.Create(mergedFile)
+ if err != nil {
+ return err
+ }
+
+ buf := bufio.NewWriter(f)
+ fmt.Fprintln(buf, "// Code generated by mkmerge; DO NOT EDIT.")
+ fmt.Fprintln(buf)
+ fmt.Fprintf(buf, "//go:build %s\n", goos)
+ fmt.Fprintf(buf, "// +build %s\n", goos)
+ fmt.Fprintln(buf)
+ buf.Write(mergedSrc)
+
+ err = buf.Flush()
+ if err != nil {
+ return err
+ }
+ err = f.Close()
+ if err != nil {
+ return err
+ }
+
+ // 3. Remove duplicate declarations from the architecture files
+ for _, inFile := range inSrc {
+ src, err := filter(inFile.src, commonSet.keepArchSpecific)
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(inFile.name, src, 0644)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func main() {
+ var mergedFile string
+ flag.StringVar(&mergedFile, "out", "", "Write merged code to `FILE`")
+ flag.Parse()
+
+ // Expand wildcards
+ var filenames []string
+ for _, arg := range flag.Args() {
+ matches, err := filepath.Glob(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Invalid command line argument %q: %v\n", arg, err)
+ os.Exit(1)
+ }
+ filenames = append(filenames, matches...)
+ }
+
+ if len(filenames) < 2 {
+ // No need to merge
+ return
+ }
+
+ err := merge(mergedFile, filenames...)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Merge failed with error: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge_test.go b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge_test.go
new file mode 100644
index 0000000..e61d7d4
--- /dev/null
+++ b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/internal/mkmerge/mkmerge_test.go
@@ -0,0 +1,506 @@
+// 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.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/parser"
+ "go/token"
+ "html/template"
+ "strings"
+ "testing"
+)
+
+func TestImports(t *testing.T) {
+ t.Run("importName", func(t *testing.T) {
+ cases := []struct {
+ src string
+ ident string
+ }{
+ {`"syscall"`, "syscall"},
+ {`. "foobar"`, "."},
+ {`"go/ast"`, "ast"},
+ {`moo "go/format"`, "moo"},
+ {`. "go/token"`, "."},
+ {`"golang.org/x/sys/unix"`, "unix"},
+ {`nix "golang.org/x/sys/unix"`, "nix"},
+ {`_ "golang.org/x/sys/unix"`, "_"},
+ }
+
+ for _, c := range cases {
+ pkgSrc := fmt.Sprintf("package main\nimport %s", c.src)
+
+ f, err := parser.ParseFile(token.NewFileSet(), "", pkgSrc, parser.ImportsOnly)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if len(f.Imports) != 1 {
+ t.Errorf("Got %d imports, expected 1", len(f.Imports))
+ continue
+ }
+
+ got, err := importName(f.Imports[0])
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != c.ident {
+ t.Errorf("Got %q, expected %q", got, c.ident)
+ }
+ }
+ })
+
+ t.Run("filterImports", func(t *testing.T) {
+ cases := []struct{ before, after string }{
+ {`package test
+
+ import (
+ "foo"
+ "bar"
+ )`,
+ "package test\n"},
+ {`package test
+
+ import (
+ "foo"
+ "bar"
+ )
+
+ func useFoo() { foo.Usage() }`,
+ `package test
+
+import (
+ "foo"
+)
+
+func useFoo() { foo.Usage() }
+`},
+ }
+ for _, c := range cases {
+ got, err := filterImports([]byte(c.before))
+ if err != nil {
+ t.Error(err)
+ }
+
+ if string(got) != c.after {
+ t.Errorf("Got:\n%s\nExpected:\n%s\n", got, c.after)
+ }
+ }
+ })
+}
+
+func TestMerge(t *testing.T) {
+ // Input architecture files
+ inTmpl := template.Must(template.New("input").Parse(`
+// Package comments
+
+// build directives for arch{{.}}
+
+//go:build goos && arch{{.}}
+// +build goos,arch{{.}}
+
+package main
+
+/*
+#include <stdint.h>
+#include <stddef.h>
+int utimes(uintptr_t, uintptr_t);
+int utimensat(int, uintptr_t, uintptr_t, int);
+*/
+import "C"
+
+// The imports
+import (
+ "commonDep"
+ "uniqueDep{{.}}"
+)
+
+// Vars
+var (
+ commonVar = commonDep.Use("common")
+
+ uniqueVar{{.}} = "unique{{.}}"
+)
+
+// Common free standing comment
+
+// Common comment
+const COMMON_INDEPENDENT = 1234
+const UNIQUE_INDEPENDENT_{{.}} = "UNIQUE_INDEPENDENT_{{.}}"
+
+// Group comment
+const (
+ COMMON_GROUP = "COMMON_GROUP"
+ UNIQUE_GROUP_{{.}} = "UNIQUE_GROUP_{{.}}"
+)
+
+// Group2 comment
+const (
+ UNIQUE_GROUP21_{{.}} = "UNIQUE_GROUP21_{{.}}"
+ UNIQUE_GROUP22_{{.}} = "UNIQUE_GROUP22_{{.}}"
+)
+
+// Group3 comment
+const (
+ sub1Common1 = 11
+ sub1Unique2{{.}} = 12
+ sub1Common3_LONG = 13
+
+ sub2Unique1{{.}} = 21
+ sub2Common2 = 22
+ sub2Common3 = 23
+ sub2Unique4{{.}} = 24
+)
+
+type commonInt int
+
+type uniqueInt{{.}} int
+
+func commonF() string {
+ return commonDep.Use("common")
+ }
+
+func uniqueF() string {
+ C.utimes(0, 0)
+ return uniqueDep{{.}}.Use("{{.}}")
+ }
+
+// Group4 comment
+const (
+ sub3Common1 = 31
+ sub3Unique2{{.}} = 32
+ sub3Unique3{{.}} = 33
+ sub3Common4 = 34
+
+ sub4Common1, sub4Unique2{{.}} = 41, 42
+ sub4Unique3{{.}}, sub4Common4 = 43, 44
+)
+`))
+
+ // Filtered architecture files
+ outTmpl := template.Must(template.New("output").Parse(`// Package comments
+
+// build directives for arch{{.}}
+
+//go:build goos && arch{{.}}
+// +build goos,arch{{.}}
+
+package main
+
+/*
+#include <stdint.h>
+#include <stddef.h>
+int utimes(uintptr_t, uintptr_t);
+int utimensat(int, uintptr_t, uintptr_t, int);
+*/
+import "C"
+
+// The imports
+import (
+ "commonDep"
+ "uniqueDep{{.}}"
+)
+
+// Vars
+var (
+ commonVar = commonDep.Use("common")
+
+ uniqueVar{{.}} = "unique{{.}}"
+)
+
+const UNIQUE_INDEPENDENT_{{.}} = "UNIQUE_INDEPENDENT_{{.}}"
+
+// Group comment
+const (
+ UNIQUE_GROUP_{{.}} = "UNIQUE_GROUP_{{.}}"
+)
+
+// Group2 comment
+const (
+ UNIQUE_GROUP21_{{.}} = "UNIQUE_GROUP21_{{.}}"
+ UNIQUE_GROUP22_{{.}} = "UNIQUE_GROUP22_{{.}}"
+)
+
+// Group3 comment
+const (
+ sub1Unique2{{.}} = 12
+
+ sub2Unique1{{.}} = 21
+ sub2Unique4{{.}} = 24
+)
+
+type uniqueInt{{.}} int
+
+func uniqueF() string {
+ C.utimes(0, 0)
+ return uniqueDep{{.}}.Use("{{.}}")
+}
+
+// Group4 comment
+const (
+ sub3Unique2{{.}} = 32
+ sub3Unique3{{.}} = 33
+
+ sub4Common1, sub4Unique2{{.}} = 41, 42
+ sub4Unique3{{.}}, sub4Common4 = 43, 44
+)
+`))
+
+ const mergedFile = `// Package comments
+
+package main
+
+// The imports
+import (
+ "commonDep"
+)
+
+// Common free standing comment
+
+// Common comment
+const COMMON_INDEPENDENT = 1234
+
+// Group comment
+const (
+ COMMON_GROUP = "COMMON_GROUP"
+)
+
+// Group3 comment
+const (
+ sub1Common1 = 11
+ sub1Common3_LONG = 13
+
+ sub2Common2 = 22
+ sub2Common3 = 23
+)
+
+type commonInt int
+
+func commonF() string {
+ return commonDep.Use("common")
+}
+
+// Group4 comment
+const (
+ sub3Common1 = 31
+ sub3Common4 = 34
+)
+`
+
+ // Generate source code for different "architectures"
+ var inFiles, outFiles []srcFile
+ for _, arch := range strings.Fields("A B C D") {
+ buf := new(bytes.Buffer)
+ err := inTmpl.Execute(buf, arch)
+ if err != nil {
+ t.Fatal(err)
+ }
+ inFiles = append(inFiles, srcFile{"file" + arch, buf.Bytes()})
+
+ buf = new(bytes.Buffer)
+ err = outTmpl.Execute(buf, arch)
+ if err != nil {
+ t.Fatal(err)
+ }
+ outFiles = append(outFiles, srcFile{"file" + arch, buf.Bytes()})
+ }
+
+ t.Run("getCodeSet", func(t *testing.T) {
+ got, err := getCodeSet(inFiles[0].src)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expectedElems := []codeElem{
+ {token.COMMENT, "Package comments\n"},
+ {token.COMMENT, "build directives for archA\n"},
+ {token.COMMENT, "+build goos,archA\n"},
+ {token.CONST, `COMMON_INDEPENDENT = 1234`},
+ {token.CONST, `UNIQUE_INDEPENDENT_A = "UNIQUE_INDEPENDENT_A"`},
+ {token.CONST, `COMMON_GROUP = "COMMON_GROUP"`},
+ {token.CONST, `UNIQUE_GROUP_A = "UNIQUE_GROUP_A"`},
+ {token.CONST, `UNIQUE_GROUP21_A = "UNIQUE_GROUP21_A"`},
+ {token.CONST, `UNIQUE_GROUP22_A = "UNIQUE_GROUP22_A"`},
+ {token.CONST, `sub1Common1 = 11`},
+ {token.CONST, `sub1Unique2A = 12`},
+ {token.CONST, `sub1Common3_LONG = 13`},
+ {token.CONST, `sub2Unique1A = 21`},
+ {token.CONST, `sub2Common2 = 22`},
+ {token.CONST, `sub2Common3 = 23`},
+ {token.CONST, `sub2Unique4A = 24`},
+ {token.CONST, `sub3Common1 = 31`},
+ {token.CONST, `sub3Unique2A = 32`},
+ {token.CONST, `sub3Unique3A = 33`},
+ {token.CONST, `sub3Common4 = 34`},
+ {token.CONST, `sub4Common1, sub4Unique2A = 41, 42`},
+ {token.CONST, `sub4Unique3A, sub4Common4 = 43, 44`},
+ {token.TYPE, `commonInt int`},
+ {token.TYPE, `uniqueIntA int`},
+ {token.FUNC, `func commonF() string {
+ return commonDep.Use("common")
+}`},
+ {token.FUNC, `func uniqueF() string {
+ C.utimes(0, 0)
+ return uniqueDepA.Use("A")
+}`},
+ }
+ expected := newCodeSet()
+ for _, d := range expectedElems {
+ expected.add(d)
+ }
+
+ if len(got.set) != len(expected.set) {
+ t.Errorf("Got %d codeElems, expected %d", len(got.set), len(expected.set))
+ }
+ for expElem := range expected.set {
+ if !got.has(expElem) {
+ t.Errorf("Didn't get expected codeElem %#v", expElem)
+ }
+ }
+ for gotElem := range got.set {
+ if !expected.has(gotElem) {
+ t.Errorf("Got unexpected codeElem %#v", gotElem)
+ }
+ }
+ })
+
+ t.Run("getCommonSet", func(t *testing.T) {
+ got, err := getCommonSet(inFiles)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := newCodeSet()
+ expected.add(codeElem{token.COMMENT, "Package comments\n"})
+ expected.add(codeElem{token.CONST, `COMMON_INDEPENDENT = 1234`})
+ expected.add(codeElem{token.CONST, `COMMON_GROUP = "COMMON_GROUP"`})
+ expected.add(codeElem{token.CONST, `sub1Common1 = 11`})
+ expected.add(codeElem{token.CONST, `sub1Common3_LONG = 13`})
+ expected.add(codeElem{token.CONST, `sub2Common2 = 22`})
+ expected.add(codeElem{token.CONST, `sub2Common3 = 23`})
+ expected.add(codeElem{token.CONST, `sub3Common1 = 31`})
+ expected.add(codeElem{token.CONST, `sub3Common4 = 34`})
+ expected.add(codeElem{token.TYPE, `commonInt int`})
+ expected.add(codeElem{token.FUNC, `func commonF() string {
+ return commonDep.Use("common")
+}`})
+
+ if len(got.set) != len(expected.set) {
+ t.Errorf("Got %d codeElems, expected %d", len(got.set), len(expected.set))
+ }
+ for expElem := range expected.set {
+ if !got.has(expElem) {
+ t.Errorf("Didn't get expected codeElem %#v", expElem)
+ }
+ }
+ for gotElem := range got.set {
+ if !expected.has(gotElem) {
+ t.Errorf("Got unexpected codeElem %#v", gotElem)
+ }
+ }
+ })
+
+ t.Run("filter(keepCommon)", func(t *testing.T) {
+ commonSet, err := getCommonSet(inFiles)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ got, err := filter(inFiles[0].src, commonSet.keepCommon)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := []byte(mergedFile)
+
+ if !bytes.Equal(got, expected) {
+ t.Errorf("Got:\n%s\nExpected:\n%s", addLineNr(got), addLineNr(expected))
+ diffLines(t, got, expected)
+ }
+ })
+
+ t.Run("filter(keepArchSpecific)", func(t *testing.T) {
+ commonSet, err := getCommonSet(inFiles)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for i := range inFiles {
+ got, err := filter(inFiles[i].src, commonSet.keepArchSpecific)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := outFiles[i].src
+
+ if !bytes.Equal(got, expected) {
+ t.Errorf("Got:\n%s\nExpected:\n%s", addLineNr(got), addLineNr(expected))
+ diffLines(t, got, expected)
+ }
+ }
+ })
+}
+
+func TestMergedName(t *testing.T) {
+ t.Run("getValidGOOS", func(t *testing.T) {
+ testcases := []struct {
+ filename, goos string
+ ok bool
+ }{
+ {"zerrors_aix.go", "aix", true},
+ {"zerrors_darwin.go", "darwin", true},
+ {"zerrors_dragonfly.go", "dragonfly", true},
+ {"zerrors_freebsd.go", "freebsd", true},
+ {"zerrors_linux.go", "linux", true},
+ {"zerrors_netbsd.go", "netbsd", true},
+ {"zerrors_openbsd.go", "openbsd", true},
+ {"zerrors_solaris.go", "solaris", true},
+ {"zerrors_multics.go", "", false},
+ }
+ for _, tc := range testcases {
+ goos, ok := getValidGOOS(tc.filename)
+ if goos != tc.goos {
+ t.Errorf("got GOOS %q, expected %q", goos, tc.goos)
+ }
+ if ok != tc.ok {
+ t.Errorf("got ok %v, expected %v", ok, tc.ok)
+ }
+ }
+ })
+}
+
+// Helper functions to diff test sources
+
+func diffLines(t *testing.T, got, expected []byte) {
+ t.Helper()
+
+ gotLines := bytes.Split(got, []byte{'\n'})
+ expLines := bytes.Split(expected, []byte{'\n'})
+
+ i := 0
+ for i < len(gotLines) && i < len(expLines) {
+ if !bytes.Equal(gotLines[i], expLines[i]) {
+ t.Errorf("Line %d: Got:\n%q\nExpected:\n%q", i+1, gotLines[i], expLines[i])
+ return
+ }
+ i++
+ }
+
+ if i < len(gotLines) && i >= len(expLines) {
+ t.Errorf("Line %d: got %q, expected EOF", i+1, gotLines[i])
+ }
+ if i >= len(gotLines) && i < len(expLines) {
+ t.Errorf("Line %d: got EOF, expected %q", i+1, gotLines[i])
+ }
+}
+
+func addLineNr(src []byte) []byte {
+ lines := bytes.Split(src, []byte("\n"))
+ for i, line := range lines {
+ lines[i] = []byte(fmt.Sprintf("%d: %s", i+1, line))
+ }
+ return bytes.Join(lines, []byte("\n"))
+}