github.com/gophergala2016/cmd-go-js@v0.0.0-20160421080227-24a7748a7d62/README.md (about)

     1  # cmd-go-js
     2  
     3  cmd-go-js is a "feature branch" of cmd/go command with experimental changes. The goal was to explore adding support for additional Go compilers and environments by making use of existing -compiler and -exec flags:
     4  
     5  > -compiler name - name of compiler to use, as in runtime.Compiler (gccgo or gc).
     6  >
     7  > -exec xprog - Run the test binary using xprog. The behavior is the same as in 'go run'. See 'go help run' for details.
     8  
     9  Specifically, I wanted to try adding support for [GopherJS compiler](https://github.com/gopherjs/gopherjs) which targets `GOARCH=js` architecture. It compiles Go code to JavaScript which can be executed by a JavaScript engine (such as V8 JavaScript Engine, Node.js, or any browser with JavaScript support).
    10  
    11  # Results
    12  
    13  ![](Screenshot.png)
    14  
    15  #### go build
    16  
    17  You can now use `go` to build for `GOARCH=js` architecture! That is the only architecture that all browsers can execute natively, without any plugins.
    18  
    19  ```bash
    20  # Normal build for GOARCH=amd64.
    21  $ go build ./samples/hello-world
    22  # Note that above implicitly means:
    23  # GOARCH=amd64 go build -compiler=gc ./samples/hello-world
    24  $ ./hello-world
    25  Hello brave new world! It is working on go1.5.3 darwin/amd64!
    26  
    27  # Newly supported build for GOARCH=js.
    28  $ GOARCH=js go build -compiler=gopherjs ./samples/hello-world
    29  $ node ./hello-world
    30  Hello brave new world! It is working on go1.5.3 darwin/js! That means you can execute it in browsers.
    31  ```
    32  
    33  #### go run
    34  
    35  `go run` also works. You can use the `-exec` flag to have `node` execute the compiled JavaScript output.
    36  
    37  ```bash
    38  $ go run ./samples/hello-world/main.go
    39  Hello brave new world! It is working on go1.5.3 darwin/amd64!
    40  
    41  $ GOARCH=js go run -compiler=gopherjs -exec=node ./samples/hello-world/main.go
    42  Hello brave new world! It is working on go1.5.3 darwin/js! That means you can execute it in browsers.
    43  ```
    44  
    45  From https://golang.org/cmd/go/#hdr-Compile_and_run_Go_program:
    46  
    47  > If the -exec flag is not given, GOOS or GOARCH is different from the system default, and a program named go_$GOOS_$GOARCH_exec can be found on the current search path, 'go run' invokes the binary using that program, for example 'go_nacl_386_exec a.out arguments...'. This allows execution of cross-compiled programs when a simulator or other execution method is available.
    48  
    49  That means if you create a symlink to `node` binary named `go_darwin_js_exec`, then you can just:
    50  
    51  ```bash
    52  $ GOARCH=js go run -compiler=gopherjs ./samples/hello-world/main.go
    53  Hello brave new world! It is working on go1.5.3 darwin/js! That means you can execute it in browsers.
    54  ```
    55  
    56  # Implementation details, lessons learned, other notes
    57  
    58  ### go test
    59  
    60  `go test` can be made to support `-compiler=gopherjs` with `-exec=node`, but additional work needs to be done; it currently doesn't work.
    61  
    62  ```bash
    63  $ GOARCH=js go test -v -compiler=gopherjs -exec=node ./samples/hello-world
    64  === RUN   TestBasic
    65  --- PASS: TestBasic (0.00s)
    66  PASS
    67  ok  	github.com/gophergala2016/cmd-go-js/samples/hello-world	0.015s
    68  ```
    69  
    70  That means all of go support for [testing](https://godoc.org/testing) would be availble. Including tests, executable examples, and benchmarks.
    71  
    72  ### Toolchains
    73  
    74  `cmd/go` defines a very clean `toolchain` interface internally:
    75  
    76  ```Go
    77  type toolchain interface {
    78  	// gc runs the compiler in a specific directory on a set of files
    79  	// and returns the name of the generated output file.
    80  	// The compiler runs in the directory dir.
    81  	gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, out []byte, err error)
    82  	// cc runs the toolchain's C compiler in a directory on a C file
    83  	// to produce an output file.
    84  	cc(b *builder, p *Package, objdir, ofile, cfile string) error
    85  	// asm runs the assembler in a specific directory on a specific file
    86  	// to generate the named output file.
    87  	asm(b *builder, p *Package, obj, ofile, sfile string) error
    88  	// pkgpath builds an appropriate path for a temporary package file.
    89  	pkgpath(basedir string, p *Package) string
    90  	// pack runs the archive packer in a specific directory to create
    91  	// an archive from a set of object files.
    92  	// typically it is run in the object directory.
    93  	pack(b *builder, p *Package, objDir, afile string, ofiles []string) error
    94  	// ld runs the linker to create an executable starting at mainpkg.
    95  	ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error
    96  	// ldShared runs the linker to create a shared library containing the pkgs built by toplevelactions
    97  	ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error
    98  
    99  	compiler() string
   100  	linker() string
   101  }
   102  ```
   103  
   104  It currently has two main implementations `gcToolchain`, and `gccgoToolchain` (there's also a 3rd, `noToolchain`, that does nothing). Here you can see how Go currently supports only two values for -compiler flag.
   105  
   106  ```Go
   107  switch value {
   108  case "gc":
   109  	buildToolchain = gcToolchain{}
   110  case "gccgo":
   111  	buildToolchain = gccgoToolchain{}
   112  default:
   113  	return fmt.Errorf("unknown compiler %q", value)
   114  }
   115  ```
   116  
   117  It's possible to either add GopherJS specifically:
   118  
   119  ```Go
   120  switch value {
   121  case "gc":
   122  	buildToolchain = gcToolchain{}
   123  case "gccgo":
   124  	buildToolchain = gccgoToolchain{}
   125  case "gopherjs":
   126  	buildToolchain = gopherjsToolchain{}
   127  default:
   128  	return fmt.Errorf("unknown compiler %q", value)
   129  }
   130  ```
   131  
   132  Or create a general toolchain that can accept any arbitrary -compiler flag value:
   133  
   134  ```Go
   135  switch value {
   136  case "gc":
   137  	buildToolchain = gcToolchain{}
   138  case "gccgo":
   139  	buildToolchain = gccgoToolchain{}
   140  default:
   141  	switch compilerBin, err := exec.LookPath(value); err {
   142  	case nil:
   143  		buildToolchain = generalToolchain{compilerBin: compilerBin}
   144  	default:
   145  		return fmt.Errorf("unknown compiler %q", value)
   146  	}
   147  }
   148  ```
   149  
   150  ### Go 1.6 vs 1.5
   151  
   152  The code for `cmd/go` has a large volume of changes from 1.5 to tip.
   153  
   154  I've chosen to work with Go 1.5 for now, since GopherJS compiler only supports Go 1.5 at this time. There is an issue at [gopherjs/gopherjs#355](https://github.com/gopherjs/gopherjs/issues/355) tracking progress for adding Go 1.6 support, and @neelance says it's close.
   155  
   156  I initially tried Go 1.6 (see [`go1.6` branch](https://github.com/gophergala2016/cmd-go-js/commits/go1.6)), but switched to working with Go 1.5 at this time for the above reasons.
   157  
   158  ### Code changes
   159  
   160  The first five commits of [`master` branch](https://github.com/gophergala2016/cmd-go-js/commits/master) simply check in the original `cmd/go` binary source at commit [`go1.5.3`](https://github.com/golang/go/tree/go1.5.3), and make minimal changes to allow it to build on OS X and be go-gettable. It also vendors `go/build` package from standard library, in order to be able to make changes to it.
   161  
   162  The rest of the changes are the relevant changes to add `-compiler=gopherjs` support to be able to build for `GOARCH=js` architecture. These changes are relatively minimal (a couple hundred lines) and not disruptive. The diff can be seen here:
   163  
   164  https://github.com/gophergala2016/cmd-go-js/compare/c900f90...master
   165  
   166  ### Installation
   167  
   168  **Note:** Due to time limits, this project works on OS X only. Adding support for other other systems is easy, but it hasn't been done yet. See commit message of [`2dae5232`](https://github.com/gophergala2016/cmd-go-js/commit/2dae52322dcef1b91b9b363fa2301da735188370), that is the only blocker.
   169  
   170  Go 1.5 is required.
   171  
   172  ```bash
   173  go get -u github.com/gopherjs/gopherjs
   174  GO15VENDOREXPERIMENT=1 go get -u github.com/gophergala2016/cmd-go-js/cmd/go
   175  ```
   176  
   177  Since the binary is also called `go`, make sure you run the right one.
   178  
   179  ```bash
   180  $ $GOPATH/bin/go version
   181  go version go1.5.3 darwin/amd64 (with experimental changes)
   182  ```
   183  
   184  You may need Node if you want to execute generated JavaScript from the command line rather than inside a browser.
   185  
   186  ```bash
   187  brew install node
   188  ```