diff options
Diffstat (limited to 'vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go')
-rw-r--r-- | vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go | 342 |
1 files changed, 0 insertions, 342 deletions
diff --git a/vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go b/vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go deleted file mode 100644 index c66849e83..000000000 --- a/vendor/google.golang.org/appengine/cmd/aebundler/aebundler.go +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2015 Google Inc. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -// Program aebundler turns a Go app into a fully self-contained tar file. -// The app and its subdirectories (if any) are placed under "." -// and the dependencies from $GOPATH are placed under ./_gopath/src. -// A main func is synthesized if one does not exist. -// -// A sample Dockerfile to be used with this bundler could look like this: -// FROM gcr.io/google-appengine/go-compat -// ADD . /app -// RUN GOPATH=/app/_gopath go build -tags appenginevm -o /app/_ah/exe -package main - -import ( - "archive/tar" - "flag" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" -) - -var ( - output = flag.String("o", "", "name of output tar file or '-' for stdout") - rootDir = flag.String("root", ".", "directory name of application root") - vm = flag.Bool("vm", true, `bundle an app for App Engine "flexible environment"`) - - skipFiles = map[string]bool{ - ".git": true, - ".gitconfig": true, - ".hg": true, - ".travis.yml": true, - } -) - -const ( - newMain = `package main -import "google.golang.org/appengine" -func main() { - appengine.Main() -} -` -) - -func usage() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "\t%s -o <file.tar|->\tBundle app to named tar file or stdout\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "\noptional arguments:\n") - flag.PrintDefaults() -} - -func main() { - flag.Usage = usage - flag.Parse() - - var tags []string - if *vm { - tags = append(tags, "appenginevm") - } else { - tags = append(tags, "appengine") - } - - tarFile := *output - if tarFile == "" { - usage() - errorf("Required -o flag not specified.") - } - - app, err := analyze(tags) - if err != nil { - errorf("Error analyzing app: %v", err) - } - if err := app.bundle(tarFile); err != nil { - errorf("Unable to bundle app: %v", err) - } -} - -// errorf prints the error message and exits. -func errorf(format string, a ...interface{}) { - fmt.Fprintf(os.Stderr, "aebundler: "+format+"\n", a...) - os.Exit(1) -} - -type app struct { - hasMain bool - appFiles []string - imports map[string]string -} - -// analyze checks the app for building with the given build tags and returns hasMain, -// app files, and a map of full directory import names to original import names. -func analyze(tags []string) (*app, error) { - ctxt := buildContext(tags) - hasMain, appFiles, err := checkMain(ctxt) - if err != nil { - return nil, err - } - gopath := filepath.SplitList(ctxt.GOPATH) - im, err := imports(ctxt, *rootDir, gopath) - return &app{ - hasMain: hasMain, - appFiles: appFiles, - imports: im, - }, err -} - -// buildContext returns the context for building the source. -func buildContext(tags []string) *build.Context { - return &build.Context{ - GOARCH: build.Default.GOARCH, - GOOS: build.Default.GOOS, - GOROOT: build.Default.GOROOT, - GOPATH: build.Default.GOPATH, - Compiler: build.Default.Compiler, - BuildTags: append(build.Default.BuildTags, tags...), - } -} - -// bundle bundles the app into the named tarFile ("-"==stdout). -func (s *app) bundle(tarFile string) (err error) { - var out io.Writer - if tarFile == "-" { - out = os.Stdout - } else { - f, err := os.Create(tarFile) - if err != nil { - return err - } - defer func() { - if cerr := f.Close(); err == nil { - err = cerr - } - }() - out = f - } - tw := tar.NewWriter(out) - - for srcDir, importName := range s.imports { - dstDir := "_gopath/src/" + importName - if err = copyTree(tw, dstDir, srcDir); err != nil { - return fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err) - } - } - if err := copyTree(tw, ".", *rootDir); err != nil { - return fmt.Errorf("unable to copy root directory to /app: %v", err) - } - if !s.hasMain { - if err := synthesizeMain(tw, s.appFiles); err != nil { - return fmt.Errorf("unable to synthesize new main func: %v", err) - } - } - - if err := tw.Close(); err != nil { - return fmt.Errorf("unable to close tar file %v: %v", tarFile, err) - } - return nil -} - -// synthesizeMain generates a new main func and writes it to the tarball. -func synthesizeMain(tw *tar.Writer, appFiles []string) error { - appMap := make(map[string]bool) - for _, f := range appFiles { - appMap[f] = true - } - var f string - for i := 0; i < 100; i++ { - f = fmt.Sprintf("app_main%d.go", i) - if !appMap[filepath.Join(*rootDir, f)] { - break - } - } - if appMap[filepath.Join(*rootDir, f)] { - return fmt.Errorf("unable to find unique name for %v", f) - } - hdr := &tar.Header{ - Name: f, - Mode: 0644, - Size: int64(len(newMain)), - } - if err := tw.WriteHeader(hdr); err != nil { - return fmt.Errorf("unable to write header for %v: %v", f, err) - } - if _, err := tw.Write([]byte(newMain)); err != nil { - return fmt.Errorf("unable to write %v to tar file: %v", f, err) - } - return nil -} - -// imports returns a map of all import directories (recursively) used by the app. -// The return value maps full directory names to original import names. -func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) { - pkg, err := ctxt.ImportDir(srcDir, 0) - if err != nil { - return nil, fmt.Errorf("unable to analyze source: %v", err) - } - - // Resolve all non-standard-library imports - result := make(map[string]string) - for _, v := range pkg.Imports { - if !strings.Contains(v, ".") { - continue - } - src, err := findInGopath(v, gopath) - if err != nil { - return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err) - } - result[src] = v - im, err := imports(ctxt, src, gopath) - if err != nil { - return nil, fmt.Errorf("unable to parse package %v: %v", src, err) - } - for k, v := range im { - result[k] = v - } - } - return result, nil -} - -// findInGopath searches the gopath for the named import directory. -func findInGopath(dir string, gopath []string) (string, error) { - for _, v := range gopath { - dst := filepath.Join(v, "src", dir) - if _, err := os.Stat(dst); err == nil { - return dst, nil - } - } - return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath) -} - -// copyTree copies srcDir to tar file dstDir, ignoring skipFiles. -func copyTree(tw *tar.Writer, dstDir, srcDir string) error { - entries, err := ioutil.ReadDir(srcDir) - if err != nil { - return fmt.Errorf("unable to read dir %v: %v", srcDir, err) - } - for _, entry := range entries { - n := entry.Name() - if skipFiles[n] { - continue - } - s := filepath.Join(srcDir, n) - d := filepath.Join(dstDir, n) - if entry.IsDir() { - if err := copyTree(tw, d, s); err != nil { - return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err) - } - continue - } - if err := copyFile(tw, d, s); err != nil { - return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err) - } - } - return nil -} - -// copyFile copies src to tar file dst. -func copyFile(tw *tar.Writer, dst, src string) error { - s, err := os.Open(src) - if err != nil { - return fmt.Errorf("unable to open %v: %v", src, err) - } - defer s.Close() - fi, err := s.Stat() - if err != nil { - return fmt.Errorf("unable to stat %v: %v", src, err) - } - - hdr, err := tar.FileInfoHeader(fi, dst) - if err != nil { - return fmt.Errorf("unable to create tar header for %v: %v", dst, err) - } - hdr.Name = dst - if err := tw.WriteHeader(hdr); err != nil { - return fmt.Errorf("unable to write header for %v: %v", dst, err) - } - _, err = io.Copy(tw, s) - if err != nil { - return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err) - } - return nil -} - -// checkMain verifies that there is a single "main" function. -// It also returns a list of all Go source files in the app. -func checkMain(ctxt *build.Context) (bool, []string, error) { - pkg, err := ctxt.ImportDir(*rootDir, 0) - if err != nil { - return false, nil, fmt.Errorf("unable to analyze source: %v", err) - } - if !pkg.IsCommand() { - errorf("Your app's package needs to be changed from %q to \"main\".\n", pkg.Name) - } - // Search for a "func main" - var hasMain bool - var appFiles []string - for _, f := range pkg.GoFiles { - n := filepath.Join(*rootDir, f) - appFiles = append(appFiles, n) - if hasMain, err = readFile(n); err != nil { - return false, nil, fmt.Errorf("error parsing %q: %v", n, err) - } - } - return hasMain, appFiles, nil -} - -// isMain returns whether the given function declaration is a main function. -// Such a function must be called "main", not have a receiver, and have no arguments or return types. -func isMain(f *ast.FuncDecl) bool { - ft := f.Type - return f.Name.Name == "main" && f.Recv == nil && ft.Params.NumFields() == 0 && ft.Results.NumFields() == 0 -} - -// readFile reads and parses the Go source code file and returns whether it has a main function. -func readFile(filename string) (hasMain bool, err error) { - var src []byte - src, err = ioutil.ReadFile(filename) - if err != nil { - return - } - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, filename, src, 0) - for _, decl := range file.Decls { - funcDecl, ok := decl.(*ast.FuncDecl) - if !ok { - continue - } - if !isMain(funcDecl) { - continue - } - hasMain = true - break - } - return -} |