// Copyright 2014 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 ( "fmt" "io" "os" "path/filepath" "unicode" "unicode/utf8" "go/ast" "go/build" "go/parser" "go/scanner" "go/token" "golang.org/x/mobile/bind" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/types" ) func genPkg(pkg *build.Package) { if len(pkg.CgoFiles) > 0 { errorf("gobind: cannot use cgo-dependent package as service definition: %s", pkg.CgoFiles[0]) return } files := parseFiles(pkg.Dir, pkg.GoFiles) if len(files) == 0 { return // some error has been reported } conf := loader.Config{ Fset: fset, } conf.TypeChecker.Error = func(err error) { errorf("%v", err) } conf.CreateFromFiles(pkg.ImportPath, files...) program, err := conf.Load() if err != nil { errorf("%v", err) return } p := program.Created[0].Pkg w, closer, err := writer(*lang, p) if err != nil { errorf("%v", err) return } switch *lang { case "java": err = bind.GenJava(w, fset, p) case "go": err = bind.GenGo(w, fset, p) default: errorf("unknown target language: %q", *lang) } if err != nil { if list, _ := err.(bind.ErrorList); len(list) > 0 { for _, err := range list { errorf("%v", err) } } else { errorf("%v", err) } } if err := closer(); err != nil { errorf("error in closing output: %v", err) } } var fset = token.NewFileSet() func parseFiles(dir string, filenames []string) []*ast.File { var files []*ast.File hasErr := false for _, filename := range filenames { path := filepath.Join(dir, filename) file, err := parser.ParseFile(fset, path, nil, parser.AllErrors) if err != nil { hasErr = true if list, _ := err.(scanner.ErrorList); len(list) > 0 { for _, err := range list { errorf("%v", err) } } else { errorf("%v", err) } } files = append(files, file) } if hasErr { return nil } return files } func writer(lang string, pkg *types.Package) (w io.Writer, closer func() error, err error) { if *outdir == "" { return os.Stdout, func() error { return nil }, nil } // TODO(hakim): support output of multiple files e.g. .h/.m files for objc. if err := os.MkdirAll(*outdir, 0755); err != nil { return nil, nil, fmt.Errorf("invalid output dir: %v\n", err) } fname := defaultFileName(lang, pkg) f, err := os.Create(filepath.Join(*outdir, fname)) if err != nil { return nil, nil, fmt.Errorf("invalid output dir: %v\n", err) } return f, f.Close, nil } func defaultFileName(lang string, pkg *types.Package) string { switch lang { case "java": firstRune, size := utf8.DecodeRuneInString(pkg.Name()) className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] return className + ".java" case "go": return "go_" + pkg.Name() + ".go" } errorf("unknown target language: %q", lang) os.Exit(exitStatus) return "" }