diff options
Diffstat (limited to 'content/appengine-gopath.article')
-rw-r--r-- | content/appengine-gopath.article | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/content/appengine-gopath.article b/content/appengine-gopath.article new file mode 100644 index 0000000..d9f995b --- /dev/null +++ b/content/appengine-gopath.article @@ -0,0 +1,157 @@ +# 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. |