| The App Engine SDK and workspaces (GOPATH) |
| 9 Jan 2013 |
| Tags: appengine, tools, gopath |
| |
| Andrew Gerrand |
| |
| * Introduction |
| |
| When we released Go 1 we introduced the [[https://golang.org/cmd/go/][go tool]] and, with it, the concept of workspaces. Workspaces (specified by the GOPATH environment variable) are a convention for organizing code that simplifies fetching, building, and installing Go packages. If you're not familiar with workspaces, please read [[https://golang.org/doc/code.html][this article]] or watch [[http://www.youtube.com/watch?v=XCsL89YtqCs][this screencast]] before reading on. |
| |
| Until recently, the tools in the App Engine SDK were not aware of workspaces. Without workspaces the "[[https://golang.org/cmd/go/#hdr-Download_and_install_packages_and_dependencies][go get]]" command cannot function, and so app authors had to install and update their app dependencies manually. It was a pain. |
| |
| This has all changed with version 1.7.4 of the App Engine SDK. The [[https://developers.google.com/appengine/docs/go/tools/devserver][dev_appserver]] and [[https://developers.google.com/appengine/docs/go/tools/uploadinganapp][appcfg]] tools are now workspace-aware. When running locally or uploading an app, these tools now search for dependencies in the workspaces specified by the GOPATH environment variable. This means you can now use "go get" while building App Engine apps, and switch between normal Go programs and App Engine apps without changing your environment or habits. |
| |
| For example, let's say you want to build an app that uses OAuth 2.0 to authenticate with a remote service. A popular OAuth 2.0 library for Go is the [[https://godoc.org/golang.org/x/oauth2][oauth2]] package, which you can install to your workspace with this command: |
| |
| go get golang.org/x/oauth2 |
| |
| When writing your App Engine app, import the oauth package just as you would in a regular Go program: |
| |
| import "golang.org/x/oauth2" |
| |
| Now, whether running your app with the dev_appserver or deploying it with appcfg, the tools will find the oauth package in your workspace. It just works. |
| |
| * Hybrid stand-alone/App Engine apps |
| |
| The Go App Engine SDK builds on Go's standard [[https://golang.org/pkg/net/http/][net/http]] package to serve web requests and, as a result, many Go web servers can be run on App Engine with only a few changes. For example, [[https://golang.org/cmd/godoc/][godoc]] is included in the Go distribution as a stand-alone program, but it can also run as an App Engine app (godoc serves [[https://golang.org/][golang.org]] from App Engine). |
| |
| But wouldn't it be nice if you could write a program that is both a stand-alone web server and an App Engine app? By using [[https://golang.org/pkg/go/build/#hdr-Build_Constraints][build constraints]], you can. |
| |
| Build constraints are line comments that determine whether a file should be included in a package. They are most often used in code that handles a variety of operating systems or processor architectures. For instance, the [[https://golang.org/pkg/path/filepath/][path/filepath]] package includes the file [[https://golang.org/src/pkg/path/filepath/symlink.go][symlink.go]], which specifies a build constraint to ensure that it is not built on Windows systems (which do not have symbolic links): |
| |
| // +build !windows |
| |
| The App Engine SDK introduces a new build constraint term: "appengine". Files that specify |
| |
| // +build appengine |
| |
| will be built by the App Engine SDK and ignored by the go tool. Conversely, files that specify |
| |
| // +build !appengine |
| |
| are ignored by the App Engine SDK, while the go tool will happily build them. |
| |
| The [[http://code.google.com/p/goprotobuf/][goprotobuf]] library uses this mechanism to provide two implementations of a key part of its encode/decode machinery: [[http://code.google.com/p/goprotobuf/source/browse/proto/pointer_unsafe.go][pointer_unsafe.go]] is the faster version that cannot be used on App Engine because it uses the [[https://golang.org/pkg/unsafe/][unsafe package]], while [[http://code.google.com/p/goprotobuf/source/browse/proto/pointer_reflect.go][pointer_reflect.go]] is a slower version that avoids unsafe by using the [[https://golang.org/pkg/reflect/][reflect package]] instead. |
| |
| Let's take a simple Go web server and turn it into a hybrid app. This is main.go: |
| |
| package main |
| |
| import ( |
| "fmt" |
| "net/http" |
| ) |
| |
| func main() { |
| http.HandleFunc("/", handler) |
| http.ListenAndServe("localhost:8080", nil) |
| } |
| |
| func handler(w http.ResponseWriter, r *http.Request) { |
| fmt.Fprint(w, "Hello!") |
| } |
| |
| Build this with the go tool and you'll get a stand-alone web server executable. |
| |
| The App Engine infrastructure provides its own main function that runs its equivalent to ListenAndServe. To convert main.go to an App Engine app, drop the call to ListenAndServe and register the handler in an init function (which runs before main). This is app.go: |
| |
| package main |
| |
| import ( |
| "fmt" |
| "net/http" |
| ) |
| |
| func init() { |
| http.HandleFunc("/", handler) |
| } |
| |
| func handler(w http.ResponseWriter, r *http.Request) { |
| fmt.Fprint(w, "Hello!") |
| } |
| |
| To make this a hybrid app, we need to split it into an App Engine-specific part, an stand-alone binary-specific part, and the parts common to both versions. In this case, there is no App Engine-specific part, so we split it into just two files: |
| |
| app.go specifies and registers the handler function. It is identical to the code listing above, and requires no build constraints as it should be included in all versions of the program. |
| |
| main.go runs the web server. It includes the "!appengine" build constraint, as it must only included when building the stand-alone binary. |
| |
| // +build !appengine |
| |
| package main |
| |
| import "net/http" |
| |
| func main() { |
| http.ListenAndServe("localhost:8080", nil) |
| } |
| |
| To see a more complex hybrid app, take a look at the [[https://godoc.org/golang.org/x/tools/present][present tool]]. |
| |
| * Conclusions |
| |
| We hope these changes will make it easier to work on apps with external dependencies, and to maintain code bases that contain both stand-alone programs and App Engine apps. |