[dev.cc] runtime: generate GOOS- and GOARCH-specific files with go generate

Eventually I'd like almost everything cmd/dist generates
to be done with 'go generate' and checked in, to simplify
the bootstrap process. The only thing cmd/dist really needs
to do is write things like the current experiment info and
the current version.

This is a first step toward that. It replaces the _NaCl etc
constants with generated ones goos_nacl, goos_darwin,
goarch_386, and so on.

LGTM=dave, austin
R=austin, dave, bradfitz
CC=golang-codereviews, iant, r
https://golang.org/cl/174290043
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index e4f307b..bfb3d15 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -615,8 +615,6 @@
 	{"anames9.c", mkanames},
 	{"zdefaultcc.go", mkzdefaultcc},
 	{"zsys_", mkzsys},
-	{"zgoarch_", mkzgoarch},
-	{"zgoos_", mkzgoos},
 	{"zversion.go", mkzversion},
 	{"zaexperiment.h", mkzexperiment},
 
@@ -1419,12 +1417,13 @@
 			xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
 	}
 
-	// remove src/runtime/z* unconditionally
+	// remove src/runtime/z* unconditionally,
+	// except leave zgoos and zgoarch, now maintained with go generate.
 	vreset(&dir);
 	bpathf(&path, "%s/src/runtime", goroot);
 	xreaddir(&dir, bstr(&path));
 	for(j=0; j<dir.len; j++) {
-		if(hasprefix(dir.p[j], "z"))
+		if(hasprefix(dir.p[j], "z") && !hasprefix(dir.p[j], "zg"))
 			xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
 	}
 
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
index e561937..38e99e1 100644
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -67,66 +67,6 @@
 	bfree(&exp);
 }
 
-// mkzgoarch writes zgoarch_$GOARCH.go:
-//
-//	package runtime
-//	const theGoarch = <goarch>
-//
-void
-mkzgoarch(char *dir, char *file)
-{
-	Buf b, out;
-
-	USED(dir);
-	
-	binit(&b);
-	binit(&out);
-	
-	bwritestr(&out, bprintf(&b,
-		"// auto generated by go tool dist\n"
-		"\n"
-		"package runtime\n"
-		"\n"
-		"const theGoarch = `%s`\n", goarch));
-
-	writefile(&out, file, 0);
-	
-	bfree(&b);
-	bfree(&out);
-}
-
-// mkzgoos writes zgoos_$GOOS.go:
-//
-//	package runtime
-//	const theGoos = <goos>
-//
-void
-mkzgoos(char *dir, char *file)
-{
-	Buf b, out;
-
-	USED(dir);
-	
-	binit(&b);
-	binit(&out);
-
-	bwritestr(&out, "// auto generated by go tool dist\n\n");
-
-	if(streq(goos, "linux")) {
-		bwritestr(&out, "// +build !android\n\n");
-	}
-	
-	bwritestr(&out, bprintf(&b,
-		"package runtime\n"
-		"\n"
-		"const theGoos = `%s`\n", goos));
-
-	writefile(&out, file, 0);
-	
-	bfree(&b);
-	bfree(&out);
-}
-
 #define MAXWINCB 2000 /* maximum number of windows callbacks allowed */
 
 // mkzsys writes zsys_$GOOS_$GOARCH.s,
diff --git a/src/runtime/arch1_386.go b/src/runtime/arch1_386.go
index 7746dfb..a73e207 100644
--- a/src/runtime/arch1_386.go
+++ b/src/runtime/arch1_386.go
@@ -9,7 +9,7 @@
 	_BigEndian        = 0
 	_CacheLineSize    = 64
 	_RuntimeGogoBytes = 64
-	_PhysPageSize     = _NaCl*65536 + (1-_NaCl)*4096 // 4k normally; 64k on NaCl
+	_PhysPageSize     = goos_nacl*65536 + (1-goos_nacl)*4096 // 4k normally; 64k on NaCl
 	_PCQuantum        = 1
 	_Int64Align       = 4
 )
diff --git a/src/runtime/arch1_amd64.go b/src/runtime/arch1_amd64.go
index 83c9c2d..794b7f6 100644
--- a/src/runtime/arch1_amd64.go
+++ b/src/runtime/arch1_amd64.go
@@ -8,7 +8,7 @@
 	thechar           = '6'
 	_BigEndian        = 0
 	_CacheLineSize    = 64
-	_RuntimeGogoBytes = 64 + (_Plan9|_Solaris|_Windows)*16
+	_RuntimeGogoBytes = 64 + (goos_plan9|goos_solaris|goos_windows)*16
 	_PhysPageSize     = 4096
 	_PCQuantum        = 1
 	_Int64Align       = 8
diff --git a/src/runtime/arch1_arm.go b/src/runtime/arch1_arm.go
index 5cb79fd..6662eae 100644
--- a/src/runtime/arch1_arm.go
+++ b/src/runtime/arch1_arm.go
@@ -9,7 +9,7 @@
 	_BigEndian        = 0
 	_CacheLineSize    = 32
 	_RuntimeGogoBytes = 60
-	_PhysPageSize     = 65536*_NaCl + 4096*(1-_NaCl)
+	_PhysPageSize     = 65536*goos_nacl + 4096*(1-goos_nacl)
 	_PCQuantum        = 4
 	_Int64Align       = 4
 )
diff --git a/src/runtime/gengoos.go b/src/runtime/gengoos.go
new file mode 100644
index 0000000..029575b
--- /dev/null
+++ b/src/runtime/gengoos.go
@@ -0,0 +1,82 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"strconv"
+	"strings"
+)
+
+var gooses, goarches []string
+
+func main() {
+	data, err := ioutil.ReadFile("../go/build/syslist.go")
+	if err != nil {
+		log.Fatal(err)
+	}
+	const (
+		goosPrefix   = `const goosList = `
+		goarchPrefix = `const goarchList = `
+	)
+	for _, line := range strings.Split(string(data), "\n") {
+		if strings.HasPrefix(line, goosPrefix) {
+			text, err := strconv.Unquote(strings.TrimPrefix(line, goosPrefix))
+			if err != nil {
+				log.Fatalf("parsing goosList %#q: %v", strings.TrimPrefix(line, goosPrefix), err)
+			}
+			gooses = strings.Fields(text)
+		}
+		if strings.HasPrefix(line, goarchPrefix) {
+			text, err := strconv.Unquote(strings.TrimPrefix(line, goarchPrefix))
+			if err != nil {
+				log.Fatal("parsing goarchList: %v", err)
+			}
+			goarches = strings.Fields(text)
+		}
+	}
+
+	for _, target := range gooses {
+		var buf bytes.Buffer
+		fmt.Fprintf(&buf, "// generated by gengoos.go using 'go generate'\n\n")
+		fmt.Fprintf(&buf, "// +build %s\n\n", target) // usually redundant, but not always; see linux vs android
+		fmt.Fprintf(&buf, "package runtime\n\n")
+		fmt.Fprintf(&buf, "const theGoos = `%s`\n\n", target)
+		for _, goos := range gooses {
+			value := 0
+			if goos == target {
+				value = 1
+			}
+			fmt.Fprintf(&buf, "const goos_%s = %d\n", goos, value)
+		}
+		err := ioutil.WriteFile("zgoos_"+target+".go", buf.Bytes(), 0666)
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	for _, target := range goarches {
+		var buf bytes.Buffer
+		fmt.Fprintf(&buf, "// generated by gengoos.go using 'go generate'\n\n")
+		fmt.Fprintf(&buf, "package runtime\n\n")
+		fmt.Fprintf(&buf, "const theGoarch = `%s`\n\n", target)
+		for _, goarch := range goarches {
+			value := 0
+			if goarch == target {
+				value = 1
+			}
+			fmt.Fprintf(&buf, "const goarch_%s = %d\n", goarch, value)
+		}
+		err := ioutil.WriteFile("zgoarch_"+target+".go", buf.Bytes(), 0666)
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+}
diff --git a/src/runtime/malloc2.go b/src/runtime/malloc2.go
index e4bd963..c175c2a 100644
--- a/src/runtime/malloc2.go
+++ b/src/runtime/malloc2.go
@@ -126,7 +126,7 @@
 	// See http://golang.org/issue/5402 and http://golang.org/issue/5236.
 	// On other 64-bit platforms, we limit the arena to 128GB, or 37 bits.
 	// On 32-bit, we don't bother limiting anything, so we use the full 32-bit address.
-	_MHeapMap_TotalBits = (_64bit*_Windows)*35 + (_64bit*(1-_Windows))*37 + (1-_64bit)*32
+	_MHeapMap_TotalBits = (_64bit*goos_windows)*35 + (_64bit*(1-goos_windows))*37 + (1-_64bit)*32
 	_MHeapMap_Bits      = _MHeapMap_TotalBits - _PageShift
 
 	_MaxMem = uintptr(1<<_MHeapMap_TotalBits - 1)
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index c999b30..7987a73 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -45,7 +45,13 @@
 	_Pdead
 )
 
-// XXX inserting below here
+// The next line makes 'go generate' write the zgen_*.go files with
+// per-OS and per-arch information, including constants
+// named goos_$GOOS and goarch_$GOARCH for every
+// known GOOS and GOARCH. The constant is 1 on the
+// current system, 0 otherwise; multiplying by them is
+// useful for defining GOOS- or GOARCH-specific constants.
+//go:generate go run gengoos.go
 
 type mutex struct {
 	// Futex-based impl treats it as uint32 key,
@@ -395,14 +401,6 @@
 	fun    [0]uintptr
 }
 
-const (
-	// TODO: Generate in cmd/dist.
-	_NaCl    = 0
-	_Windows = 0
-	_Solaris = 0
-	_Plan9   = 0
-)
-
 // Lock-free stack node.
 type lfnode struct {
 	next    *lfnode
diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index 40dfc76..ad83e58 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -775,7 +775,7 @@
 	}
 
 	/* TODO
-	if _Windows && gp.m != nil && gp.m.libcallsp != 0 {
+	if goos_windows && gp.m != nil && gp.m.libcallsp != 0 {
 		return
 	}
 	*/
diff --git a/src/runtime/stack2.go b/src/runtime/stack2.go
index c3718c2..e50b32c 100644
--- a/src/runtime/stack2.go
+++ b/src/runtime/stack2.go
@@ -59,7 +59,7 @@
 	// to each stack below the usual guard area for OS-specific
 	// purposes like signal handling. Used on Windows and on
 	// Plan 9 because they do not use a separate stack.
-	_StackSystem = _Windows*512*ptrSize + _Plan9*512
+	_StackSystem = goos_windows*512*ptrSize + goos_plan9*512
 
 	// The minimum size of stack used by Go code
 	_StackMin = 2048
diff --git a/src/runtime/zgoarch_386.go b/src/runtime/zgoarch_386.go
new file mode 100644
index 0000000..057a746
--- /dev/null
+++ b/src/runtime/zgoarch_386.go
@@ -0,0 +1,12 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `386`
+
+const goarch_386 = 1
+const goarch_amd64 = 0
+const goarch_amd64p32 = 0
+const goarch_arm = 0
+const goarch_power64 = 0
+const goarch_power64le = 0
diff --git a/src/runtime/zgoarch_amd64.go b/src/runtime/zgoarch_amd64.go
new file mode 100644
index 0000000..a712407
--- /dev/null
+++ b/src/runtime/zgoarch_amd64.go
@@ -0,0 +1,12 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `amd64`
+
+const goarch_386 = 0
+const goarch_amd64 = 1
+const goarch_amd64p32 = 0
+const goarch_arm = 0
+const goarch_power64 = 0
+const goarch_power64le = 0
diff --git a/src/runtime/zgoarch_amd64p32.go b/src/runtime/zgoarch_amd64p32.go
new file mode 100644
index 0000000..2b6a142
--- /dev/null
+++ b/src/runtime/zgoarch_amd64p32.go
@@ -0,0 +1,12 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `amd64p32`
+
+const goarch_386 = 0
+const goarch_amd64 = 0
+const goarch_amd64p32 = 1
+const goarch_arm = 0
+const goarch_power64 = 0
+const goarch_power64le = 0
diff --git a/src/runtime/zgoarch_arm.go b/src/runtime/zgoarch_arm.go
new file mode 100644
index 0000000..4030210
--- /dev/null
+++ b/src/runtime/zgoarch_arm.go
@@ -0,0 +1,12 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `arm`
+
+const goarch_386 = 0
+const goarch_amd64 = 0
+const goarch_amd64p32 = 0
+const goarch_arm = 1
+const goarch_power64 = 0
+const goarch_power64le = 0
diff --git a/src/runtime/zgoarch_power64.go b/src/runtime/zgoarch_power64.go
new file mode 100644
index 0000000..cc361f0
--- /dev/null
+++ b/src/runtime/zgoarch_power64.go
@@ -0,0 +1,12 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `power64`
+
+const goarch_386 = 0
+const goarch_amd64 = 0
+const goarch_amd64p32 = 0
+const goarch_arm = 0
+const goarch_power64 = 1
+const goarch_power64le = 0
diff --git a/src/runtime/zgoarch_power64le.go b/src/runtime/zgoarch_power64le.go
new file mode 100644
index 0000000..41294e6
--- /dev/null
+++ b/src/runtime/zgoarch_power64le.go
@@ -0,0 +1,12 @@
+// generated by gengoos.go using 'go generate'
+
+package runtime
+
+const theGoarch = `power64le`
+
+const goarch_386 = 0
+const goarch_amd64 = 0
+const goarch_amd64p32 = 0
+const goarch_arm = 0
+const goarch_power64 = 0
+const goarch_power64le = 1
diff --git a/src/runtime/zgoos_android.go b/src/runtime/zgoos_android.go
new file mode 100644
index 0000000..abfba80
--- /dev/null
+++ b/src/runtime/zgoos_android.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build android
+
+package runtime
+
+const theGoos = `android`
+
+const goos_android = 1
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_darwin.go b/src/runtime/zgoos_darwin.go
new file mode 100644
index 0000000..eb39b53
--- /dev/null
+++ b/src/runtime/zgoos_darwin.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build darwin
+
+package runtime
+
+const theGoos = `darwin`
+
+const goos_android = 0
+const goos_darwin = 1
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_dragonfly.go b/src/runtime/zgoos_dragonfly.go
new file mode 100644
index 0000000..f6e839d
--- /dev/null
+++ b/src/runtime/zgoos_dragonfly.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build dragonfly
+
+package runtime
+
+const theGoos = `dragonfly`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 1
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_freebsd.go b/src/runtime/zgoos_freebsd.go
new file mode 100644
index 0000000..3c47aef
--- /dev/null
+++ b/src/runtime/zgoos_freebsd.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build freebsd
+
+package runtime
+
+const theGoos = `freebsd`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 1
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_linux.go b/src/runtime/zgoos_linux.go
new file mode 100644
index 0000000..5d899e3
--- /dev/null
+++ b/src/runtime/zgoos_linux.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build linux
+
+package runtime
+
+const theGoos = `linux`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 1
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_nacl.go b/src/runtime/zgoos_nacl.go
new file mode 100644
index 0000000..b5c4281
--- /dev/null
+++ b/src/runtime/zgoos_nacl.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build nacl
+
+package runtime
+
+const theGoos = `nacl`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 1
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_netbsd.go b/src/runtime/zgoos_netbsd.go
new file mode 100644
index 0000000..b2e4522
--- /dev/null
+++ b/src/runtime/zgoos_netbsd.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build netbsd
+
+package runtime
+
+const theGoos = `netbsd`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 1
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_openbsd.go b/src/runtime/zgoos_openbsd.go
new file mode 100644
index 0000000..331c96d
--- /dev/null
+++ b/src/runtime/zgoos_openbsd.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build openbsd
+
+package runtime
+
+const theGoos = `openbsd`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 1
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_plan9.go b/src/runtime/zgoos_plan9.go
new file mode 100644
index 0000000..f29eb45
--- /dev/null
+++ b/src/runtime/zgoos_plan9.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build plan9
+
+package runtime
+
+const theGoos = `plan9`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 1
+const goos_solaris = 0
+const goos_windows = 0
diff --git a/src/runtime/zgoos_solaris.go b/src/runtime/zgoos_solaris.go
new file mode 100644
index 0000000..ac613db
--- /dev/null
+++ b/src/runtime/zgoos_solaris.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build solaris
+
+package runtime
+
+const theGoos = `solaris`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 1
+const goos_windows = 0
diff --git a/src/runtime/zgoos_windows.go b/src/runtime/zgoos_windows.go
new file mode 100644
index 0000000..43710d8
--- /dev/null
+++ b/src/runtime/zgoos_windows.go
@@ -0,0 +1,19 @@
+// generated by gengoos.go using 'go generate'
+
+// +build windows
+
+package runtime
+
+const theGoos = `windows`
+
+const goos_android = 0
+const goos_darwin = 0
+const goos_dragonfly = 0
+const goos_freebsd = 0
+const goos_linux = 0
+const goos_nacl = 0
+const goos_netbsd = 0
+const goos_openbsd = 0
+const goos_plan9 = 0
+const goos_solaris = 0
+const goos_windows = 1