Golang Build: Initial Sublime Text build package implementation

The documentation in the docs/ dir includes information about how
to use the package and perform any necessary configuration.

Change-Id: I132fdd2300f850d07724d76feb50361abc9e5e02
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b638fce
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*.tmLanguage.cache
+*.pyc
+__pycache__
+dev/coverage_reports/
+dev/go_projects/src/github.com/
+dev/go_projects/pkg/
+dev/go_projects/bin/
diff --git a/.pep8 b/.pep8
new file mode 100644
index 0000000..6deafc2
--- /dev/null
+++ b/.pep8
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 120
diff --git a/Default.sublime-commands b/Default.sublime-commands
new file mode 100644
index 0000000..93b893b
--- /dev/null
+++ b/Default.sublime-commands
@@ -0,0 +1,18 @@
+[
+    {
+        "caption": "Golang Build: Get",
+        "command": "golang_build_get"
+    },
+    {
+        "caption": "Golang Build: Cancel",
+        "command": "golang_build_cancel"
+    },
+    {
+        "caption": "Golang Build: Reopen Output",
+        "command": "golang_build_reopen"
+    },
+    {
+        "caption": "Golang Build: Terminal",
+        "command": "golang_build_terminal"
+    }
+]
\ No newline at end of file
diff --git a/Golang Build Output.tmLanguage b/Golang Build Output.tmLanguage
new file mode 100644
index 0000000..f09909c
--- /dev/null
+++ b/Golang Build Output.tmLanguage
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>name</key>
+	<string>Golang Build Output</string>
+	<key>patterns</key>
+	<array>
+		<dict>
+			<key>captures</key>
+			<dict>
+				<key>1</key>
+				<dict>
+					<key>name</key>
+					<string>comment.line.double-slash.go</string>
+				</dict>
+				<key>2</key>
+				<dict>
+					<key>name</key>
+					<string>markup.inserted.diff</string>
+				</dict>
+			</dict>
+			<key>match</key>
+			<string>^(&gt; Result: )(Success)$</string>
+		</dict>
+		<dict>
+			<key>captures</key>
+			<dict>
+				<key>1</key>
+				<dict>
+					<key>name</key>
+					<string>comment.line.double-slash.go</string>
+				</dict>
+				<key>2</key>
+				<dict>
+					<key>name</key>
+					<string>markup.deleted.diff</string>
+				</dict>
+			</dict>
+			<key>match</key>
+			<string>^(&gt; Result: )(Error|Cancelled)$</string>
+		</dict>
+		<dict>
+			<key>captures</key>
+			<dict>
+				<key>1</key>
+				<dict>
+					<key>name</key>
+					<string>comment.line.double-slash.go</string>
+				</dict>
+				<key>2</key>
+				<dict>
+					<key>name</key>
+					<string>markup.changed.diff</string>
+				</dict>
+			</dict>
+			<key>match</key>
+			<string>^(&gt; Elapsed: )(.*)$</string>
+		</dict>
+		<dict>
+			<key>match</key>
+			<string>^(&gt; (Directory|Environment|Command|Output):[ \n])(.*)$</string>
+			<key>name</key>
+			<string>comment.line.double-slash.go</string>
+		</dict>
+		<dict>
+			<key>match</key>
+			<string>^(&gt;   )(\w+)(=)(.*)$</string>
+			<key>name</key>
+			<string>comment.line.double-slash.go</string>
+		</dict>
+	</array>
+	<key>scopeName</key>
+	<string>output.golang_build</string>
+	<key>uuid</key>
+	<string>E3A415F0-3F50-11E0-9207-1911300C9A67</string>
+</dict>
+</plist>
diff --git a/Golang Build.sublime-build b/Golang Build.sublime-build
new file mode 100644
index 0000000..006cbf1
--- /dev/null
+++ b/Golang Build.sublime-build
@@ -0,0 +1,23 @@
+{
+    "target": "golang_build",
+    "selector": "source.go",
+
+    "variants": [
+        {
+            "name": "Test",
+            "task": "test"
+        },
+        {
+            "name": "Install",
+            "task": "install"
+        },
+        {
+            "name": "Cross-Compile (Interactive)",
+            "task": "cross_compile"
+        },
+        {
+            "name": "Clean",
+            "task": "clean"
+        }
+    ]
+}
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..3e26c6e
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,5 @@
+# *Golang Build* Changelog
+
+## 1.0.0
+
+ - Initial release
diff --git a/dependencies.json b/dependencies.json
new file mode 100644
index 0000000..5860b5a
--- /dev/null
+++ b/dependencies.json
@@ -0,0 +1,10 @@
+{
+	"*": {
+		"*": [
+			"shellenv",
+			"golangconfig",
+			"newterm",
+			"package_events"
+		]
+	}
+}
diff --git a/dev/__init__.py b/dev/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dev/__init__.py
diff --git a/dev/go_projects/src/bad/hello.go b/dev/go_projects/src/bad/hello.go
new file mode 100644
index 0000000..607814c
--- /dev/null
+++ b/dev/go_projects/src/bad/hello.go
@@ -0,0 +1,5 @@
+package main
+
+func main() {
+	fmt.Printf("Hello, world.\n")
+}
diff --git a/dev/go_projects/src/good/rune_len.go b/dev/go_projects/src/good/rune_len.go
new file mode 100644
index 0000000..765d46c
--- /dev/null
+++ b/dev/go_projects/src/good/rune_len.go
@@ -0,0 +1,9 @@
+package good
+
+func RuneLen(s string) int {
+	i := 0
+	for range s {
+		i += 1
+	}
+	return i
+}
diff --git a/dev/go_projects/src/good/rune_len_test.go b/dev/go_projects/src/good/rune_len_test.go
new file mode 100644
index 0000000..2062458
--- /dev/null
+++ b/dev/go_projects/src/good/rune_len_test.go
@@ -0,0 +1,24 @@
+package good
+
+import (
+	"testing"
+)
+
+func TestRuneLen(t *testing.T) {
+	cases := []struct {
+		s          string
+		byteLength int
+		runeLength int
+	}{
+		{"résumé", 8, 6},
+		{"résumé – new", 16, 12},
+	}
+	for _, c := range cases {
+		if len(c.s) != c.byteLength {
+			t.Errorf("len(%q) != %d", c.s, c.byteLength)
+		}
+		if RuneLen(c.s) != c.runeLength {
+			t.Errorf("RuneLen(%q) != %d", c.s, c.runeLength)
+		}
+	}
+}
diff --git a/dev/mocks.py b/dev/mocks.py
new file mode 100644
index 0000000..b471e3e
--- /dev/null
+++ b/dev/mocks.py
@@ -0,0 +1,112 @@
+# coding: utf-8
+from __future__ import unicode_literals, division, absolute_import, print_function
+
+import os
+import sys
+import locale
+
+import sublime
+import golangconfig
+
+if sys.version_info < (3,):
+    golang_build = sys.modules['golang_build']
+else:
+    golang_build = sys.modules['Golang Build.golang_build']
+
+
+class ShellenvMock():
+
+    _env_encoding = locale.getpreferredencoding() if sys.platform == 'win32' else 'utf-8'
+    _fs_encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
+
+    _shell = None
+    _data = None
+
+    def __init__(self, shell, data):
+        self._shell = shell
+        self._data = data
+
+    def get_env(self, for_subprocess=False):
+        if not for_subprocess or sys.version_info >= (3,):
+            return (self._shell, self._data)
+
+        shell = self._shell.encode(self._fs_encoding)
+        env = {}
+        for name, value in self._data.items():
+            env[name.encode(self._env_encoding)] = value.encode(self._env_encoding)
+
+        return (shell, env)
+
+    def get_path(self):
+        return (self._shell, self._data.get('PATH', '').split(os.pathsep))
+
+    def env_encode(self, value):
+        if sys.version_info >= (3,):
+            return value
+        return value.encode(self._env_encoding)
+
+    def path_encode(self, value):
+        if sys.version_info >= (3,):
+            return value
+        return value.encode(self._fs_encoding)
+
+    def path_decode(self, value):
+        if sys.version_info >= (3,):
+            return value
+        return value.decode(self._fs_encoding)
+
+
+class SublimeSettingsMock():
+
+    _values = None
+
+    def __init__(self, values):
+        self._values = values
+
+    def get(self, name, default=None):
+        return self._values.get(name, default)
+
+
+class SublimeMock():
+
+    _settings = None
+    View = sublime.View
+    Window = sublime.Window
+
+    def __init__(self, settings):
+        self._settings = SublimeSettingsMock(settings)
+
+    def load_settings(self, basename):
+        return self._settings
+
+
+class GolangBuildMock():
+
+    _shellenv = None
+    _sublime = None
+
+    _shell = None
+    _env = None
+    _sublime_settings = None
+
+    def __init__(self, shell=None, env=None, sublime_settings=None):
+        self._shell = shell
+        self._env = env
+        self._sublime_settings = sublime_settings
+
+    def __enter__(self):
+        if self._shell is not None and self._env is not None:
+            self._shellenv = golangconfig.shellenv
+            golangconfig.shellenv = ShellenvMock(self._shell, self._env)
+            golang_build.shellenv = golangconfig.shellenv
+        if self._sublime_settings is not None:
+            self._sublime = golangconfig.sublime
+            golangconfig.sublime = SublimeMock(self._sublime_settings)
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        if self._shellenv is not None:
+            golangconfig.shellenv = self._shellenv
+            golang_build.shellenv = self._shellenv
+        if self._sublime is not None:
+            golangconfig.sublime = self._sublime
diff --git a/dev/reloader.py b/dev/reloader.py
new file mode 100644
index 0000000..8ceb6e7
--- /dev/null
+++ b/dev/reloader.py
@@ -0,0 +1,23 @@
+# coding: utf-8
+from __future__ import unicode_literals, division, absolute_import, print_function
+
+import sys
+from os import path
+import time
+
+
+module_name = 'golang_build'
+if sys.version_info >= (3,):
+    module_name = 'Golang Build.' + module_name
+    from imp import reload
+
+if module_name in sys.modules:
+    reload(sys.modules[module_name])
+
+if 'Golang Build.dev.mocks' in sys.modules:
+    reload(sys.modules['Golang Build.dev.mocks'])
+
+filepath = path.join(path.dirname(__file__), '..', 'golang_build.py')
+open(filepath, 'ab').close()
+# Wait for Sublime Text to reload the file
+time.sleep(0.5)
diff --git a/dev/tests.py b/dev/tests.py
new file mode 100644
index 0000000..51c17b4
--- /dev/null
+++ b/dev/tests.py
@@ -0,0 +1,507 @@
+# coding: utf-8
+from __future__ import unicode_literals, division, absolute_import, print_function
+
+import sys
+import threading
+import unittest
+from os import path
+import time
+import re
+import shutil
+import os
+
+import sublime
+
+import shellenv
+import package_events
+
+if sys.version_info < (3,):
+    from Queue import Queue
+else:
+    from queue import Queue
+
+from .mocks import GolangBuildMock
+
+
+TEST_GOPATH = path.join(path.dirname(__file__), 'go_projects')
+VIEW_SETTINGS = {
+    'GOPATH': TEST_GOPATH,
+    'GOOS': None,
+    'GOARCH': None,
+    'GOARM': None,
+    'GO386': None,
+    'GORACE': None
+}
+
+CROSS_COMPILE_OS = 'darwin' if sys.platform != 'darwin' else 'linux'
+
+
+class GolangBuildTests(unittest.TestCase):
+
+    def setUp(self):
+        for subdir in ('pkg', 'bin', 'src'):
+            full_path = path.join(TEST_GOPATH, subdir)
+            for entry in os.listdir(full_path):
+                if entry in set(['.git-keep', 'good', 'bad']):
+                    continue
+                entry_path = path.join(full_path, entry)
+                if path.isdir(entry_path):
+                    shutil.rmtree(entry_path)
+                else:
+                    os.remove(entry_path)
+
+    def test_build(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build')
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go build" succeed?'))
+
+    def test_build_flags(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build', {'flags': ['-x']})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go build" succeed and print all commands?'))
+
+    def test_build_flags_from_settings(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        with GolangBuildMock(sublime_settings={'build:flags': ['-x']}):
+            def _run_build(view, result_queue):
+                view.window().run_command('golang_build')
+
+            result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+            result = wait_build(result_queue)
+            self.assertEqual('success', result)
+            self.assertTrue(confirm_user('Did "go build" succeed and print all commands?'))
+
+    def test_install_flags_from_view_settings(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build', {'task': 'install'})
+
+        custom_view_settings = VIEW_SETTINGS.copy()
+        custom_view_settings['install:flags'] = ['-x']
+
+        result_queue = open_file(file_path, custom_view_settings, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go install" succeed and print all commands?'))
+
+    def test_clean(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build', {'task': 'clean'})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go clean" succeed?'))
+
+    def test_test(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build', {'task': 'test'})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go test" succeed?'))
+
+    def test_install(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build', {'task': 'install'})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go install" succeed?'))
+
+    def test_cross_compile(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+        begin_event = threading.Event()
+
+        def _run_build(view, result_queue):
+            notify_user('Select %s/amd64 from quick panel' % CROSS_COMPILE_OS)
+            begin_event.set()
+            view.window().run_command('golang_build', {'task': 'cross_compile'})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        begin_event.wait()
+        result = wait_build(result_queue, timeout=15)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did the cross-compile succeed?'))
+
+    def test_get(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+        begin_event = threading.Event()
+
+        def _run_build(view, result_queue):
+            sublime.set_clipboard('github.com/golang/example/hello')
+            notify_user('Paste from the clipboard into the input panel')
+            begin_event.set()
+            view.window().run_command('golang_build_get')
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        begin_event.wait()
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go get" succeed?'))
+
+    def test_get_flags(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+        begin_event = threading.Event()
+
+        def _run_build(view, result_queue):
+            sublime.set_clipboard('github.com/golang/example/hello')
+            notify_user('Paste from the clipboard into the input panel')
+            begin_event.set()
+            view.window().run_command('golang_build_get', {'flags': ['-d']})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        begin_event.wait()
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go get" download but not install?'))
+
+    def test_get_flags_from_settings(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        with GolangBuildMock(sublime_settings={'get:flags': ['-d']}):
+            def _run_build(view, result_queue):
+                view.window().run_command('golang_build_get', {'url': 'github.com/golang/example/hello'})
+
+            result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+            result = wait_build(result_queue)
+            self.assertEqual('success', result)
+            self.assertTrue(confirm_user('Did "go get" download but not install?'))
+
+    def test_get_url(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build_get', {'url': 'github.com/golang/example/hello'})
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+        self.assertTrue(confirm_user('Did "go get" succeed for "github.com/golang/example/hello"?'))
+
+    def test_terminal(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build_terminal')
+
+        open_file(file_path, VIEW_SETTINGS, _run_build)
+        self.assertTrue(confirm_user('Did a terminal open to Packages/Golang Build/dev/go_projects/src/good/?'))
+
+    def test_build_bad(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'bad', 'hello.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build')
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('error', result)
+        self.assertTrue(confirm_user('Did "go build" fail?'))
+
+    def test_build_cancel(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build')
+
+            def _cancel_build():
+                view.window().run_command('golang_build_cancel')
+
+            sublime.set_timeout(_cancel_build, 50)
+
+        # We perform a cross-compile so the user has time to interrupt the build
+        custom_view_settings = VIEW_SETTINGS.copy()
+        custom_view_settings['GOOS'] = CROSS_COMPILE_OS
+        custom_view_settings['GOARCH'] = 'amd64'
+
+        result_queue = open_file(file_path, custom_view_settings, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('cancelled', result)
+        self.assertTrue(confirm_user('Was "go build" successfully cancelled?'))
+
+    def test_build_reopen(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+        def _run_build(view, result_queue):
+            view.window().run_command('golang_build')
+
+        result_queue = open_file(file_path, VIEW_SETTINGS, _run_build)
+        result = wait_build(result_queue)
+        self.assertEqual('success', result)
+
+        time.sleep(0.4)
+
+        def _hide_panel():
+            sublime.active_window().run_command('hide_panel')
+        sublime.set_timeout(_hide_panel, 1)
+
+        time.sleep(0.4)
+        self.assertTrue(confirm_user('Was the build output hidden?'))
+
+        def _reopen_panel():
+            sublime.active_window().run_command('golang_build_reopen')
+        sublime.set_timeout(_reopen_panel, 1)
+
+        time.sleep(0.4)
+        self.assertTrue(confirm_user('Was the build output reopened?'))
+
+    def test_build_interrupt(self):
+        ensure_not_ui_thread()
+
+        file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+        begin_event = threading.Event()
+        second_begin_event = threading.Event()
+
+        def _run_build(view, result_queue):
+            notify_user('Press the "Stop Running Build" button when prompted')
+
+            begin_event.set()
+            view.window().run_command('golang_build')
+
+            def _new_build():
+                view.window().run_command('golang_build')
+                second_begin_event.set()
+
+            sublime.set_timeout(_new_build, 50)
+
+        # We perform a cross-compile so the user has time to interrupt the build
+        custom_view_settings = VIEW_SETTINGS.copy()
+        custom_view_settings['GOOS'] = CROSS_COMPILE_OS
+        custom_view_settings['GOARCH'] = 'amd64'
+
+        result_queue = open_file(file_path, custom_view_settings, _run_build)
+        begin_event.wait()
+        result1 = wait_build(result_queue)
+        self.assertEqual('cancelled', result1)
+        second_begin_event.wait()
+        result2 = wait_build(result_queue)
+        self.assertEqual('success', result2)
+        self.assertTrue(confirm_user('Was the first build cancelled and the second successful?'))
+
+    def test_build_go_missing(self):
+        ensure_not_ui_thread()
+
+        shell, _ = shellenv.get_env()
+        search_path = path.expanduser('~')
+        with GolangBuildMock(shell=shell, env={'PATH': search_path}):
+
+            file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+            def _run_build(view, result_queue):
+                notify_user('Press the "Open Documentation" button when prompted about go not being found in the PATH')
+
+                view.window().run_command('golang_build')
+
+            open_file(file_path, VIEW_SETTINGS, _run_build)
+            time.sleep(0.5)
+            self.assertTrue(confirm_user('Were you prompted that go could not be found in the PATH?'))
+            self.assertTrue(confirm_user('When you pressed "Open Documentation", was it opened in your browser?'))
+
+    def test_build_no_gopath(self):
+        ensure_not_ui_thread()
+
+        shell, env = shellenv.get_env()
+        if 'GOPATH' in env:
+            del env['GOPATH']
+        with GolangBuildMock(shell=shell, env=env):
+
+            file_path = path.join(TEST_GOPATH, 'src', 'good', 'rune_len.go')
+
+            def _run_build(view, result_queue):
+                notify_user('Press the "Open Documentation" button when prompted about GOPATH not being set')
+
+                view.window().run_command('golang_build')
+
+            custom_view_settings = VIEW_SETTINGS.copy()
+            del custom_view_settings['GOPATH']
+            open_file(file_path, custom_view_settings, _run_build)
+            time.sleep(0.5)
+            self.assertTrue(confirm_user('Were you prompted that GOPATH was not set?'))
+            self.assertTrue(confirm_user('When you pressed "Open Documentation", was it opened in your browser?'))
+
+
+def ensure_not_ui_thread():
+    """
+    The tests won't function properly if they are run in the UI thread, so
+    this functions throws an exception if that is attempted
+    """
+
+    if isinstance(threading.current_thread(), threading._MainThread):
+        raise RuntimeError('Tests can not be run in the UI thread')
+
+
+def open_file(file_path, view_settings, callback):
+    """
+    Open a file in Sublime Text, sets settings on the view and then executes
+    the callback once the file is opened
+
+    :param file_path:
+        A unicode string of the path to the file to open
+
+    :param view_settings:
+        A dict of settings to set the "golang" key of the view's settings to
+
+    :param callback:
+        The callback to execute in the UI thread once the file is opened
+    """
+
+    result_queue = Queue()
+    file_param = file_path
+    if sys.platform == 'win32':
+        file_param = re.sub('^([a-zA-Z]):', '/\\1', file_param)
+        file_param = file_param.replace('\\', '/')
+
+    def open_file_callback():
+        window = sublime.active_window()
+
+        window.run_command(
+            'open_file',
+            {
+                'file': file_param
+            }
+        )
+
+        when_file_opened(window, file_path, view_settings, callback, result_queue)
+    sublime.set_timeout(open_file_callback, 50)
+    return result_queue
+
+
+def when_file_opened(window, file_path, view_settings, callback, result_queue):
+    """
+    Periodic polling callback used by open_file() to find the newly-opened file
+
+    :param window:
+        The sublime.Window to look for the view in
+
+    :param file_path:
+        The file path of the file that was opened
+
+    :param view_settings:
+        A dict of settings to set to the view's "golang" setting key
+
+    :param callback:
+        The callback to execute when the file is opened
+
+    :param result_queue:
+        A Queue() object the callback can use to communicate with the test
+    """
+
+    view = window.active_view()
+    if view and view.file_name() == file_path:
+        view.settings().set('golang', view_settings)
+        callback(view, result_queue)
+        return
+    # If the view was not ready, retry a short while later
+    sublime.set_timeout(lambda: when_file_opened(window, file_path, view_settings, callback, result_queue), 50)
+
+
+def wait_build(result_queue, timeout=5):
+    """
+    Uses the result queue to wait for a result from the open_file() callback
+
+    :param result_queue:
+        The Queue() to get the result from
+
+    :param timeout:
+        How long to wait before considering the test a failure
+
+    :return:
+        The value from the queue
+    """
+
+    def _send_result(package_name, event_name, payload):
+        result_queue.put(payload.result)
+
+    try:
+        package_events.listen('Golang Build', _send_result)
+        return result_queue.get(timeout=timeout)
+    finally:
+        package_events.unlisten('Golang Build', _send_result)
+
+
+def confirm_user(message):
+    """
+    Prompts the user to via a dialog to confirm a question
+
+    :param message:
+        A unicode string of the message to present to the user
+
+    :return:
+        A boolean - if the user clicked "Yes"
+    """
+
+    queue = Queue()
+
+    def _show_ok_cancel():
+        response = sublime.ok_cancel_dialog('Test Suite for Golang Build\n\n' + message, 'Yes')
+        queue.put(response)
+
+    sublime.set_timeout(_show_ok_cancel, 1)
+    return queue.get()
+
+
+def notify_user(message):
+    """
+    Open a dialog for the user to inform them of a user interaction that is
+    part of the test suite
+
+    :param message:
+        A unicode string of the message to present to the user
+    """
+
+    sublime.ok_cancel_dialog('Test Suite for Golang Build\n\n' + message, 'Ok')
diff --git a/docs/commands.md b/docs/commands.md
new file mode 100644
index 0000000..457567c
--- /dev/null
+++ b/docs/commands.md
@@ -0,0 +1,80 @@
+# *Golang Build* Commands
+
+This page lists the commands provided by this package and the arguments they
+accept.
+
+ - [Commands](#commands)
+   - [golang_build](#golang_build)
+   - [golang_build_get](#golang_build_get)
+   - [golang_build_terminal](#golang_build_terminal)
+ - [Key Binding Example](#key-binding-example)
+ - [Command Palette Example](#command-palette-example)
+
+## Commands
+
+### golang_build
+
+The `golang_build` command executes various `go` commands and accepts the
+following args:
+
+ - `task`: A string of the build task to perform. Accepts the following values:
+   - `"build"`: executes `go build -v`
+   - `"test"`: executes `go test -v`
+   - `"install"`: executes `go install -v`
+   - `"clean"`: executes `go clean -v`
+   - `"cross_compile"`: executes `go build -v` with `GOOS` and `GOARCH` set
+ - `flags`: A list of strings to pass to the `go` executable as flags. The list
+   of valid flags can be determined by executing `go help {task}` in the
+   terminal.
+
+### golang_build_get
+
+The `golang_build_get` command executes `go get -v` and accepts the following
+args:
+
+ - `url`: A string of the URL to get, instead of prompting the user for it.
+ - `flags`: A list of strings to pass to the `go` executable as flags. The list
+   of valid flags can be determined by executing `go help get` in the
+   terminal.
+
+### golang_build_terminal
+
+The `golang_build_terminal` command opens a terminal to the directory containing
+the currently open file. The command does not accept any args.
+
+## Key Binding Example
+
+The following JSON structure can be added to the file opened by the
+*Preferences > Key Bindings – User* menu entry.
+
+```json
+[
+    {
+        "keys": ["super+ctrl+g", "super+ctrl+t"],
+        "command": "golang_build",
+        "args": {
+            "task": "test",
+            "flags": ["-x"]
+        }
+    }
+]
+```
+
+## Command Palette Example
+
+The following JSON structure can be added to
+`Packages/User/Default.sublime-commands`. The `Packages/` folder can be located
+by the *Preferences > Browse Packages...* menu entry.
+
+```json
+[
+    {
+        "caption": "Golang Build: Test (Print Commands)",
+        "command": "golang_build",
+        "args": {
+            "task": "test",
+            "flags": ["-x"]
+        }
+    }
+]
+```
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..20ed406
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,198 @@
+# *Golang Build* Configuration
+
+By default, *Golang Build* looks at the user‘s shell environment to find the
+values for various Go environment variables, including `GOPATH`.
+
+In some situations the automatic detection may not be able to properly read the
+desired configuration. In other situations, it may be desirable to provide
+different configuration for different Sublime Text projects.
+
+ - [Environment Autodetection](#environment-autodetection)
+ - [Settings Load Order](#settings-load-order)
+   - [Global Sublime Text Settings](#global-sublime-text-settings)
+   - [OS-Specific Settings](#os-specific-settings)
+   - [Project-Specific Settings](#project-specific-settings)
+ - [Command Flags](#command-flags)
+
+## Environment Autodetection
+
+By default *Golang Build* tries to detect all of your Go configuration by
+invoking your login shell. It will pull in your `PATH`, `GOPATH`, and any other
+environment variables you have set.
+
+## Settings Load Order
+
+Generally, autodetecting the shell environment is sufficient for most users
+with a standard Go environment. If your Go configuration is more complex, or
+you wish to customize command flags, Golang Build will read settings from the
+following sources:
+
+ - [Global Sublime Text Settings](#global-sublime-text-settings)
+ - [OS-Specific Settings](#os-specific-settings)
+ - [Project-Specific Settings](#project-specific-settings)
+
+Settings are loaded using the following precedence, from most-to-least
+specific:
+
+ - OS-specific project settings
+ - OS-specific global Sublime Text settings
+ - Project settings
+ - Global Sublime Text settings
+ - Shell environment
+
+### Global Sublime Text Settings
+
+To set variables for use in Sublime Text windows, you will want to edit your
+`golang.sublime-settings` file. This can be accessed via the menu:
+
+ 1. Preferences
+ 2. Package Settings
+ 3. Golang Config
+ 3. Settings - User
+
+Settings are placed in a json structure. Common settings include:
+
+ - `PATH` - a list of directories to search for executables within. On Windows
+   these are separated by `;`. OS X and Linux use `:` as a directory separator.
+ - `GOPATH` - a string of the path to the root of your Go environment
+
+Other Go environment variables will be used if set. Examples include: `GOOS`,
+`GOARCH`, `GOROOT` and `GORACE`. The
+[go command documentation](https://golang.org/cmd/go/#hdr-Environment_variables)
+has a complete list.
+
+```json
+{
+    "PATH": "/Users/jsmith/go/bin",
+    "GOPATH": "/Users/jsmith/go"
+}
+```
+
+### OS-Specific Settings
+
+For users that are working on different operating systems, it may be necessary
+to segement settings per OS. All settings may be nested under a key of one of
+the following strings:
+
+ - "osx"
+ - "windows"
+ - "linux"
+
+```json
+{
+    "osx": {
+        "PATH": "/Users/jsmith/go/bin",
+        "GOPATH": "/Users/jsmith/go"
+    },
+    "windows": {
+        "PATH": "C:\\Users\\jsmith\\go\\bin",
+        "GOPATH": "C:\\Users\\jsmith\\go"
+    },
+    "linux": {
+        "PATH": "/home/jsmith/go/bin",
+        "GOPATH": "/home/jsmith/go"
+    },
+}
+```
+
+### Project-Specific Settings
+
+When working on Go projects that use different environments, it may be
+necessary to define settings in a
+[Sublime Text project](http://docs.sublimetext.info/en/latest/file_management/file_management.html#projects)
+file. The *Project* menu in Sublime Text provides the interface to create and
+edit project files.
+
+Within projects, all Go settings are placed under the `"settings"` key and then
+further under a subkey named `"golang"`.
+
+```json
+{
+    "folders": {
+        "/Users/jsmith/projects/myproj"
+    },
+    "settings": {
+        "golang": {
+            "PATH": "/Users/jsmith/projects/myproj/env/bin",
+            "GOPATH": "/Users/jsmith/projects/myproj/env"
+        }
+    }
+}
+```
+
+Project-specific settings may also utilize the OS-specific settings feature.
+
+```json
+{
+    "folders": {
+        "/Users/jsmith/projects/myproj"
+    },
+    "settings": {
+        "golang": {
+            "osx": {
+                "PATH": "/Users/jsmith/projects/myproj/env/bin",
+                "GOPATH": "/Users/jsmith/projects/myproj/env"
+            },
+            "linux": {
+                "PATH": "/home/jsmith/projects/myproj/env/bin",
+                "GOPATH": "/home/jsmith/projects/myproj/env"
+            }
+        }
+    }
+}
+```
+
+## Command Flags
+
+When working with the build system, it may be necessary to set flags to pass
+to the `go` executable. Utilizing the various settings locations discussed in
+the [Settings Load Order section](#settings-load-order), each build variant may
+have its flags customized. The settings names are:
+
+ - `build:flags` for "go build"
+ - `test:flags` for "go test"
+ - `install:flags` for "go install"
+ - `clean:flags` for "go clean"
+ - `cross_compile:flags` for "go build" with GOOS and GOARCH
+ - `get:flags` for "go get"
+
+Each setting must have a value that is a list of strings.
+
+The most common location to set these settings will be in a project file:
+
+```json
+{
+    "folders": {
+        "/Users/jsmith/projects/myproj"
+    },
+    "settings": {
+        "golang": {
+            "build:flags": ["-a"],
+            "install:flags": ["-a"],
+            "get:flags": ["-u"]
+        }
+    }
+}
+```
+
+As with the `GOPATH` and `PATH` settings, these flag settings may be set on a
+per-OS basis, even within project files.
+
+An example of settings for just OS X, within a project file:
+
+```json
+{
+    "folders": {
+        "/Users/jsmith/projects/myproj"
+    },
+    "settings": {
+        "golang": {
+            "osx": {
+                "build:flags": ["-a", "-race"],
+                "install:flags": ["-a", "-race"],
+                "get:flags": ["-u"]
+            }
+        }
+    }
+}
+```
diff --git a/docs/design.md b/docs/design.md
new file mode 100644
index 0000000..c20f484
--- /dev/null
+++ b/docs/design.md
@@ -0,0 +1,48 @@
+# *Golang Build* Design
+
+The Golang Build Sublime Text package is structured as follows:
+
+ - The primary user interaction happens through the Sublime Text build system,
+   which parses the "Golang Build.sublime-build" file
+ - The primary build task is "go build", but variants exists for "go test",
+   "go install", "go clean" and cross-compile, which is "go build" with GOOS
+   and GOARCH set. All of these tasks are executing by the Sublime Text command
+   named "golang_build".
+ - Additional Sublime Text commands are implemented that implement the following
+   functionality:
+    - "golang_build_get" provides an interface to "go get"
+    - "golang_terminal" opens a terminal to the Go workspace with all
+      appropriate environment variables set
+    - "golang_build_cancel" allows users to kill an in-process build
+    - "golang_build_reopen" allows users to reopen the build output panel
+   Each of these commands is exposed to the command palette via the file
+   Default.sublime-commands
+ - Configuration uses the Package Control dependency golangconfig, which allows
+   users to set settings globally in Sublime Text, for each OS globally,
+   per-project, or for each OS in a project
+ - Settings exists that allow users to customize command line flags on a
+   per-task-basis
+
+As is dictated by the Sublime Text API, the following list shows a mapping of
+command to Python class name:
+
+ - `golang_build`: `GolangBuildCommand()`
+ - `golang_build_get`: `GolangBuildGetCommand()`
+ - `golang_build_cancel`: `GolangBuildCancelCommand()`
+ - `golang_build_reopen`: `GolangBuildReopenCommand()`
+ - `golang_build_terminal`: `GolangBuildTerminalCommand()`
+
+For `golang_build` and `golang_build_get`, the commands display output to the
+user via an output panel. Normally with Sublime Text when a reference to the
+`sublime.View` object for an output panel is requested, any existing content is
+erased. To prevent a jarring experience for users when a build is interrupted,
+a reference to each window's Golang Build output panel is held in memory and
+re-used when a user interrupts a running build with a new invocation.
+
+The `GolangProcess()` class reprents an invocation of the `go` executable, and
+provides a queue of output information. This output queue is processed by a
+`GolangProcessPrinter()` object which adds environment information before the
+output starts, and summary information once completed. There is one
+`GolangPanel()` object per Sublime Text window, and it contains a lock to ensure
+that only one `GolangProcessPrinter()` may be displaying output at a time to
+prevent interleaved output.
diff --git a/docs/development.md b/docs/development.md
new file mode 100644
index 0000000..7234327
--- /dev/null
+++ b/docs/development.md
@@ -0,0 +1,29 @@
+# *Golang Build* Development
+
+## Setup
+
+ - Install [Package Coverage](https://packagecontrol.io/packages/Package%20Coverage)
+   to run tests
+ - Install this package by executing
+   `git clone https://go.googlesource.com/sublime-build "Golang Build"`
+   inside of your `Packages/` folder
+ - Use the Package Control command "Satisfy Dependencies" to install the
+   `shellenv`, `newterm`, `package_events` and `golangconfig` dependencies
+   and then restart Sublime Text
+
+## General Notes
+
+ - All code must pass the checks of the Sublime Text package
+   [Python Flake8 Lint](https://packagecontrol.io/packages/Python%20Flake8%20Lint).
+   The `python_interpreter` setting should be set to `internal`.
+ - Tests and coverage measurement can not be run in the UI thread since the
+   tests interact with the user interface and would become deadlocked
+ - Sublime Text 2 and 3 must be supported, on Windows, OS X and Linux
+ - All functions must include a full docstring with parameter and return types
+   and a list of exceptions raised
+ - All code should use a consistent Python header
+
+```python
+# coding: utf-8
+from __future__ import unicode_literals, division, absolute_import, print_function
+```
diff --git a/docs/readme.md b/docs/readme.md
new file mode 100644
index 0000000..dfb1ffe
--- /dev/null
+++ b/docs/readme.md
@@ -0,0 +1,35 @@
+# Golang Build
+
+*Golang Build* is a Sublime Text package for compiling Go projects. It provides
+integration between Sublime Text and the command line `go` tool.
+
+The package consists of the following features:
+
+ - A Sublime Text build system for executing:
+   - `go build`
+   - `go install`
+   - `go test`
+   - `go clean`
+   - Cross-compilation using `go build` with `GOOS` and `GOARCH`
+ - Sublime Text command palette commands to:
+   - Execute `go get`
+   - Open a terminal into a Go workspace
+
+## Documentation
+
+### End User
+
+ - [Usage](usage.md)
+ - [Configuration](configuration.md)
+ - [Commands](commands.md)
+ - [Changelog](../changelog.md)
+ - [License](../LICENSE)
+ - [Patents](../PATENTS)
+
+### Contributor
+
+ - [Contributing](../CONTRIBUTING.md)
+ - [Design](design.md)
+ - [Development](development.md)
+ - [Contributors](../CONTRIBUTORS)
+ - [Authors](../AUTHORS)
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 0000000..13afcb8
--- /dev/null
+++ b/docs/usage.md
@@ -0,0 +1,75 @@
+# *Golang Build* Usage
+
+The primary functionality of the *Golang Build* package is the *Golang Build*
+build system. It includes a number of what Sublime Text refers to as "variants."
+It also includes a couple of regular Sublime Text commands for common, related
+tasks.
+
+ - [Build System](#build-system)
+ - [Other Commands](#other-commands)
+ - [Configuration](#configuration)
+ - [Commands](#commands)
+
+## Build System
+
+To use the *Golang Build* build system, open the *Tools > Build System* menu and
+select *Golang Build*.
+
+The variants included with the build system include:
+
+ - **Build**, which executes `go build`
+ - **Test**, which executes `go test`
+ - **Install**, which executes `go install`
+ - **Cross-Compile (Interactive)**, which executes `go build` with `GOOS` and
+   `GOARCH` set
+ - **Clean**, which executes `go clean`
+
+Once the *Golang Build* build system is selected, the command palette can be
+used to run any of the variants.
+
+On Sublime Text 3, the command palette entries will be:
+
+ - `Build with: Golang Build`
+ - `Build with: Golang Build - Test`
+ - `Build with: Golang Build - Install`
+ - `Build with: Golang Build - Cross-Compile (Interactive)`
+ - `Build with: Golang Build - Clean`
+
+On Sublime Text 2, the command palette entries will be:
+
+ - `Build: Build`
+ - `Build: Test`
+ - `Build: Install`
+ - `Build: Cross-Compile (Interactive)`
+ - `Build: Clean`
+
+### Cancelling a Build
+
+If a build is running and needs to be stopped, the command palette will contain
+an extra entry `Golang Build: Cancel`.
+
+### Reopening Build Results
+
+If the output panel for a build is closed, it can be re-opened by using the
+command palette to run `Golang Build: Reopen Output`. *Once a new build is
+started, the old build output is erased.*
+
+## Other Commands
+
+In addition to the build system variants, two other command palette commands are
+available:
+
+ - `Golang Build: Get`, which executes `go get` after prompting for a URL
+ - `Golang Build: Terminal`, which opens a terminal and sets relevant Go
+   environment variables
+
+## Configuration
+
+To control the environment variables used with the build system, please read
+the [configuration documentation](configuration.md).
+
+## Commands
+
+For information on the available commands, their arguments and example key
+bindings and command palette entries, please read the
+[commands documentation](commands.md).
diff --git a/golang_build.py b/golang_build.py
new file mode 100644
index 0000000..7062af9
--- /dev/null
+++ b/golang_build.py
@@ -0,0 +1,1021 @@
+# coding: utf-8
+from __future__ import unicode_literals, division, absolute_import, print_function
+
+import sys
+import os
+import threading
+import subprocess
+import time
+import re
+import textwrap
+import collections
+
+if sys.version_info < (3,):
+    import Queue as queue
+else:
+    import queue
+
+import sublime
+import sublime_plugin
+
+import shellenv
+import golangconfig
+import newterm
+import package_events
+
+
+# A list of the environment variables to pull from settings when creating a
+# subprocess. Some subprocesses may have one or more manually overridden.
+GO_ENV_VARS = set([
+    'GOPATH',
+    'GOROOT',
+    'GOROOT_FINAL',
+    'GOBIN',
+    'GOHOSTOS',
+    'GOHOSTARCH',
+    'GOOS',
+    'GOARCH',
+    'GOARM',
+    'GO386',
+    'GORACE',
+])
+
+# References to any existing GolangProcess() for a sublime.Window.id(). For
+# basic get and set operations, the dict is threadsafe.
+_PROCS = {}
+
+# References to any existing GolangPanel() for a sublime.Window.id(). For
+# basic get and set operations, the dict is threadsafe.
+_PANELS = {}
+_PANEL_LOCK = threading.Lock()
+
+
+class GolangBuildCommand(sublime_plugin.WindowCommand):
+
+    """
+    Command to run "go build", "go install", "go test" and "go clean"
+    """
+
+    def run(self, task='build', flags=None):
+        """
+        Runs the "golang_build" command - invoked by Sublime Text via the
+        command palette or sublime.Window.run_command()
+
+        :param task:
+            A unicode string of "build", "test", "install", "clean"
+            or "cross_compile"
+
+        :param flags:
+            A list of unicode strings of flags to send to the command-line go
+            tool. The "cross_compile" task executes the "build" command with
+            the GOOS and GOARCH environment variables set, meaning that
+            flags for "build" should be used with it. Execute "go help" on the
+            command line to learn about available flags.
+        """
+
+        if _yeild_to_running_build(self.window):
+            return
+
+        working_dir = _determine_working_dir(self.window)
+        if working_dir is None:
+            return
+
+        go_bin, env = _get_config(
+            'go',
+            set(['GOPATH']),
+            GO_ENV_VARS - set(['GOPATH']),
+            view=self.window.active_view(),
+            window=self.window,
+        )
+        if (go_bin, env) == (None, None):
+            return
+
+        if flags is None:
+            flags, _ = golangconfig.setting_value(
+                '%s:flags' % task,
+                view=self.window.active_view(),
+                window=self.window
+            )
+
+        if task == 'cross_compile':
+            _task_cross_compile(
+                self,
+                go_bin,
+                flags,
+                working_dir,
+                env
+            )
+            return
+
+        args = [go_bin, task, '-v']
+        if flags and isinstance(flags, list):
+            args.extend(flags)
+        proc = _run_process(
+            task,
+            self.window,
+            args,
+            working_dir,
+            env
+        )
+        _set_proc(self.window, proc)
+
+
+def _task_cross_compile(command, go_bin, flags, working_dir, env):
+    """
+    Prompts the user to select the OS and ARCH to use for a cross-compile
+
+    :param command:
+        A sublime_plugin.WindowCommand object
+
+    :param go_bin:
+        A unicode string with the path to the "go" executable
+
+    :param flags:
+        A list of unicode string of flags to pass to the "go" executable
+
+    :param working_dir:
+        A unicode string with the working directory for the "go" executable
+
+    :param env:
+        A dict of environment variables to use with the "go" executable
+    """
+
+    valid_combinations = [
+        ('darwin', '386'),
+        ('darwin', 'amd64'),
+        ('darwin', 'arm'),
+        ('darwin', 'arm64'),
+        ('dragonfly', 'amd64'),
+        ('freebsd', '386'),
+        ('freebsd', 'amd64'),
+        ('freebsd', 'arm'),
+        ('linux', '386'),
+        ('linux', 'amd64'),
+        ('linux', 'arm'),
+        ('linux', 'arm64'),
+        ('linux', 'ppc64'),
+        ('linux', 'ppc64le'),
+        ('netbsd', '386'),
+        ('netbsd', 'amd64'),
+        ('netbsd', 'arm'),
+        ('openbsd', '386'),
+        ('openbsd', 'amd64'),
+        ('openbsd', 'arm'),
+        ('plan9', '386'),
+        ('plan9', 'amd64'),
+        ('solaris', 'amd64'),
+        ('windows', '386'),
+        ('windows', 'amd64'),
+    ]
+
+    def on_done(index):
+        """
+        Processes the user's input and launch the build process
+
+        :param index:
+            The index of the option the user selected, or -1 if cancelled
+        """
+
+        if index == -1:
+            return
+
+        env['GOOS'], env['GOARCH'] = valid_combinations[index]
+
+        args = [go_bin, 'build', '-v']
+        if flags and isinstance(flags, list):
+            args.extend(flags)
+        proc = _run_process(
+            'cross_compile',
+            command.window,
+            args,
+            working_dir,
+            env
+        )
+        _set_proc(command.window, proc)
+
+    quick_panel_options = []
+    for os_, arch in valid_combinations:
+        quick_panel_options.append('OS: %s, ARCH: %s' % (os_, arch))
+
+    command.window.show_quick_panel(
+        quick_panel_options,
+        on_done
+    )
+
+
+class GolangBuildCancelCommand(sublime_plugin.WindowCommand):
+
+    """
+    Terminates any existing "go" process that is running for the current window
+    """
+
+    def run(self):
+        proc = _get_proc(self.window)
+        if proc and not proc.finished:
+            proc.terminate()
+        if proc is not None:
+            _set_proc(self.window, None)
+
+    def is_enabled(self):
+        proc = _get_proc(self.window)
+        if not proc:
+            return False
+        return not proc.finished
+
+
+class GolangBuildReopenCommand(sublime_plugin.WindowCommand):
+
+    """
+    Reopens the output from the last build command
+    """
+
+    def run(self):
+        self.window.run_command('show_panel', {'panel': 'output.golang_build'})
+
+
+class GolangBuildGetCommand(sublime_plugin.WindowCommand):
+
+    """
+    Prompts the use to enter the URL of a Go package to get
+    """
+
+    def run(self, url=None, flags=None):
+        """
+        Runs the "golang_build_get" command - invoked by Sublime Text via the
+        command palette or sublime.Window.run_command()
+
+        :param url:
+            A unicode string of the URL to download, instead of prompting the
+            user
+
+        :param flags:
+            A list of unicode strings of flags to send to the command-line go
+            tool. Execute "go help" on the command line to learn about available
+            flags.
+        """
+
+        if _yeild_to_running_build(self.window):
+            return
+
+        working_dir = _determine_working_dir(self.window)
+        if working_dir is None:
+            return
+
+        go_bin, env = _get_config(
+            'go',
+            set(['GOPATH']),
+            GO_ENV_VARS - set(['GOPATH']),
+            view=self.window.active_view(),
+            window=self.window,
+        )
+        if (go_bin, env) == (None, None):
+            return
+
+        if flags is None:
+            flags, _ = golangconfig.setting_value(
+                'get:flags',
+                view=self.window.active_view(),
+                window=self.window
+            )
+
+        def on_done(get_url):
+            """
+            Processes the user's input and launches the "go get" command
+
+            :param get_url:
+                A unicode string of the URL to get
+            """
+
+            args = [go_bin, 'get', '-v']
+            if flags and isinstance(flags, list):
+                args.extend(flags)
+            args.append(get_url)
+            proc = _run_process(
+                'get',
+                self.window,
+                args,
+                working_dir,
+                env
+            )
+            _set_proc(self.window, proc)
+
+        if url is not None:
+            on_done(url)
+            return
+
+        self.window.show_input_panel(
+            'go get',
+            '',
+            on_done,
+            None,
+            None
+        )
+
+
+class GolangBuildTerminalCommand(sublime_plugin.WindowCommand):
+
+    """
+    Opens a terminal for the user to the directory containing the open file,
+    setting any necessary environment variables
+    """
+
+    def run(self):
+        """
+        Runs the "golang_build_terminal" command - invoked by Sublime Text via
+        the command palette or sublime.Window.run_command()
+        """
+
+        working_dir = _determine_working_dir(self.window)
+        if working_dir is None:
+            return
+
+        relevant_sources = set([
+            'project file',
+            'project file (os-specific)',
+            'golang.sublime-settings',
+            'golang.sublime-settings (os-specific)'
+        ])
+
+        env_overrides = {}
+        for var_name in GO_ENV_VARS:
+            value, source = golangconfig.setting_value(var_name, window=self.window)
+            # Only set overrides that are not coming from the user's shell
+            if source in relevant_sources:
+                env_overrides[var_name] = value
+
+        # Get the PATH from the shell environment and then prepend any custom
+        # value so the user's terminal searches all locations
+        value, source = golangconfig.setting_value('PATH', window=self.window)
+        if source in relevant_sources:
+            shell, env = shellenv.get_env()
+            env_overrides['PATH'] = value + os.pathsep + env.get('PATH', '')
+
+        newterm.launch_terminal(working_dir, env=env_overrides)
+
+
+def _yeild_to_running_build(window):
+    """
+    Check if a build is already running, and if so, allow the user to stop it,
+    or cancel the new build
+
+    :param window:
+        A sublime.Window of the window the build is being run in
+
+    :return:
+        A boolean - if the new build should be abandoned
+    """
+
+    proc = _get_proc(window)
+    if proc and not proc.finished:
+        message = _format_message("""
+            Golang Build
+
+            There is already a build running. Would you like to stop it?
+        """)
+        if not sublime.ok_cancel_dialog(message, 'Stop Running Build'):
+            return True
+        proc.terminate()
+        _set_proc(window, None)
+
+    return False
+
+
+def _determine_working_dir(window):
+    """
+    Determine the working directory for a command based on the user's open file
+    or open folders
+
+    :param window:
+        The sublime.Window object of the window the command was run on
+
+    :return:
+        A unicode string of the working directory, or None if no working
+        directory was found
+    """
+
+    view = window.active_view()
+    working_dir = None
+
+    # If a file is open, get the folder from the file, and error if the file
+    # has not been saved yet
+    if view:
+        if view.file_name():
+            working_dir = os.path.dirname(view.file_name())
+
+    # If no file is open, then get the list of folders and grab the first one
+    else:
+        folders = window.folders()
+        if len(folders) > 0:
+            working_dir = folders[0]
+
+    if working_dir is None or not os.path.exists(working_dir):
+        message = _format_message("""
+            Golang Build
+
+            No files or folders are open, or the open file or folder does not exist on disk
+        """)
+        sublime.error_message(message)
+        return None
+
+    return working_dir
+
+
+def _get_config(executable_name, required_vars, optional_vars=None, view=None, window=None):
+    """
+    :param executable_name:
+        A unicode string of the executable to locate, e.g. "go" or "gofmt"
+
+    :param required_vars:
+        A list of unicode strings of the environment variables that are
+        required, e.g. "GOPATH". Obtains values from setting_value().
+
+    :param optional_vars:
+        A list of unicode strings of the environment variables that are
+        optional, but should be pulled from setting_value() if available - e.g.
+        "GOOS", "GOARCH". Obtains values from setting_value().
+
+    :param view:
+        A sublime.View object to use in finding project-specific settings. This
+        should be passed whenever available.
+
+    :param window:
+        A sublime.Window object to use in finding project-specific settings.
+        This will only work for Sublime Text 3, and should only be passed if
+        no sublime.View object is available to pass via the view parameter.
+
+    :return:
+        A two-element tuple.
+
+        If there was an error finding the executable or required vars:
+
+         - [0] None
+         - [1] None
+
+        Otherwise:
+
+         - [0] A string of the path to the executable
+         - [1] A dict of environment variables for the executable
+    """
+
+    try:
+        return golangconfig.subprocess_info(
+            executable_name,
+            required_vars,
+            optional_vars,
+            view=view,
+            window=window
+        )
+
+    except (golangconfig.ExecutableError) as e:
+        error_message = '''
+            Golang Build
+
+            The %s executable could not be found. Please ensure it is
+            installed and available via your PATH.
+
+            Would you like to view documentation for setting a custom PATH?
+        '''
+
+        prompt = error_message % e.name
+
+        if sublime.ok_cancel_dialog(_format_message(prompt), 'Open Documentation'):
+            window.run_command(
+                'open_url',
+                {'url': 'https://go.googlesource.com/sublime-build/+/master/docs/configuration.md'}
+            )
+
+    except (golangconfig.EnvVarError) as e:
+        error_message = '''
+            Golang Build
+
+            The setting%s %s could not be found in your Sublime Text
+            settings or your shell environment.
+
+            Would you like to view the configuration documentation?
+        '''
+
+        plural = 's' if len(e.missing) > 1 else ''
+        setting_names = ', '.join(e.missing)
+        prompt = error_message % (plural, setting_names)
+
+        if sublime.ok_cancel_dialog(_format_message(prompt), 'Open Documentation'):
+            window.run_command(
+                'open_url',
+                {'url': 'https://go.googlesource.com/sublime-build/+/master/docs/configuration.md'}
+            )
+
+    return (None, None)
+
+
+class GolangProcess():
+
+    """
+    A wrapper around subprocess.Popen() that provides information about how
+    the process was started and finished, plus a queue.Queue of output
+    """
+
+    # A float of the unix timestamp of when the process was started
+    started = None
+
+    # A list of strings (unicode for Python 3, byte string for Python 2) of
+    # the process path and any arguments passed to it
+    args = None
+
+    # A unicode string of the process working directory
+    cwd = None
+
+    # A dict of the env passed to the process
+    env = None
+
+    # A subprocess.Popen() object of the running process
+    proc = None
+
+    # A queue.Queue object of output from the process
+    output = None
+
+    # The result of the process, a unicode string of "cancelled", "success" or "error"
+    result = None
+
+    # A float of the unix timestamp of when the process ended
+    finished = None
+
+    # A threading.Lock() used to prevent the stdout and stderr handlers from
+    # both trying to perform process cleanup at the same time
+    _cleanup_lock = None
+
+    def __init__(self, args, cwd, env):
+        """
+        :param args:
+            A list of strings (unicode for Python 3, byte string for Python 2)
+            of the process path and any arguments passed to it
+
+        :param cwd:
+            A unicode string of the working directory for the process
+
+        :param env:
+            A dict of strings (unicode for Python 3, byte string for Python 2)
+            to pass to the process as the environment variables
+        """
+
+        self.args = args
+        self.cwd = cwd
+        self.env = env
+
+        startupinfo = None
+        if sys.platform == 'win32':
+            startupinfo = subprocess.STARTUPINFO()
+            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+
+        self._cleanup_lock = threading.Lock()
+        self.started = time.time()
+        self.proc = subprocess.Popen(
+            args,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            cwd=cwd,
+            env=env,
+            startupinfo=startupinfo
+        )
+        self.finished = False
+
+        self.output = queue.Queue()
+
+        self._stdout_thread = threading.Thread(
+            target=self._read_output,
+            args=(
+                self.output,
+                self.proc.stdout.fileno(),
+                'stdout'
+            )
+        )
+        self._stdout_thread.start()
+
+        self._stderr_thread = threading.Thread(
+            target=self._read_output,
+            args=(
+                self.output,
+                self.proc.stderr.fileno(),
+                'stderr'
+            )
+        )
+        self._stderr_thread.start()
+
+        self._cleanup_thread = threading.Thread(target=self._cleanup)
+        self._cleanup_thread.start()
+
+    def wait(self):
+        """
+        Blocks waiting for the subprocess to complete
+        """
+
+        self._cleanup_thread.wait()
+
+    def terminate(self):
+        """
+        Terminates the subprocess
+        """
+
+        self._cleanup_lock.acquire()
+        try:
+            if not self.proc:
+                return
+            self.proc.terminate()
+            self.result = 'cancelled'
+            self.finished = time.time()
+            self.proc = None
+        finally:
+            self._cleanup_lock.release()
+
+    def _read_output(self, output_queue, fileno, output_type):
+        """
+        Handler to process output from stdout/stderr
+
+        RUNS IN A THREAD
+
+        :param output_queue:
+            The queue.Queue object to add the output to
+
+        :param fileno:
+            The fileno to read output from
+
+        :param output_type:
+            A unicode string of "stdout" or "stderr"
+        """
+
+        while self.proc and self.proc.poll() is None:
+            chunk = os.read(fileno, 32768)
+            if len(chunk) == 0:
+                break
+            output_queue.put((output_type, chunk.decode('utf-8')))
+
+    def _cleanup(self):
+        """
+        Cleans up the subprocess and marks the state of self appropriately
+
+        RUNS IN A THREAD
+        """
+
+        self._stdout_thread.join()
+        self._stderr_thread.join()
+
+        self._cleanup_lock.acquire()
+        try:
+            if not self.proc:
+                return
+            # Get the returncode to prevent a zombie/defunct child process
+            self.proc.wait()
+            self.result = 'success' if self.proc.returncode == 0 else 'error'
+            self.finished = time.time()
+            self.proc = None
+        finally:
+            self._cleanup_lock.release()
+            self.output.put(('eof', None))
+
+
+class GolangProcessPrinter():
+
+    """
+    Describes a Go process, the environment it was started in and its result
+    """
+
+    # The GolangProcess() object the printer is displaying output from
+    proc = None
+
+    # The GolangPanel() object the information is written to
+    panel = None
+
+    def __init__(self, proc, panel):
+        """
+        :param proc:
+            A GolangProcess() object
+
+        :param panel:
+            A GolangPanel() object to write information to
+        """
+
+        self.proc = proc
+        self.panel = panel
+
+        self.thread = threading.Thread(
+            target=self._run
+        )
+        self.thread.start()
+
+    def _run(self):
+        """
+        GolangProcess() output queue processor
+
+        RUNS IN A THREAD
+        """
+
+        self.panel.printer_lock.acquire()
+
+        try:
+            self._write_header()
+
+            while True:
+                message_type, message = self.proc.output.get()
+
+                if message_type == 'eof':
+                    break
+
+                if message_type == 'stdout':
+                    output = message
+
+                if message_type == 'stderr':
+                    output = message
+
+                self.panel.write(output)
+
+            self._write_footer()
+
+        finally:
+            self.panel.printer_lock.release()
+
+    def _write_header(self):
+        """
+        Displays startup information about the process
+        """
+
+        title = ''
+
+        env_vars = []
+        for var_name in GO_ENV_VARS:
+            var_key = var_name if sys.version_info >= (3,) else var_name.encode('ascii')
+            if var_key in self.proc.env:
+                value = self.proc.env.get(var_key)
+                if sys.version_info < (3,):
+                    value = value.decode('utf-8')
+                env_vars.append((var_name, value))
+        if env_vars:
+            title += '> Environment:\n'
+            for var_name, value in env_vars:
+                title += '>   %s=%s\n' % (var_name, value)
+
+        title += '> Directory: %s\n' % self.proc.cwd
+        title += '> Command: %s\n' % subprocess.list2cmdline(self.proc.args)
+        title += '> Output:\n'
+
+        self.panel.write(title, content_separator='\n\n')
+
+    def _write_footer(self):
+        """
+        Displays result information about the process, blocking until the
+        write is completed
+        """
+
+        formatted_result = self.proc.result.title()
+        runtime = self.proc.finished - self.proc.started
+
+        output = '> Elapsed: %0.3fs\n> Result: %s' % (runtime, formatted_result)
+
+        event = threading.Event()
+        self.panel.write(output, content_separator='\n', event=event)
+        event.wait()
+
+        package_events.notify(
+            'Golang Build',
+            'build_complete',
+            BuildCompleteEvent(
+                task='',
+                args=list(self.proc.args),
+                working_dir=self.proc.cwd,
+                env=self.proc.env.copy(),
+                runtime=runtime,
+                result=self.proc.result
+            )
+        )
+
+
+BuildCompleteEvent = collections.namedtuple(
+    'BuildCompleteEvent',
+    [
+        'task',
+        'args',
+        'working_dir',
+        'env',
+        'runtime',
+        'result',
+    ]
+)
+
+
+class GolangPanel():
+
+    """
+    Holds a reference to an output panel used by the Golang Build package,
+    and provides synchronization features to ensure output is printed in proper
+    order
+    """
+
+    # A sublime.View object of the output panel being printed to
+    panel = None
+
+    # A queue.Queue() that holds all of the info to be written to the panel
+    queue = None
+
+    # A lock used to ensure only on GolangProcessPrinter() is using the panel
+    # at any given time
+    printer_lock = None
+
+    def __init__(self, window):
+        """
+        :param window:
+            The sublime.Window object the output panel is contained within
+        """
+
+        self.printer_lock = threading.Lock()
+        self.reset(window)
+
+    def reset(self, window):
+        """
+        Creates a new, fresh output panel and output Queue object
+
+        :param window:
+            The sublime.Window object the output panel is contained within
+        """
+
+        if not isinstance(threading.current_thread(), threading._MainThread):
+            raise RuntimeError('GolangPanel.reset() must be run in the UI thread')
+
+        self.queue = queue.Queue()
+        self.panel = window.get_output_panel('golang_build')
+
+        st_settings = sublime.load_settings('Preferences.sublime-settings')
+        panel_settings = self.panel.settings()
+        panel_settings.set('syntax', 'Packages/Golang Build/Golang Build Output.tmLanguage')
+        panel_settings.set('color_scheme', st_settings.get('color_scheme'))
+        panel_settings.set('draw_white_space', 'selection')
+        panel_settings.set('word_wrap', False)
+        panel_settings.set("auto_indent", False)
+        panel_settings.set('line_numbers', False)
+        panel_settings.set('gutter', False)
+        panel_settings.set('scroll_past_end', False)
+
+    def write(self, string, content_separator=None, event=None):
+        """
+        Queues data to be written to the output panel. Normally this will be
+        called from a thread other than the UI thread.
+
+        :param string:
+            A unicode string to write to the output panel
+
+        :param content_separator:
+            A unicode string to prefix to the string param if there is already
+            output in the output panel. Is only prefixed if the previous number
+            of characters are not equal to this string.
+
+        :param event:
+            An optional threading.Event() object to set once the data has been
+            written to the output panel
+        """
+
+        self.queue.put((string, content_separator, event))
+        sublime.set_timeout(self._process_queue, 1)
+
+    def _process_queue(self):
+        """
+        A callback that is run in the UI thread to actually perform writes to
+        the output panel. Reads from the queue until it is empty.
+        """
+
+        try:
+            while True:
+                chars, content_separator, event = self.queue.get(False)
+
+                if content_separator is not None and self.panel.size() > 0:
+                    end = self.panel.size()
+                    start = end - len(content_separator)
+                    if self.panel.substr(sublime.Region(start, end)) != content_separator:
+                        chars = content_separator + chars
+
+                # In Sublime Text 2, the "insert" command does not handle newlines
+                if sys.version_info < (3,):
+                    edit = self.panel.begin_edit('golang_panel_print', [])
+                    self.panel.insert(edit, self.panel.size(), chars)
+                    self.panel.end_edit(edit)
+
+                else:
+                    self.panel.run_command('insert', {'characters': chars})
+
+                if event:
+                    event.set()
+
+        except (queue.Empty):
+            pass
+
+
+def _run_process(task, window, args, cwd, env):
+    """
+    Starts a GolangProcess() and creates a GolangProcessPrinter() for it
+
+    :param task:
+        A unicode string of the build task name - one of "build", "test",
+        "cross_compile", "install", "clean", "get"
+
+    :param window:
+        A sublime.Window object of the window to display the output panel in
+
+    :param args:
+        A list of strings (unicode for Python 3, byte string for Python 2)
+        of the process path and any arguments passed to it
+
+    :param cwd:
+        A unicode string of the working directory for the process
+
+    :param env:
+        A dict of strings (unicode for Python 3, byte string for Python 2)
+        to pass to the process as the environment variables
+
+    :return:
+        A GolangProcess() object
+    """
+
+    panel = _get_panel(window)
+
+    proc = GolangProcess(args, cwd, env)
+
+    # If there is no printer using the panel, reset it
+    if panel.printer_lock.acquire(False):
+        panel.reset(window)
+        panel.printer_lock.release()
+
+    GolangProcessPrinter(proc, panel)
+
+    window.run_command('show_panel', {'panel': 'output.golang_build'})
+
+    return proc
+
+
+def _set_proc(window, proc):
+    """
+    Sets the GolangProcess() object associated with a sublime.Window
+
+    :param window:
+        A sublime.Window object
+
+    :param proc:
+        A GolangProcess() object that is being run for the window
+    """
+
+    _PROCS[window.id()] = proc
+
+
+def _get_proc(window):
+    """
+    Returns the GolangProcess() object associated with a sublime.Window
+
+    :param window:
+        A sublime.Window object
+
+    :return:
+        None or a GolangProcess() object. The GolangProcess() may or may not
+        still be running.
+    """
+
+    return _PROCS.get(window.id())
+
+
+def _get_panel(window):
+    """
+    Returns the GolangPanel() object associated with a sublime.Window
+
+    :param window:
+        A sublime.Window object
+
+    :return:
+        A GolangPanel() object
+    """
+
+    _PANEL_LOCK.acquire()
+    try:
+        if window.id() not in _PANELS:
+            _PANELS[window.id()] = GolangPanel(window)
+        return _PANELS.get(window.id())
+    finally:
+        _PANEL_LOCK.release()
+
+
+def _format_message(string):
+    """
+    Takes a multi-line string and does the following:
+
+     - dedents
+     - converts newlines with text before and after into a single line
+     - strips leading and trailing whitespace
+
+    :param string:
+        The string to format
+
+    :return:
+        The formatted string
+    """
+
+    output = textwrap.dedent(string)
+
+    # Unwrap lines, taking into account bulleted lists, ordered lists and
+    # underlines consisting of = signs
+    if output.find('\n') != -1:
+        output = re.sub('(?<=\\S)\n(?=[^ \n\t\\d\\*\\-=])', ' ', output)
+
+    return output.strip()
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..5ab1173
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,45 @@
+# Golang Build
+
+*Golang Build* is a Sublime Text package for compiling Go projects. It provides
+integration between Sublime Text and the command line `go` tool.
+
+The package consists of the following features:
+
+ - A Sublime Text build system for executing:
+   - `go build`
+   - `go install`
+   - `go test`
+   - `go clean`
+   - Cross-compilation using `go build` with `GOOS` and `GOARCH`
+ - Sublime Text command palette commands to:
+   - Execute `go get`
+   - Open a terminal into a Go workspace
+
+## Installation
+
+The *Golang Build* package is installed by using
+[Package Control](https://packagecontrol.io).
+
+ - If Package Control is not installed, follow the [Installation Instructions](https://packagecontrol.io/installation)
+ - Open the Sublime Text command palette and run the `Package Control: Install
+   Package` command
+ - Type `Golang Build` and select the package to perform the installation
+
+## Documentation
+
+### End User
+
+ - [Usage](docs/usage.md)
+ - [Configuration](docs/configuration.md)
+ - [Commands](docs/commands.md)
+ - [Changelog](changelog.md)
+ - [License](LICENSE)
+ - [Patents](PATENTS)
+
+### Contributor
+
+ - [Contributing](CONTRIBUTING.md)
+ - [Design](docs/design.md)
+ - [Development](docs/development.md)
+ - [Contributors](CONTRIBUTORS)
+ - [Authors](AUTHORS)