| # The App Engine SDK and workspaces (GOPATH) | |
| 9 Jan 2013 | |
| Tags: appengine, tools, gopath | |
| Summary: App Engine SDK 1.7.4 adds support for GOPATH-style workspaces. | |
| OldURL: /the-app-engine-sdk-and-workspaces-gopath | |
| Andrew Gerrand | |
| ## Introduction | |
| When we released Go 1 we introduced the [go tool](https://golang.org/cmd/go/) 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 [this article](https://golang.org/doc/code.html) | |
| or watch [this screencast](http://www.youtube.com/watch?v=XCsL89YtqCs) before reading on. | |
| Until recently, the tools in the App Engine SDK were not aware of workspaces. | |
| Without workspaces the "[go get](https://golang.org/cmd/go/#hdr-Download_and_install_packages_and_dependencies)" | |
| 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 [dev\_appserver](https://developers.google.com/appengine/docs/go/tools/devserver) | |
| and [appcfg](https://developers.google.com/appengine/docs/go/tools/uploadinganapp) | |
| 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 [oauth2](https://godoc.org/golang.org/x/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 [net/http](https://golang.org/pkg/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, [godoc](https://golang.org/cmd/godoc/) is included in the | |
| Go distribution as a stand-alone program, | |
| but it can also run as an App Engine app (godoc serves [golang.org](https://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 [build constraints](https://golang.org/pkg/go/build/#hdr-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 [path/filepath](https://golang.org/pkg/path/filepath/) | |
| package includes the file [symlink.go](https://golang.org/src/pkg/path/filepath/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 [goprotobuf](http://code.google.com/p/goprotobuf/) library uses this | |
| mechanism to provide two implementations of a key part of its encode/decode machinery: | |
| [pointer\_unsafe.go](http://code.google.com/p/goprotobuf/source/browse/proto/pointer_unsafe.go) | |
| is the faster version that cannot be used on App Engine because it uses | |
| the [unsafe package](https://golang.org/pkg/unsafe/), | |
| while [pointer\_reflect.go](http://code.google.com/p/goprotobuf/source/browse/proto/pointer_reflect.go) | |
| is a slower version that avoids unsafe by using the [reflect package](https://golang.org/pkg/reflect/) 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 [present tool](https://godoc.org/golang.org/x/tools/present). | |
| ## 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. |