Import zx from zircon/vdso, and various other fixes

This CL enables `using zx` to work in fidlbolt. The zx library used to
be a fidlc built-in, but as of Ib026655c3e8133bb14c92d0a5a68e730e26b746d
it is an ordinary library. This means fidlbolt needs to search in
//zircon/vdso so find its .fidl files.

In addition, this CL:

* Restructures the deployment/ directory to put all FIDL libraries under
  deployment/fuchsia/. This fixes the info tooltips which say where the
  library was found (library.go searches for "/fuchsia/" in the path).

* Sources fx-env.sh in prepare_deployment.sh so that users with multiple
  checkouts can simply run `FUCHSIA_DIR=... ./prepare_deployment.sh`.

* Changes the Makefile to use lowercase variable names, to distinguish
  them from user-configurable env variables which are in uppercase.

* Improves the error message when the fidlbolt_deployment.json file
  cannot be read or fails to parse.

* Updates the README to recommend installing npm instead of nodejs (both
  are required, and the former gets you both).

* Changes the frontend to use <html lang="en">.

Change-Id: I0ff7ebba77089e483a506e522f7fc5ba1b482e91
diff --git a/Dockerfile b/Dockerfile
index fb8e4e6..743d2ec 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,4 +29,4 @@
     "-static", "/static", \
     "-bin", "/deployment/bin", \
     "-etc", "/deployment/etc", \
-    "-fidl", "/deployment/sdk/fidl:/deployment/zircon/system/fidl"]
+    "-fidl", "/deployment/fuchsia/sdk/fidl:/deployment/fuchsia/zircon:/deployment/fuchsia/zircon/system/fidl"]
diff --git a/Makefile b/Makefile
index a3e19a8..c23d252 100644
--- a/Makefile
+++ b/Makefile
@@ -7,33 +7,40 @@
 DEPLOYMENT ?= 0
 
 # Detect host platform.
-UNAME_S := $(shell uname -s)
-ifeq ($(UNAME_S),Linux)
-HOST_PLATFORM := linux-x64
-else ifeq ($(UNAME_S),Darwin)
-HOST_PLATFORM := mac-x64
+uname_s := $(shell uname -s)
+ifeq ($(uname_s),Linux)
+host_platform := linux-x64
+else ifeq ($(uname_s),Darwin)
+host_platform := mac-x64
 else
-$(error Unsupported platform $(UNAME_S))
+$(error Unsupported platform $(uname_s))
 endif
 
 # Set paths to external resources used by the server.
+# Note: the $\ ending used below breaks lines without inserting spaces:
+# https://www.gnu.org/software/make/manual/make.html#Splitting-Lines
 ifeq ($(DEPLOYMENT),1)
 ifeq ($(wildcard deployment),)
 $(error "No ./deployment directory found. Run ./prepare_deployment.sh")
 endif
-BIN := ./deployment/bin
-ETC := ./deployment/etc
-FIDL := ./deployment/sdk/fidl:./deployment/zircon/system/fidl
+bin := ./deployment/bin
+etc := ./deployment/etc
+fidl := ./deployment/fuchsia/sdk/fidl$\
+	:./deployment/fuchsia/zircon$\
+	:./deployment/fuchsia/zircon/system/fidl
 else
-BUILD_DIR := $(FUCHSIA_DIR)/$(file < $(FUCHSIA_DIR)/.fx-build-dir)
-RUST_BIN := $(FUCHSIA_DIR)/prebuilt/third_party/rust/$(HOST_PLATFORM)/bin
-BIN := $(BUILD_DIR)/host_x64:$(BUILD_DIR).zircon/tools:$(RUST_BIN)
-ETC := $(FUCHSIA_DIR)
-FIDL := $(FUCHSIA_DIR)/sdk/fidl:$(FUCHSIA_DIR)/zircon/system/fidl
+build_dir := $(FUCHSIA_DIR)/$(file < $(FUCHSIA_DIR)/.fx-build-dir)
+bin := $(build_dir)/host_x64$\
+	:$(build_dir).zircon/tools$\
+	:$(FUCHSIA_DIR)/prebuilt/third_party/rust/$(host_platform)/bin
+etc := $(FUCHSIA_DIR)
+fidl := $(FUCHSIA_DIR)/sdk/fidl$\
+	:$(FUCHSIA_DIR)/zircon$\
+	:$(FUCHSIA_DIR)/zircon/system/fidl
 endif
 
 # Path to the server binary.
-SERVER := backend/dist/fidlbolt
+server := backend/dist/fidlbolt
 
 .PHONY: all help frontend backend run format clean
 
@@ -45,7 +52,7 @@
 	@echo "    help       Show this help message."
 	@echo "    frontend   Build the frontend/dist bundle using webpack."
 	@echo "               For more build options: cd frontend && npm run."
-	@echo "    backend    Build the $(SERVER) server binary."
+	@echo "    backend    Build the $(server) server binary."
 	@echo "    run        Run the fidlbolt server on \$$PORT."
 	@echo "               Set VERBOSE=1 to enable verbose logging."
 	@echo "               Set DEPLOYMENT=1 to use files in ./deployment instead"
@@ -63,21 +70,21 @@
 frontend/dist: frontend/node_modules frontend/src/* frontend/src/elm/*
 	cd frontend && npm run build
 
-backend: $(SERVER)
+backend: $(server)
 
-$(SERVER): backend/go.mod backend/*.go
+$(server): backend/go.mod backend/*.go
 	cd backend && go build -o ../$@
 
 run: $(if $(wildcard frontend/dist),,frontend)
-run: $(if $(wildcard $(SERVER)),,backend)
+run: $(if $(wildcard $(server)),,backend)
 ifndef FUCHSIA_DIR
 	$(error FUCHSIA_DIR is not defined. Either pass in FUCHSIA_DIR="path" or define the variable)
 endif
-	./$(SERVER) \
+	./$(server) \
 		-static=frontend/dist \
-		-bin=$(BIN) \
-		-etc=$(ETC) \
-		-fidl=$(FIDL) \
+		-bin=$(bin) \
+		-etc=$(etc) \
+		-fidl=$(fidl) \
 		-port=$(PORT) \
 		-verbose=$(VERBOSE)
 
diff --git a/README.md b/README.md
index 672b582..31021ca 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@
 
 To set up the frontend:
 
-1. Install [Node.js][]. On Debian-based systems, use `sudo apt-get install nodejs`.
+1. Install [Node.js][] and [npm][]. On Debian-based systems, use `sudo apt-get install npm`.
 2. `cd frontend && npm ci` (using `ci` instead of `install` ensures a repeatable build).
 
 Then, use one of the commands listed in `npm run`:
@@ -65,6 +65,7 @@
 [Elm]: https://elm-lang.org/
 [Go]: https://golang.org/
 [Node.js]: https://nodejs.org/
+[npm]: https://www.npmjs.com/get-npm
 [TypeScript]: https://www.typescriptlang.org/
 [fuchsia]: https://fuchsia.dev/fuchsia-src/getting_started
 [godbolt]: https://godbolt.org/
diff --git a/backend/library.go b/backend/library.go
index a2ea51a..efad874 100644
--- a/backend/library.go
+++ b/backend/library.go
@@ -20,6 +20,9 @@
 // "library" declaration, not necessarily matching directory or file names).
 type library string
 
+// The zx library describes Zircon syscalls.
+const zxLibrary library = "zx"
+
 // A libraryMap stores information derived about a group of libraries.
 type libraryMap map[library]libraryInfo
 
@@ -319,6 +322,11 @@
 
 // possibleDirs returns directory names that lib's files might be found in.
 func (lib library) possibleDirs() []string {
+	if lib == zxLibrary {
+		// The zx library is a special case: its .fidl files are in a
+		// directory named "vsdo" (fuchsia/zircon/vdso), not "zx".
+		return []string{"vdso"}
+	}
 	return []string{string(lib), strings.ReplaceAll(string(lib), ".", "-")}
 }
 
@@ -383,10 +391,12 @@
 
 	// Although "using" can be used anywhere (e.g. as a type name), this regex
 	// is robust because it only matches imports of platform libraries (ones
-	// that start with fidl, fuchsia, or test). The only problem is it can match
-	// commented imports, so we have to check for this after matching.
+	// that start with fidl, fuchsia, or test) and the special library zx. The
+	// only problem is it can match commented imports, so we have to check for
+	// this after matching.
 	platformImportRegexp = regexp.MustCompile(`` +
 		`\busing\s+(` +
+		`zx|` +
 		`(?:fidl|fuchsia|test)` +
 		`(?:\.` + fidlIdentifierPattern + `)+` +
 		`)` +
diff --git a/backend/server.go b/backend/server.go
index d91ccbb..ebe02d3 100644
--- a/backend/server.go
+++ b/backend/server.go
@@ -40,11 +40,11 @@
 	if deploymentFile, err := findFile("fidlbolt_deployment.json", etc); err == nil {
 		deploymentBytes, err := ioutil.ReadFile(deploymentFile)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("failed to read etc/fidlbolt_deployment.json: %s", err)
 		}
 		s.deployment = new(Deployment)
 		if err = json.Unmarshal(deploymentBytes, s.deployment); err != nil {
-			return nil, err
+			return nil, fmt.Errorf("failed to parse etc/fidlbolt_deployment.json: %s", err)
 		}
 	}
 	s.fidlFormat, err = findProgram("fidl-format", bin)
diff --git a/frontend/src/index.html b/frontend/src/index.html
index 7f436c1..8c4ec53 100644
--- a/frontend/src/index.html
+++ b/frontend/src/index.html
@@ -1,5 +1,5 @@
 <!doctype html>
-<html>
+<html lang="en">
   <head>
     <meta charset="utf-8">
     <title>fidlbolt</title>
diff --git a/prepare_deployment.sh b/prepare_deployment.sh
index fcf7004..8e020a8 100755
--- a/prepare_deployment.sh
+++ b/prepare_deployment.sh
@@ -18,10 +18,9 @@
 
    This uses the following directory structure:
 
-       ./deployment/bin                   binaries (fidlc, fidl-format, etc.)
-       ./deployment/etc                   configuration files
-       ./deployment/sdk/fidl              sdk fidl libraries
-       ./deployment/zircon/system/fidl    zircon fidl libraries
+       ./deployment/bin        binaries (fidlc, fidl-format, etc.)
+       ./deployment/etc        configuration files
+       ./deployment/fuchsia    parts of the fuchsia tree that have .fidl files
 
    It preserves directories for FIDL libraries so that paths displayed in
    fidlbolt (e.g. import tooltips) resemble Fuchsia source tree paths.
@@ -69,9 +68,11 @@
     die "FUCHSIA_DIR not set"
 fi
 
+# Source fx-env.sh to get the fx tool on the PATH.
 # Source vars.sh for FUCHSIA_BUILD_DIR, ZIRCON_TOOLS_DIR, and PREBUILT_RUST_DIR.
 # (Reset shell options before sourcing since vars.sh does not expect them.)
 set +ufo pipefail
+source "$FUCHSIA_DIR/scripts/fx-env.sh" || exit $?
 source "$FUCHSIA_DIR/tools/devshell/lib/vars.sh" || exit $?
 fx-config-read
 set -ufo pipefail
@@ -82,7 +83,7 @@
 step "Cleaning ./deployment"
 cd "$(dirname "$0")"
 rm -rf deployment/
-mkdir -p deployment/{bin,etc,sdk/fidl,zircon/system/fidl}
+mkdir -p deployment/{bin,etc,fuchsia/{sdk/fidl,zircon/system/fidl}}
 
 step "Copying binaries"
 cp \
@@ -93,12 +94,14 @@
 
 step "Copying FIDL libraries"
 find "$FUCHSIA_DIR/sdk/fidl" -mindepth 1 -maxdepth 1 -type d -print0 \
-    | xargs -0 -I% cp -r % deployment/sdk/fidl
+    | xargs -0 -I% cp -r % deployment/fuchsia/sdk/fidl
 find "$FUCHSIA_DIR/zircon/system/fidl" -mindepth 1 -maxdepth 1 -type d -print0 \
-    | xargs -0 -I% cp -r % deployment/zircon/system/fidl
+    | xargs -0 -I% cp -r % deployment/fuchsia/zircon/system/fidl
+cp -r "$FUCHSIA_DIR/zircon/vdso" deployment/fuchsia/zircon/vdso
 # Remove the non-fidl files. This is easier and less error-prone than
 # manipulating paths to call mkdir -p and only copying the .fidl files.
-find deployment/{sdk,zircon} -type f -not -name '*.fidl' -delete
+find deployment/fuchsia -type f -not -name '*.fidl' -delete
+find deployment/fuchsia -type d -empty -delete
 
 step "Copying etc files"
 cp \
@@ -107,7 +110,7 @@
 
 hash() {
     # Check for uncommitted changes https://stackoverflow.com/a/3879077
-    git update-index --refresh
+    git update-index --refresh > /dev/null 2>&1
     if ! git diff-index --quiet HEAD --; then
         warn "fidlbolt_deployment.json is inaccurate due to uncommitted changes"
         warn "$(pwd): uncommitted changes:"
@@ -116,7 +119,7 @@
     if [[ -z "$(git branch -r --contains HEAD)" ]]; then
         warn "fidlbolt will have broken links due to unpublished commits"
         warn "$(pwd): HEAD has not been published:"
-        git show --stat >&2
+        git log HEAD -n1 --oneline >&2
     fi
     git rev-parse --verify HEAD
 }