diff options
author | Tamir Duberstein <tamird@google.com> | 2018-12-28 07:24:56 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-12-28 07:26:18 -0800 |
commit | a3217b71723a93abb7a2aca535408ab84d81ac2f (patch) | |
tree | 7016e516eb34bfe616449e86957a98b43bbc4610 /tools/go_generics/go_merge | |
parent | 46e6577014c849d7306c63905db25f3c695fa7e7 (diff) |
Extract go_merge into its own package
This change is needed to support building gvisor for Fuchsia, which uses
Chromium's GN build system; at the time of writing, Fuchsia's Go support
does not include explicit enumeration of files, assuming instead that Go
binaries are always built from all Go source files in a given package.
Rather than extending Fuchsia's Go support, it is easier simply to
extract a separate package here.
PiperOrigin-RevId: 227133402
Change-Id: I1c64fff286d9c014b4bd1183b76023b35b60c720
Diffstat (limited to 'tools/go_generics/go_merge')
-rw-r--r-- | tools/go_generics/go_merge/BUILD | 9 | ||||
-rw-r--r-- | tools/go_generics/go_merge/main.go | 139 |
2 files changed, 148 insertions, 0 deletions
diff --git a/tools/go_generics/go_merge/BUILD b/tools/go_generics/go_merge/BUILD new file mode 100644 index 000000000..a60437962 --- /dev/null +++ b/tools/go_generics/go_merge/BUILD @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +package(licenses = ["notice"]) # Apache 2.0 + +go_binary( + name = "go_merge", + srcs = ["main.go"], + visibility = ["//visibility:public"], +) diff --git a/tools/go_generics/go_merge/main.go b/tools/go_generics/go_merge/main.go new file mode 100644 index 000000000..2f83facf8 --- /dev/null +++ b/tools/go_generics/go_merge/main.go @@ -0,0 +1,139 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "os" + "path/filepath" + "strconv" +) + +var ( + output = flag.String("o", "", "output `file`") +) + +func fatalf(s string, args ...interface{}) { + fmt.Fprintf(os.Stderr, s, args...) + os.Exit(1) +} + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [options] <input1> [<input2> ...]\n", os.Args[0]) + flag.PrintDefaults() + } + + flag.Parse() + if *output == "" || len(flag.Args()) == 0 { + flag.Usage() + os.Exit(1) + } + + // Load all files. + files := make(map[string]*ast.File) + fset := token.NewFileSet() + var name string + for _, fname := range flag.Args() { + f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments|parser.DeclarationErrors|parser.SpuriousErrors) + if err != nil { + fatalf("%v\n", err) + } + + files[fname] = f + if name == "" { + name = f.Name.Name + } else if name != f.Name.Name { + fatalf("Expected '%s' for package name instead of '%s'.\n", name, f.Name.Name) + } + } + + // Merge all files into one. + pkg := &ast.Package{ + Name: name, + Files: files, + } + f := ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments|ast.FilterFuncDuplicates|ast.FilterImportDuplicates) + + // Create a new declaration slice with all imports at the top, merging any + // redundant imports. + imports := make(map[string]*ast.ImportSpec) + var anonImports []*ast.ImportSpec + for _, d := range f.Decls { + if g, ok := d.(*ast.GenDecl); ok && g.Tok == token.IMPORT { + for _, s := range g.Specs { + i := s.(*ast.ImportSpec) + p, _ := strconv.Unquote(i.Path.Value) + var n string + if i.Name == nil { + n = filepath.Base(p) + } else { + n = i.Name.Name + } + if n == "_" { + anonImports = append(anonImports, i) + } else { + if i2, ok := imports[n]; ok { + if first, second := i.Path.Value, i2.Path.Value; first != second { + fatalf("Conflicting paths for import name '%s': '%s' vs. '%s'\n", n, first, second) + } + } else { + imports[n] = i + } + } + } + } + } + newDecls := make([]ast.Decl, 0, len(f.Decls)) + if l := len(imports) + len(anonImports); l > 0 { + // Non-NoPos Lparen is needed for Go to recognize more than one spec in + // ast.GenDecl.Specs. + d := &ast.GenDecl{ + Tok: token.IMPORT, + Lparen: token.NoPos + 1, + Specs: make([]ast.Spec, 0, l), + } + for _, i := range imports { + d.Specs = append(d.Specs, i) + } + for _, i := range anonImports { + d.Specs = append(d.Specs, i) + } + newDecls = append(newDecls, d) + } + for _, d := range f.Decls { + if g, ok := d.(*ast.GenDecl); !ok || g.Tok != token.IMPORT { + newDecls = append(newDecls, d) + } + } + f.Decls = newDecls + + // Write the output file. + var buf bytes.Buffer + if err := format.Node(&buf, fset, f); err != nil { + fatalf("%v\n", err) + } + + if err := ioutil.WriteFile(*output, buf.Bytes(), 0644); err != nil { + fatalf("%v\n", err) + } +} |