blob: b0605d1a8353a59054cbdf6cc99cfa079dadc012 [file] [log] [blame]
Andrew Gerranddb9a09f2013-03-08 11:17:09 +11001The App Engine SDK and workspaces (GOPATH)
29 Jan 2013
Andrew Gerrandb316fcd2013-06-05 09:59:16 +10003Tags: appengine, tools, gopath
Andrew Gerranddb9a09f2013-03-08 11:17:09 +11004
5Andrew Gerrand
6
7* Introduction
8
9When we released Go 1 we introduced the [[http://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 [[http://golang.org/doc/code.html][this article]] or watch [[http://www.youtube.com/watch?v=XCsL89YtqCs][this screencast]] before reading on.
10
11Until recently, the tools in the App Engine SDK were not aware of workspaces. Without workspaces the "[[http://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.
12
13This 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.
14
Chris Broadfoot669e5ee2016-02-01 16:04:41 -080015For 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:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110016
Chris Broadfoot669e5ee2016-02-01 16:04:41 -080017 go get golang.org/x/oauth2
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110018
19When writing your App Engine app, import the oauth package just as you would in a regular Go program:
20
Chris Broadfoot669e5ee2016-02-01 16:04:41 -080021 import "golang.org/x/oauth2"
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110022
23Now, 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.
24
25* Hybrid stand-alone/App Engine apps
26
27The Go App Engine SDK builds on Go's standard [[http://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, [[http://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 [[http://golang.org/][golang.org]] from App Engine).
28
29But 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 [[http://golang.org/pkg/go/build/#hdr-Build_Constraints][build constraints]], you can.
30
31Build 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 [[http://golang.org/pkg/path/filepath/][path/filepath]] package includes the file [[http://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):
32
33 // +build !windows
34
35The App Engine SDK introduces a new build constraint term: "appengine". Files that specify
36
37 // +build appengine
38
39will be built by the App Engine SDK and ignored by the go tool. Conversely, files that specify
40
41 // +build !appengine
42
43are ignored by the App Engine SDK, while the go tool will happily build them.
44
45The [[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 [[http://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 [[http://golang.org/pkg/reflect/][reflect package]] instead.
46
47Let's take a simple Go web server and turn it into a hybrid app. This is main.go:
48
49 package main
50
51 import (
52 "fmt"
53 "net/http"
54 )
55
56 func main() {
57 http.HandleFunc("/", handler)
58 http.ListenAndServe("localhost:8080", nil)
59 }
60
61 func handler(w http.ResponseWriter, r *http.Request) {
62 fmt.Fprint(w, "Hello!")
63 }
64
65Build this with the go tool and you'll get a stand-alone web server executable.
66
67The 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:
68
69 package main
70
71 import (
72 "fmt"
73 "net/http"
74 )
75
76 func init() {
77 http.HandleFunc("/", handler)
78 }
79
80 func handler(w http.ResponseWriter, r *http.Request) {
81 fmt.Fprint(w, "Hello!")
82 }
83
84To 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:
85
86app.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.
87
88main.go runs the web server. It includes the "!appengine" build constraint, as it must only included when building the stand-alone binary.
89
90 // +build !appengine
91
92 package main
93
94 import "net/http"
95
96 func main() {
97 http.ListenAndServe("localhost:8080", nil)
98 }
99
Yury Smolskyb75d5042018-02-14 15:49:44 +0200100To see a more complex hybrid app, take a look at the [[https://godoc.org/golang.org/x/tools/present][present tool]].
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100101
102* Conclusions
103
104We 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.