mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-21 18:42:36 +00:00
cmd/cloner,util/codegen: refactor cloner internals to allow reuse
Also run go generate again for Copyright updates. Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
@@ -22,13 +22,11 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
"tailscale.com/util/codegen"
|
||||
)
|
||||
|
||||
var (
|
||||
flagTypes = flag.String("type", "", "comma-separated list of types; required")
|
||||
flagOutput = flag.String("output", "", "output file; required")
|
||||
flagBuildTags = flag.String("tags", "", "compiler build tags to apply")
|
||||
flagCloneFunc = flag.Bool("clonefunc", false, "add a top-level Clone func")
|
||||
)
|
||||
@@ -43,30 +41,18 @@ func main() {
|
||||
}
|
||||
typeNames := strings.Split(*flagTypes, ",")
|
||||
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedName,
|
||||
Tests: false,
|
||||
}
|
||||
if *flagBuildTags != "" {
|
||||
cfg.BuildFlags = []string{"-tags=" + *flagBuildTags}
|
||||
}
|
||||
pkgs, err := packages.Load(cfg, ".")
|
||||
pkg, namedTypes, err := codegen.LoadTypes(*flagBuildTags, ".")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if len(pkgs) != 1 {
|
||||
log.Fatalf("wrong number of packages: %d", len(pkgs))
|
||||
}
|
||||
pkg := pkgs[0]
|
||||
it := codegen.NewImportTracker(pkg.Types)
|
||||
buf := new(bytes.Buffer)
|
||||
imports := make(map[string]struct{})
|
||||
namedTypes := codegen.NamedTypes(pkg)
|
||||
for _, typeName := range typeNames {
|
||||
typ, ok := namedTypes[typeName]
|
||||
if !ok {
|
||||
log.Fatalf("could not find type %s", typeName)
|
||||
}
|
||||
gen(buf, imports, typ, pkg.Types)
|
||||
gen(buf, it, typ)
|
||||
}
|
||||
|
||||
w := func(format string, args ...any) {
|
||||
@@ -93,62 +79,13 @@ func main() {
|
||||
w(" return false")
|
||||
w("}")
|
||||
}
|
||||
|
||||
contents := new(bytes.Buffer)
|
||||
var flagArgs []string
|
||||
if *flagTypes != "" {
|
||||
flagArgs = append(flagArgs, "-type="+*flagTypes)
|
||||
}
|
||||
if *flagOutput != "" {
|
||||
flagArgs = append(flagArgs, "-output="+*flagOutput)
|
||||
}
|
||||
if *flagBuildTags != "" {
|
||||
flagArgs = append(flagArgs, "-tags="+*flagBuildTags)
|
||||
}
|
||||
if *flagCloneFunc {
|
||||
flagArgs = append(flagArgs, "-clonefunc")
|
||||
}
|
||||
fmt.Fprintf(contents, header, strings.Join(flagArgs, " "), pkg.Name)
|
||||
fmt.Fprintf(contents, "import (\n")
|
||||
for s := range imports {
|
||||
fmt.Fprintf(contents, "\t%q\n", s)
|
||||
}
|
||||
fmt.Fprintf(contents, ")\n\n")
|
||||
contents.Write(buf.Bytes())
|
||||
|
||||
output := *flagOutput
|
||||
if output == "" {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
if err := codegen.WriteFormatted(contents.Bytes(), output); err != nil {
|
||||
cloneOutput := pkg.Name + "_clone.go"
|
||||
if err := codegen.WritePackageFile("tailscale.com/cmd/cloner", pkg, cloneOutput, it, buf); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
const header = `// Copyright (c) 2020 Tailscale Inc & 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 tailscale.com/cmd/cloner; DO NOT EDIT.
|
||||
//` + `go:generate` + ` go run tailscale.com/cmd/cloner %s
|
||||
|
||||
package %s
|
||||
|
||||
`
|
||||
|
||||
func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisPkg *types.Package) {
|
||||
pkgQual := func(pkg *types.Package) string {
|
||||
if thisPkg == pkg {
|
||||
return ""
|
||||
}
|
||||
imports[pkg.Path()] = struct{}{}
|
||||
return pkg.Name()
|
||||
}
|
||||
importedName := func(t types.Type) string {
|
||||
return types.TypeString(t, pkgQual)
|
||||
}
|
||||
|
||||
func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named) {
|
||||
t, ok := typ.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return
|
||||
@@ -169,11 +106,11 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
fname := t.Field(i).Name()
|
||||
ft := t.Field(i).Type()
|
||||
if !codegen.ContainsPointers(ft) {
|
||||
if !codegen.ContainsPointers(ft) || codegen.HasNoClone(t.Tag(i)) {
|
||||
continue
|
||||
}
|
||||
if named, _ := ft.(*types.Named); named != nil {
|
||||
if isViewType(ft) {
|
||||
if codegen.IsViewType(ft) {
|
||||
writef("dst.%s = src.%s", fname, fname)
|
||||
continue
|
||||
}
|
||||
@@ -185,7 +122,7 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
|
||||
switch ft := ft.Underlying().(type) {
|
||||
case *types.Slice:
|
||||
if codegen.ContainsPointers(ft.Elem()) {
|
||||
n := importedName(ft.Elem())
|
||||
n := it.QualifiedName(ft.Elem())
|
||||
writef("dst.%s = make([]%s, len(src.%s))", fname, n, fname)
|
||||
writef("for i := range dst.%s {", fname)
|
||||
if _, isPtr := ft.Elem().(*types.Pointer); isPtr {
|
||||
@@ -202,7 +139,7 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
|
||||
writef("dst.%s = src.%s.Clone()", fname, fname)
|
||||
continue
|
||||
}
|
||||
n := importedName(ft.Elem())
|
||||
n := it.QualifiedName(ft.Elem())
|
||||
writef("if dst.%s != nil {", fname)
|
||||
writef("\tdst.%s = new(%s)", fname, n)
|
||||
writef("\t*dst.%s = *src.%s", fname, fname)
|
||||
@@ -212,9 +149,9 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
|
||||
writef("}")
|
||||
case *types.Map:
|
||||
writef("if dst.%s != nil {", fname)
|
||||
writef("\tdst.%s = map[%s]%s{}", fname, importedName(ft.Key()), importedName(ft.Elem()))
|
||||
writef("\tdst.%s = map[%s]%s{}", fname, it.QualifiedName(ft.Key()), it.QualifiedName(ft.Elem()))
|
||||
if sliceType, isSlice := ft.Elem().(*types.Slice); isSlice {
|
||||
n := importedName(sliceType.Elem())
|
||||
n := it.QualifiedName(sliceType.Elem())
|
||||
writef("\tfor k := range src.%s {", fname)
|
||||
// use zero-length slice instead of nil to ensure
|
||||
// the key is always copied.
|
||||
@@ -237,20 +174,10 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
|
||||
writef("return dst")
|
||||
fmt.Fprintf(buf, "}\n\n")
|
||||
|
||||
buf.Write(codegen.AssertStructUnchanged(t, thisPkg, name, "Clone", imports))
|
||||
}
|
||||
|
||||
func isViewType(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if t.NumFields() != 1 {
|
||||
return false
|
||||
}
|
||||
return t.Field(0).Name() == "ж"
|
||||
buf.Write(codegen.AssertStructUnchanged(t, name, "Clone", it))
|
||||
}
|
||||
|
||||
// hasBasicUnderlying reports true when typ.Underlying() is a slice or a map.
|
||||
func hasBasicUnderlying(typ types.Type) bool {
|
||||
switch typ.Underlying().(type) {
|
||||
case *types.Slice, *types.Map:
|
||||
|
Reference in New Issue
Block a user