blob: 0bf3fbf7ec1520cc56fad5c458d0453a70b2f934 [file] [log] [blame]
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.