A lightweight mechanism to provide an *instant kickstart* to a Go web server instance upon changing any Go source files under the project directory (and its subdirectories).

mkall.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // Copyright 2017 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // linux/mkall.go - Generates all Linux zsysnum, zsyscall, zerror, and ztype
  5. // files for all 11 linux architectures supported by the go compiler. See
  6. // README.md for more information about the build system.
  7. // To run it you must have a git checkout of the Linux kernel and glibc. Once
  8. // the appropriate sources are ready, the program is run as:
  9. // go run linux/mkall.go <linux_dir> <glibc_dir>
  10. // +build ignore
  11. package main
  12. import (
  13. "fmt"
  14. "os"
  15. "os/exec"
  16. "path/filepath"
  17. "runtime"
  18. "strings"
  19. )
  20. // These will be paths to the appropriate source directories.
  21. var LinuxDir string
  22. var GlibcDir string
  23. const TempDir = "/tmp"
  24. const IncludeDir = TempDir + "/include" // To hold our C headers
  25. const BuildDir = TempDir + "/build" // To hold intermediate build files
  26. const GOOS = "linux" // Only for Linux targets
  27. const BuildArch = "amd64" // Must be built on this architecture
  28. const MinKernel = "2.6.23" // https://golang.org/doc/install#requirements
  29. type target struct {
  30. GoArch string // Architecture name according to Go
  31. LinuxArch string // Architecture name according to the Linux Kernel
  32. GNUArch string // Architecture name according to GNU tools (https://wiki.debian.org/Multiarch/Tuples)
  33. BigEndian bool // Default Little Endian
  34. SignedChar bool // Is -fsigned-char needed (default no)
  35. Bits int
  36. }
  37. // List of the 11 Linux targets supported by the go compiler. sparc64 is not
  38. // currently supported, though a port is in progress.
  39. var targets = []target{
  40. {
  41. GoArch: "386",
  42. LinuxArch: "x86",
  43. GNUArch: "i686-linux-gnu", // Note "i686" not "i386"
  44. Bits: 32,
  45. },
  46. {
  47. GoArch: "amd64",
  48. LinuxArch: "x86",
  49. GNUArch: "x86_64-linux-gnu",
  50. Bits: 64,
  51. },
  52. {
  53. GoArch: "arm64",
  54. LinuxArch: "arm64",
  55. GNUArch: "aarch64-linux-gnu",
  56. SignedChar: true,
  57. Bits: 64,
  58. },
  59. {
  60. GoArch: "arm",
  61. LinuxArch: "arm",
  62. GNUArch: "arm-linux-gnueabi",
  63. Bits: 32,
  64. },
  65. {
  66. GoArch: "mips",
  67. LinuxArch: "mips",
  68. GNUArch: "mips-linux-gnu",
  69. BigEndian: true,
  70. Bits: 32,
  71. },
  72. {
  73. GoArch: "mipsle",
  74. LinuxArch: "mips",
  75. GNUArch: "mipsel-linux-gnu",
  76. Bits: 32,
  77. },
  78. {
  79. GoArch: "mips64",
  80. LinuxArch: "mips",
  81. GNUArch: "mips64-linux-gnuabi64",
  82. BigEndian: true,
  83. Bits: 64,
  84. },
  85. {
  86. GoArch: "mips64le",
  87. LinuxArch: "mips",
  88. GNUArch: "mips64el-linux-gnuabi64",
  89. Bits: 64,
  90. },
  91. {
  92. GoArch: "ppc64",
  93. LinuxArch: "powerpc",
  94. GNUArch: "powerpc64-linux-gnu",
  95. BigEndian: true,
  96. Bits: 64,
  97. },
  98. {
  99. GoArch: "ppc64le",
  100. LinuxArch: "powerpc",
  101. GNUArch: "powerpc64le-linux-gnu",
  102. Bits: 64,
  103. },
  104. {
  105. GoArch: "s390x",
  106. LinuxArch: "s390",
  107. GNUArch: "s390x-linux-gnu",
  108. BigEndian: true,
  109. SignedChar: true,
  110. Bits: 64,
  111. },
  112. // {
  113. // GoArch: "sparc64",
  114. // LinuxArch: "sparc",
  115. // GNUArch: "sparc64-linux-gnu",
  116. // BigEndian: true,
  117. // Bits: 64,
  118. // },
  119. }
  120. func main() {
  121. if runtime.GOOS != GOOS || runtime.GOARCH != BuildArch {
  122. fmt.Printf("Build system has GOOS_GOARCH = %s_%s, need %s_%s\n",
  123. runtime.GOOS, runtime.GOARCH, GOOS, BuildArch)
  124. return
  125. }
  126. // Check that we are using the new build system if we should
  127. if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
  128. fmt.Println("In the new build system, mkall.go should not be called directly.")
  129. fmt.Println("See README.md")
  130. return
  131. }
  132. // Parse the command line options
  133. if len(os.Args) != 3 {
  134. fmt.Println("USAGE: go run linux/mkall.go <linux_dir> <glibc_dir>")
  135. return
  136. }
  137. LinuxDir = os.Args[1]
  138. GlibcDir = os.Args[2]
  139. for _, t := range targets {
  140. fmt.Printf("----- GENERATING: %s -----\n", t.GoArch)
  141. if err := t.generateFiles(); err != nil {
  142. fmt.Printf("%v\n***** FAILURE: %s *****\n\n", err, t.GoArch)
  143. } else {
  144. fmt.Printf("----- SUCCESS: %s -----\n\n", t.GoArch)
  145. }
  146. }
  147. }
  148. // Makes an exec.Cmd with Stderr attached to os.Stderr
  149. func makeCommand(name string, args ...string) *exec.Cmd {
  150. cmd := exec.Command(name, args...)
  151. cmd.Stderr = os.Stderr
  152. return cmd
  153. }
  154. // Runs the command, pipes output to a formatter, pipes that to an output file.
  155. func (t *target) commandFormatOutput(formatter string, outputFile string,
  156. name string, args ...string) (err error) {
  157. mainCmd := makeCommand(name, args...)
  158. fmtCmd := makeCommand(formatter)
  159. if formatter == "mkpost" {
  160. fmtCmd = makeCommand("go", "run", "mkpost.go")
  161. // Set GOARCH_TARGET so mkpost knows what GOARCH is..
  162. fmtCmd.Env = append(os.Environ(), "GOARCH_TARGET="+t.GoArch)
  163. // Set GOARCH to host arch for mkpost, so it can run natively.
  164. for i, s := range fmtCmd.Env {
  165. if strings.HasPrefix(s, "GOARCH=") {
  166. fmtCmd.Env[i] = "GOARCH=" + BuildArch
  167. }
  168. }
  169. }
  170. // mainCmd | fmtCmd > outputFile
  171. if fmtCmd.Stdin, err = mainCmd.StdoutPipe(); err != nil {
  172. return
  173. }
  174. if fmtCmd.Stdout, err = os.Create(outputFile); err != nil {
  175. return
  176. }
  177. // Make sure the formatter eventually closes
  178. if err = fmtCmd.Start(); err != nil {
  179. return
  180. }
  181. defer func() {
  182. fmtErr := fmtCmd.Wait()
  183. if err == nil {
  184. err = fmtErr
  185. }
  186. }()
  187. return mainCmd.Run()
  188. }
  189. // Generates all the files for a Linux target
  190. func (t *target) generateFiles() error {
  191. // Setup environment variables
  192. os.Setenv("GOOS", GOOS)
  193. os.Setenv("GOARCH", t.GoArch)
  194. // Get appropriate compiler and emulator (unless on x86)
  195. if t.LinuxArch != "x86" {
  196. // Check/Setup cross compiler
  197. compiler := t.GNUArch + "-gcc"
  198. if _, err := exec.LookPath(compiler); err != nil {
  199. return err
  200. }
  201. os.Setenv("CC", compiler)
  202. // Check/Setup emulator (usually first component of GNUArch)
  203. qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")]
  204. if t.LinuxArch == "powerpc" {
  205. qemuArchName = t.GoArch
  206. }
  207. os.Setenv("GORUN", "qemu-"+qemuArchName)
  208. } else {
  209. os.Setenv("CC", "gcc")
  210. }
  211. // Make the include directory and fill it with headers
  212. if err := os.MkdirAll(IncludeDir, os.ModePerm); err != nil {
  213. return err
  214. }
  215. defer os.RemoveAll(IncludeDir)
  216. if err := t.makeHeaders(); err != nil {
  217. return fmt.Errorf("could not make header files: %v", err)
  218. }
  219. fmt.Println("header files generated")
  220. // Make each of the four files
  221. if err := t.makeZSysnumFile(); err != nil {
  222. return fmt.Errorf("could not make zsysnum file: %v", err)
  223. }
  224. fmt.Println("zsysnum file generated")
  225. if err := t.makeZSyscallFile(); err != nil {
  226. return fmt.Errorf("could not make zsyscall file: %v", err)
  227. }
  228. fmt.Println("zsyscall file generated")
  229. if err := t.makeZTypesFile(); err != nil {
  230. return fmt.Errorf("could not make ztypes file: %v", err)
  231. }
  232. fmt.Println("ztypes file generated")
  233. if err := t.makeZErrorsFile(); err != nil {
  234. return fmt.Errorf("could not make zerrors file: %v", err)
  235. }
  236. fmt.Println("zerrors file generated")
  237. return nil
  238. }
  239. // Create the Linux and glibc headers in the include directory.
  240. func (t *target) makeHeaders() error {
  241. // Make the Linux headers we need for this architecture
  242. linuxMake := makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+TempDir)
  243. linuxMake.Dir = LinuxDir
  244. if err := linuxMake.Run(); err != nil {
  245. return err
  246. }
  247. // A Temporary build directory for glibc
  248. if err := os.MkdirAll(BuildDir, os.ModePerm); err != nil {
  249. return err
  250. }
  251. defer os.RemoveAll(BuildDir)
  252. // Make the glibc headers we need for this architecture
  253. confScript := filepath.Join(GlibcDir, "configure")
  254. glibcConf := makeCommand(confScript, "--prefix="+TempDir, "--host="+t.GNUArch, "--enable-kernel="+MinKernel)
  255. glibcConf.Dir = BuildDir
  256. if err := glibcConf.Run(); err != nil {
  257. return err
  258. }
  259. glibcMake := makeCommand("make", "install-headers")
  260. glibcMake.Dir = BuildDir
  261. if err := glibcMake.Run(); err != nil {
  262. return err
  263. }
  264. // We only need an empty stubs file
  265. stubsFile := filepath.Join(IncludeDir, "gnu/stubs.h")
  266. if file, err := os.Create(stubsFile); err != nil {
  267. return err
  268. } else {
  269. file.Close()
  270. }
  271. return nil
  272. }
  273. // makes the zsysnum_linux_$GOARCH.go file
  274. func (t *target) makeZSysnumFile() error {
  275. zsysnumFile := fmt.Sprintf("zsysnum_linux_%s.go", t.GoArch)
  276. unistdFile := filepath.Join(IncludeDir, "asm/unistd.h")
  277. args := append(t.cFlags(), unistdFile)
  278. return t.commandFormatOutput("gofmt", zsysnumFile, "linux/mksysnum.pl", args...)
  279. }
  280. // makes the zsyscall_linux_$GOARCH.go file
  281. func (t *target) makeZSyscallFile() error {
  282. zsyscallFile := fmt.Sprintf("zsyscall_linux_%s.go", t.GoArch)
  283. // Find the correct architecture syscall file (might end with x.go)
  284. archSyscallFile := fmt.Sprintf("syscall_linux_%s.go", t.GoArch)
  285. if _, err := os.Stat(archSyscallFile); os.IsNotExist(err) {
  286. shortArch := strings.TrimSuffix(t.GoArch, "le")
  287. archSyscallFile = fmt.Sprintf("syscall_linux_%sx.go", shortArch)
  288. }
  289. args := append(t.mksyscallFlags(), "-tags", "linux,"+t.GoArch,
  290. "syscall_linux.go", archSyscallFile)
  291. return t.commandFormatOutput("gofmt", zsyscallFile, "./mksyscall.pl", args...)
  292. }
  293. // makes the zerrors_linux_$GOARCH.go file
  294. func (t *target) makeZErrorsFile() error {
  295. zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch)
  296. return t.commandFormatOutput("gofmt", zerrorsFile, "./mkerrors.sh", t.cFlags()...)
  297. }
  298. // makes the ztypes_linux_$GOARCH.go file
  299. func (t *target) makeZTypesFile() error {
  300. ztypesFile := fmt.Sprintf("ztypes_linux_%s.go", t.GoArch)
  301. args := []string{"tool", "cgo", "-godefs", "--"}
  302. args = append(args, t.cFlags()...)
  303. args = append(args, "linux/types.go")
  304. return t.commandFormatOutput("mkpost", ztypesFile, "go", args...)
  305. }
  306. // Flags that should be given to gcc and cgo for this target
  307. func (t *target) cFlags() []string {
  308. // Compile statically to avoid cross-architecture dynamic linking.
  309. flags := []string{"-Wall", "-Werror", "-static", "-I" + IncludeDir}
  310. // Architecture-specific flags
  311. if t.SignedChar {
  312. flags = append(flags, "-fsigned-char")
  313. }
  314. if t.LinuxArch == "x86" {
  315. flags = append(flags, fmt.Sprintf("-m%d", t.Bits))
  316. }
  317. return flags
  318. }
  319. // Flags that should be given to mksyscall for this target
  320. func (t *target) mksyscallFlags() (flags []string) {
  321. if t.Bits == 32 {
  322. if t.BigEndian {
  323. flags = append(flags, "-b32")
  324. } else {
  325. flags = append(flags, "-l32")
  326. }
  327. }
  328. // This flag menas a 64-bit value should use (even, odd)-pair.
  329. if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) {
  330. flags = append(flags, "-arm")
  331. }
  332. return
  333. }