func handleError(err error, options *gbuild.Options, browserErrors *bytes.Buffer) int {
	switch err := err.(type) {
	case nil:
		return 0
	case compiler.ErrorList:
		for _, entry := range err {
			printError(entry, options, browserErrors)
		}
		return 1
	case *exec.ExitError:
		return err.Sys().(syscall.WaitStatus).ExitStatus()
	default:
		printError(err, options, browserErrors)
		return 1
	}
}
handleError handles err and returns an appropriate exit code.
If browserErrors is non-nil, errors are written for presentation in browser.
package main
import (
	"bytes"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/doc"
	"go/parser"
	"go/scanner"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"text/template"
	"time"
	"unicode"
	"unicode/utf8"
	gbuild "github.com/gopherjs/gopherjs/build"
	"github.com/gopherjs/gopherjs/compiler"
	"github.com/gopherjs/gopherjs/internal/sysutil"
	"github.com/kisielk/gotool"
	"github.com/neelance/sourcemap"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/tools/go/buildutil"
)
var currentDirectory string
func init() {
	var err error
	currentDirectory, err = os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	currentDirectory, err = filepath.EvalSymlinks(currentDirectory)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	gopaths := filepath.SplitList(build.Default.GOPATH)
	if len(gopaths) == 0 {
		fmt.Fprintf(os.Stderr, "$GOPATH not set. For more details see: go help gopath\n")
		os.Exit(1)
	}
}
func main() {
	var (
		options = &gbuild.Options{CreateMapFile: true}
		pkgObj  string
		tags    string
	)
	flagVerbose := pflag.NewFlagSet("", 0)
	flagVerbose.BoolVarP(&options.Verbose, "verbose", "v", false, "print the names of packages as they are compiled")
	flagQuiet := pflag.NewFlagSet("", 0)
	flagQuiet.BoolVarP(&options.Quiet, "quiet", "q", false, "suppress non-fatal warnings")
	compilerFlags := pflag.NewFlagSet("", 0)
	compilerFlags.BoolVarP(&options.Minify, "minify", "m", false, "minify generated code")
	compilerFlags.BoolVar(&options.Color, "color", terminal.IsTerminal(int(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb", "colored output")
	compilerFlags.StringVar(&tags, "tags", "", "a list of build tags to consider satisfied during the build")
	compilerFlags.BoolVar(&options.MapToLocalDisk, "localmap", false, "use local paths for sourcemap")
	flagWatch := pflag.NewFlagSet("", 0)
	flagWatch.BoolVarP(&options.Watch, "watch", "w", false, "watch for changes to the source files")
	cmdBuild := &cobra.Command{
		Use:   "build [packages]",
		Short: "compile packages and dependencies",
	}
	cmdBuild.Flags().StringVarP(&pkgObj, "output", "o", "", "output file")
	cmdBuild.Flags().AddFlagSet(flagVerbose)
	cmdBuild.Flags().AddFlagSet(flagQuiet)
	cmdBuild.Flags().AddFlagSet(compilerFlags)
	cmdBuild.Flags().AddFlagSet(flagWatch)
	cmdBuild.Run = func(cmd *cobra.Command, args []string) {
		options.BuildTags = strings.Fields(tags)
		for {
			s := gbuild.NewSession(options)
			err := func() error {
				if len(args) > 0 && (strings.HasSuffix(args[0], ".go") || strings.HasSuffix(args[0], ".inc.js")) {
					for _, arg := range args {
						if !strings.HasSuffix(arg, ".go") && !strings.HasSuffix(arg, ".inc.js") {
							return fmt.Errorf("named files must be .go or .inc.js files")
						}
					}
					if pkgObj == "" {
						basename := filepath.Base(args[0])
						pkgObj = basename[:len(basename)-3] + ".js"
					}
					names := make([]string, len(args))
					for i, name := range args {
						name = filepath.ToSlash(name)
						names[i] = name
						if s.Watcher != nil {
							s.Watcher.Add(name)
						}
					}
					err := s.BuildFiles(args, pkgObj, currentDirectory)
					return err
				}
				patternContext := gbuild.NewBuildContext("", options.BuildTags)
				pkgs := (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args)
				for _, pkgPath := range pkgs {
					if s.Watcher != nil {
						pkg, err := gbuild.NewBuildContext(s.InstallSuffix(), options.BuildTags).Import(pkgPath, "", build.FindOnly)
						if err != nil {
							return err
						}
						s.Watcher.Add(pkg.Dir)
					}
					pkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags)
					if err != nil {
						return err
					}
					archive, err := s.BuildPackage(pkg)
					if err != nil {
						return err
					}
					if len(pkgs) == 1 {  
						if pkgObj == "" {
							pkgObj = filepath.Base(pkg.Dir) + ".js"
						}
						if pkg.IsCommand() && !pkg.UpToDate {
							if err := s.WriteCommandPackage(archive, pkgObj); err != nil {
								return err
							}
						}
					}
				}
				return nil
			}()
			exitCode := handleError(err, options, nil)
			if s.Watcher == nil {
				os.Exit(exitCode)
			}
			s.WaitForChange()
		}
	}
	cmdInstall := &cobra.Command{
		Use:   "install [packages]",
		Short: "compile and install packages and dependencies",
	}
	cmdInstall.Flags().AddFlagSet(flagVerbose)
	cmdInstall.Flags().AddFlagSet(flagQuiet)
	cmdInstall.Flags().AddFlagSet(compilerFlags)
	cmdInstall.Flags().AddFlagSet(flagWatch)
	cmdInstall.Run = func(cmd *cobra.Command, args []string) {
		options.BuildTags = strings.Fields(tags)
		for {
			s := gbuild.NewSession(options)
			err := func() error {
				patternContext := gbuild.NewBuildContext("", options.BuildTags)
				pkgs := (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args)
				if cmd.Name() == "get" {
					goGet := exec.Command("go", append([]string{"get", "-d", "-tags=js"}, pkgs...)...)
					goGet.Stdout = os.Stdout
					goGet.Stderr = os.Stderr
					if err := goGet.Run(); err != nil {
						return err
					}
				}
				for _, pkgPath := range pkgs {
					pkg, err := gbuild.Import(pkgPath, 0, s.InstallSuffix(), options.BuildTags)
					if s.Watcher != nil && pkg != nil {  
						s.Watcher.Add(pkg.Dir)
					}
					if err != nil {
						return err
					}
					archive, err := s.BuildPackage(pkg)
					if err != nil {
						return err
					}
					if pkg.IsCommand() && !pkg.UpToDate {
						if err := s.WriteCommandPackage(archive, pkg.PkgObj); err != nil {
							return err
						}
					}
				}
				return nil
			}()
			exitCode := handleError(err, options, nil)
			if s.Watcher == nil {
				os.Exit(exitCode)
			}
			s.WaitForChange()
		}
	}
	cmdDoc := &cobra.Command{
		Use:   "doc [arguments]",
		Short: "display documentation for the requested, package, method or symbol",
	}
	cmdDoc.Run = func(cmd *cobra.Command, args []string) {
		goDoc := exec.Command("go", append([]string{"doc"}, args...)...)
		goDoc.Stdout = os.Stdout
		goDoc.Stderr = os.Stderr
		goDoc.Env = append(os.Environ(), "GOARCH=js")
		err := goDoc.Run()
		exitCode := handleError(err, options, nil)
		os.Exit(exitCode)
	}
	cmdGet := &cobra.Command{
		Use:   "get [packages]",
		Short: "download and install packages and dependencies",
	}
	cmdGet.Flags().AddFlagSet(flagVerbose)
	cmdGet.Flags().AddFlagSet(flagQuiet)
	cmdGet.Flags().AddFlagSet(compilerFlags)
	cmdGet.Run = cmdInstall.Run
	cmdRun := &cobra.Command{
		Use:   "run [gofiles...] [arguments...]",
		Short: "compile and run Go program",
	}
	cmdRun.Flags().AddFlagSet(flagVerbose)
	cmdRun.Flags().AddFlagSet(flagQuiet)
	cmdRun.Flags().AddFlagSet(compilerFlags)
	cmdRun.Run = func(cmd *cobra.Command, args []string) {
		err := func() error {
			lastSourceArg := 0
			for {
				if lastSourceArg == len(args) || !(strings.HasSuffix(args[lastSourceArg], ".go") || strings.HasSuffix(args[lastSourceArg], ".inc.js")) {
					break
				}
				lastSourceArg++
			}
			if lastSourceArg == 0 {
				return fmt.Errorf("gopherjs run: no go files listed")
			}
			tempfile, err := ioutil.TempFile(currentDirectory, filepath.Base(args[0])+".")
			if err != nil && strings.HasPrefix(currentDirectory, runtime.GOROOT()) {
				tempfile, err = ioutil.TempFile("", filepath.Base(args[0])+".")
			}
			if err != nil {
				return err
			}
			defer func() {
				tempfile.Close()
				os.Remove(tempfile.Name())
				os.Remove(tempfile.Name() + ".map")
			}()
			s := gbuild.NewSession(options)
			if err := s.BuildFiles(args[:lastSourceArg], tempfile.Name(), currentDirectory); err != nil {
				return err
			}
			if err := runNode(tempfile.Name(), args[lastSourceArg:], "", options.Quiet); err != nil {
				return err
			}
			return nil
		}()
		exitCode := handleError(err, options, nil)
		os.Exit(exitCode)
	}
	cmdTest := &cobra.Command{
		Use:   "test [packages]",
		Short: "test packages",
	}
	bench := cmdTest.Flags().String("bench", "", "Run benchmarks matching the regular expression. By default, no benchmarks run. To run all benchmarks, use '--bench=.'.")
	benchtime := cmdTest.Flags().String("benchtime", "", "Run enough iterations of each benchmark to take t, specified as a time.Duration (for example, -benchtime 1h30s). The default is 1 second (1s).")
	count := cmdTest.Flags().String("count", "", "Run each test and benchmark n times (default 1). Examples are always run once.")
	run := cmdTest.Flags().String("run", "", "Run only those tests and examples matching the regular expression.")
	short := cmdTest.Flags().Bool("short", false, "Tell long-running tests to shorten their run time.")
	verbose := cmdTest.Flags().BoolP("verbose", "v", false, "Log all tests as they are run. Also print all text from Log and Logf calls even if the test succeeds.")
	compileOnly := cmdTest.Flags().BoolP("compileonly", "c", false, "Compile the test binary to pkg.test.js but do not run it (where pkg is the last element of the package's import path). The file name can be changed with the -o flag.")
	outputFilename := cmdTest.Flags().StringP("output", "o", "", "Compile the test binary to the named file. The test still runs (unless -c is specified).")
	cmdTest.Flags().AddFlagSet(compilerFlags)
	cmdTest.Run = func(cmd *cobra.Command, args []string) {
		options.BuildTags = strings.Fields(tags)
		err := func() error {
			patternContext := gbuild.NewBuildContext("", options.BuildTags)
			args = (&gotool.Context{BuildContext: *patternContext}).ImportPaths(args)
			if *compileOnly && len(args) > 1 {
				return errors.New("cannot use -c flag with multiple packages")
			}
			if *outputFilename != "" && len(args) > 1 {
				return errors.New("cannot use -o flag with multiple packages")
			}
			pkgs := make([]*gbuild.PackageData, len(args))
			for i, pkgPath := range args {
				var err error
				pkgs[i], err = gbuild.Import(pkgPath, 0, "", options.BuildTags)
				if err != nil {
					return err
				}
			}
			var exitErr error
			for _, pkg := range pkgs {
				if len(pkg.TestGoFiles) == 0 && len(pkg.XTestGoFiles) == 0 {
					fmt.Printf("?   \t%s\t[no test files]\n", pkg.ImportPath)
					continue
				}
				s := gbuild.NewSession(options)
				tests := &testFuncs{BuildContext: s.BuildContext(), Package: pkg.Package}
				collectTests := func(testPkg *gbuild.PackageData, testPkgName string, needVar *bool) error {
					if testPkgName == "_test" {
						for _, file := range pkg.TestGoFiles {
							if err := tests.load(pkg.Package.Dir, file, testPkgName, &tests.ImportTest, &tests.NeedTest); err != nil {
								return err
							}
						}
					} else {
						for _, file := range pkg.XTestGoFiles {
							if err := tests.load(pkg.Package.Dir, file, "_xtest", &tests.ImportXtest, &tests.NeedXtest); err != nil {
								return err
							}
						}
					}
					_, err := s.BuildPackage(testPkg)
					return err
				}
				if err := collectTests(&gbuild.PackageData{
					Package: &build.Package{
						ImportPath: pkg.ImportPath,
						Dir:        pkg.Dir,
						GoFiles:    append(pkg.GoFiles, pkg.TestGoFiles...),
						Imports:    append(pkg.Imports, pkg.TestImports...),
					},
					IsTest:  true,
					JSFiles: pkg.JSFiles,
				}, "_test", &tests.NeedTest); err != nil {
					return err
				}
				if err := collectTests(&gbuild.PackageData{
					Package: &build.Package{
						ImportPath: pkg.ImportPath + "_test",
						Dir:        pkg.Dir,
						GoFiles:    pkg.XTestGoFiles,
						Imports:    pkg.XTestImports,
					},
					IsTest: true,
				}, "_xtest", &tests.NeedXtest); err != nil {
					return err
				}
				buf := new(bytes.Buffer)
				if err := testmainTmpl.Execute(buf, tests); err != nil {
					return err
				}
				fset := token.NewFileSet()
				mainFile, err := parser.ParseFile(fset, "_testmain.go", buf, 0)
				if err != nil {
					return err
				}
				importContext := &compiler.ImportContext{
					Packages: s.Types,
					Import: func(path string) (*compiler.Archive, error) {
						if path == pkg.ImportPath || path == pkg.ImportPath+"_test" {
							return s.Archives[path], nil
						}
						return s.BuildImportPath(path)
					},
				}
				mainPkgArchive, err := compiler.Compile("main", []*ast.File{mainFile}, fset, importContext, options.Minify)
				if err != nil {
					return err
				}
				if *compileOnly && *outputFilename == "" {
					*outputFilename = pkg.Package.Name + "_test.js"
				}
				var outfile *os.File
				if *outputFilename != "" {
					outfile, err = os.Create(*outputFilename)
					if err != nil {
						return err
					}
				} else {
					outfile, err = ioutil.TempFile(currentDirectory, "test.")
					if err != nil {
						return err
					}
				}
				defer func() {
					outfile.Close()
					if *outputFilename == "" {
						os.Remove(outfile.Name())
						os.Remove(outfile.Name() + ".map")
					}
				}()
				if err := s.WriteCommandPackage(mainPkgArchive, outfile.Name()); err != nil {
					return err
				}
				if *compileOnly {
					continue
				}
				var args []string
				if *bench != "" {
					args = append(args, "-test.bench", *bench)
				}
				if *benchtime != "" {
					args = append(args, "-test.benchtime", *benchtime)
				}
				if *count != "" {
					args = append(args, "-test.count", *count)
				}
				if *run != "" {
					args = append(args, "-test.run", *run)
				}
				if *short {
					args = append(args, "-test.short")
				}
				if *verbose {
					args = append(args, "-test.v")
				}
				status := "ok  "
				start := time.Now()
				if err := runNode(outfile.Name(), args, runTestDir(pkg), options.Quiet); err != nil {
					if _, ok := err.(*exec.ExitError); !ok {
						return err
					}
					exitErr = err
					status = "FAIL"
				}
				fmt.Printf("%s\t%s\t%.3fs\n", status, pkg.ImportPath, time.Since(start).Seconds())
			}
			return exitErr
		}()
		exitCode := handleError(err, options, nil)
		os.Exit(exitCode)
	}
	cmdServe := &cobra.Command{
		Use:   "serve [root]",
		Short: "compile on-the-fly and serve",
	}
	cmdServe.Flags().AddFlagSet(flagVerbose)
	cmdServe.Flags().AddFlagSet(flagQuiet)
	cmdServe.Flags().AddFlagSet(compilerFlags)
	var addr string
	cmdServe.Flags().StringVarP(&addr, "http", "", ":8080", "HTTP bind address to serve")
	cmdServe.Run = func(cmd *cobra.Command, args []string) {
		options.BuildTags = strings.Fields(tags)
		dirs := append(filepath.SplitList(build.Default.GOPATH), build.Default.GOROOT)
		var root string
		if len(args) > 1 {
			cmdServe.HelpFunc()(cmd, args)
			os.Exit(1)
		}
		if len(args) == 1 {
			root = args[0]
		}
		sourceFiles := http.FileServer(serveCommandFileSystem{
			serveRoot:  root,
			options:    options,
			dirs:       dirs,
			sourceMaps: make(map[string][]byte),
		})
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if tcpAddr := ln.Addr().(*net.TCPAddr); tcpAddr.IP.Equal(net.IPv4zero) || tcpAddr.IP.Equal(net.IPv6zero) {  
			fmt.Printf("serving at http://localhost:%d and on port %d of any available addresses\n", tcpAddr.Port, tcpAddr.Port)
		} else {  
			fmt.Printf("serving at http://%s\n", tcpAddr)
		}
		fmt.Fprintln(os.Stderr, http.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}, sourceFiles))
	}
	cmdVersion := &cobra.Command{
		Use:   "version",
		Short: "print GopherJS compiler version",
	}
	cmdVersion.Run = func(cmd *cobra.Command, args []string) {
		if len(args) > 0 {
			cmdServe.HelpFunc()(cmd, args)
			os.Exit(1)
		}
		fmt.Printf("GopherJS %s\n", compiler.Version)
	}
	rootCmd := &cobra.Command{
		Use:  "gopherjs",
		Long: "GopherJS is a tool for compiling Go source code to JavaScript.",
	}
	rootCmd.AddCommand(cmdBuild, cmdGet, cmdInstall, cmdRun, cmdTest, cmdServe, cmdVersion, cmdDoc)
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(2)
	}
}
type tcpKeepAliveListener struct {
	*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
type serveCommandFileSystem struct {
	serveRoot  string
	options    *gbuild.Options
	dirs       []string
	sourceMaps map[string][]byte
}
func (fs serveCommandFileSystem) Open(requestName string) (http.File, error) {
	name := path.Join(fs.serveRoot, requestName[1:])  
	dir, file := path.Split(name)
	base := path.Base(dir)  
	isPkg := file == base+".js"
	isMap := file == base+".js.map"
	isIndex := file == "index.html"
	if isPkg || isMap || isIndex {
		s := gbuild.NewSession(fs.options)
		pkg, err := gbuild.Import(path.Dir(name), 0, s.InstallSuffix(), fs.options.BuildTags)
		if err != nil || pkg.Name != "main" {
			isPkg = false
			isMap = false
			isIndex = false
		}
		switch {
		case isPkg:
			buf := new(bytes.Buffer)
			browserErrors := new(bytes.Buffer)
			err := func() error {
				archive, err := s.BuildPackage(pkg)
				if err != nil {
					return err
				}
				sourceMapFilter := &compiler.SourceMapFilter{Writer: buf}
				m := &sourcemap.Map{File: base + ".js"}
				sourceMapFilter.MappingCallback = gbuild.NewMappingCallback(m, fs.options.GOROOT, fs.options.GOPATH, fs.options.MapToLocalDisk)
				deps, err := compiler.ImportDependencies(archive, s.BuildImportPath)
				if err != nil {
					return err
				}
				if err := compiler.WriteProgramCode(deps, sourceMapFilter); err != nil {
					return err
				}
				mapBuf := new(bytes.Buffer)
				m.WriteTo(mapBuf)
				buf.WriteString("//# sourceMappingURL=" + base + ".js.map\n")
				fs.sourceMaps[name+".map"] = mapBuf.Bytes()
				return nil
			}()
			handleError(err, fs.options, browserErrors)
			if err != nil {
				buf = browserErrors
			}
			return newFakeFile(base+".js", buf.Bytes()), nil
		case isMap:
			if content, ok := fs.sourceMaps[name]; ok {
				return newFakeFile(base+".js.map", content), nil
			}
		}
	}
	for _, d := range fs.dirs {
		dir := http.Dir(filepath.Join(d, "src"))
		f, err := dir.Open(name)
		if err == nil {
			return f, nil
		}
		f, err = dir.Open(requestName)
		if err == nil {
			return f, nil
		}
	}
	if isIndex {
		return newFakeFile("index.html", []byte(`<html><head><meta charset="utf-8"><script src="`+base+`.js"></script></head><body></body></html>`)), nil
	}
	return nil, os.ErrNotExist
}
type fakeFile struct {
	name string
	size int
	io.ReadSeeker
}
func newFakeFile(name string, content []byte) *fakeFile {
	return &fakeFile{name: name, size: len(content), ReadSeeker: bytes.NewReader(content)}
}
func (f *fakeFile) Close() error {
	return nil
}
func (f *fakeFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}
func (f *fakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}
func (f *fakeFile) Name() string {
	return f.name
}
func (f *fakeFile) Size() int64 {
	return int64(f.size)
}
func (f *fakeFile) Mode() os.FileMode {
	return 0
}
func (f *fakeFile) ModTime() time.Time {
	return time.Time{}
}
func (f *fakeFile) IsDir() bool {
	return false
}
func (f *fakeFile) Sys() interface{} {
	return nil
}