build.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Build builds code as directed by json files.
  2. // We slurp in the JSON, and recursively process includes.
  3. // At the end, we issue a single gcc command for all the files.
  4. // Compilers are fast.
  5. package main
  6. import (
  7. // "fmt"
  8. "encoding/json"
  9. "io/ioutil"
  10. "log"
  11. "os"
  12. "os/exec"
  13. "path"
  14. )
  15. type build struct {
  16. // jsons is unexported so can not be set in a .json file
  17. jsons map[string]bool
  18. Name string
  19. // Projects name a whole subproject which is built independently of
  20. // this one. We'll need to be able to use environment variables at some point.
  21. Projects []string
  22. Pre []string
  23. Post []string
  24. Cflags []string
  25. Oflags []string
  26. Include []string
  27. SourceFiles []string
  28. ObjectFiles []string
  29. Libs []string
  30. Env []string
  31. // cmd's
  32. Programs []string
  33. SourceFilesCmd []string
  34. // Targets.
  35. Program string
  36. Library string
  37. }
  38. var (
  39. cwd string
  40. harvey string
  41. )
  42. func fail(err error) {
  43. if err != nil {
  44. log.Fatalf("%v", err)
  45. }
  46. }
  47. func adjust(s []string) (r []string) {
  48. for _, v := range s {
  49. if path.IsAbs(v) {
  50. v = path.Join(harvey, v)
  51. }
  52. r = append(r, v)
  53. }
  54. return
  55. }
  56. func process(f string, b *build) {
  57. if b.jsons[f] {
  58. return
  59. }
  60. log.Printf("Processing %v", f)
  61. d, err := ioutil.ReadFile(f)
  62. fail(err)
  63. var build build
  64. err = json.Unmarshal(d, &build)
  65. fail(err)
  66. b.jsons[f] = true
  67. b.SourceFiles = append(b.SourceFiles, build.SourceFiles...)
  68. b.Cflags = append(b.Cflags, build.Cflags...)
  69. b.Oflags = append(b.Oflags, build.Oflags...)
  70. b.Pre = append(b.Pre, build.Pre...)
  71. b.Post = append(b.Post, build.Post...)
  72. b.Libs = append(b.Libs, adjust(build.Libs)...)
  73. b.Projects = append(b.Projects, adjust(build.Projects)...)
  74. b.Env = append(b.Env, build.Env...)
  75. b.Programs = append(b.Programs, adjust(build.Programs)...)
  76. b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
  77. b.Program += build.Program
  78. b.Library += build.Library
  79. // For each source file, assume we create an object file with the last char replaced
  80. // with 'o'. We can get smarter later.
  81. for _, v := range build.SourceFiles {
  82. f := path.Base(v)
  83. o := f[:len(f)-1] + "o"
  84. b.ObjectFiles = append(b.ObjectFiles, o)
  85. }
  86. b.ObjectFiles = append(b.ObjectFiles, adjust(build.ObjectFiles)...)
  87. for _, v := range build.Include {
  88. wd := path.Dir(f)
  89. f := path.Join(wd, v)
  90. process(f, b)
  91. }
  92. }
  93. func compile(b *build) {
  94. // N.B. Plan 9 has a very well defined include structure, just three things:
  95. // /amd64/include, /sys/include, .
  96. // TODO: replace amd64 with an arch variable. Later.
  97. args := []string{"-c"}
  98. args = append(args, adjust([]string{"-I", "/amd64/include", "-I", "/sys/include", "-I", "."})...)
  99. args = append(args, b.Cflags...)
  100. if len(b.SourceFilesCmd) > 0 {
  101. for _, i := range b.SourceFilesCmd {
  102. log.Printf("compiling program %v\n", i)
  103. argscmd := append(args, []string{i}...)
  104. cmd := exec.Command("gcc", argscmd...)
  105. cmd.Env = append(os.Environ(), b.Env...)
  106. cmd.Stdin = os.Stdin
  107. cmd.Stderr = os.Stderr
  108. cmd.Stdout = os.Stdout
  109. log.Printf("Run %v %v", cmd.Path, cmd.Args)
  110. err := cmd.Run()
  111. if err != nil {
  112. log.Fatalf("%v\n", err)
  113. }
  114. argscmd = args
  115. }
  116. } else {
  117. args = append(args, b.SourceFiles...)
  118. cmd := exec.Command("gcc", args...)
  119. cmd.Env = append(os.Environ(), b.Env...)
  120. cmd.Stdin = os.Stdin
  121. cmd.Stderr = os.Stderr
  122. cmd.Stdout = os.Stdout
  123. log.Printf("Run %v %v", cmd.Path, cmd.Args)
  124. err := cmd.Run()
  125. if err != nil {
  126. log.Fatalf("%v\n", err)
  127. }
  128. }
  129. }
  130. func link(b *build) {
  131. if len(b.Programs) > 0 {
  132. for _, n := range b.Programs {
  133. args := []string{"-o", n}
  134. args = append(args, b.Oflags...)
  135. f := path.Base(n)
  136. o := f[:len(f)] + ".o"
  137. args = append(args, []string{o}...)
  138. args = append(args, b.Libs...)
  139. cmd := exec.Command("ld", args...)
  140. cmd.Env = append(os.Environ(), b.Env...)
  141. cmd.Stdin = os.Stdin
  142. cmd.Stderr = os.Stderr
  143. cmd.Stdout = os.Stdout
  144. log.Printf("Run %v %v", cmd.Path, cmd.Args)
  145. err := cmd.Run()
  146. if err != nil {
  147. log.Fatalf("%v\n", err)
  148. }
  149. }
  150. } else {
  151. args := []string{"-o", b.Program}
  152. args = append(args, b.Oflags...)
  153. args = append(args, b.ObjectFiles...)
  154. args = append(args, b.Libs...)
  155. cmd := exec.Command("ld", args...)
  156. cmd.Env = append(os.Environ(), b.Env...)
  157. cmd.Stdin = os.Stdin
  158. cmd.Stderr = os.Stderr
  159. cmd.Stdout = os.Stdout
  160. log.Printf("Run %v %v", cmd.Path, cmd.Args)
  161. err := cmd.Run()
  162. if err != nil {
  163. log.Fatalf("%v\n", err)
  164. }
  165. }
  166. }
  167. func run(b *build, cmd []string) {
  168. for _, v := range cmd {
  169. cmd := exec.Command("bash", "-c", v)
  170. cmd.Env = append(os.Environ(), b.Env...)
  171. cmd.Stderr = os.Stderr
  172. cmd.Stdout = os.Stdout
  173. log.Printf("Run %v %v", cmd.Path, cmd.Args)
  174. err := cmd.Run()
  175. if err != nil {
  176. log.Fatalf("%v\n", err)
  177. }
  178. }
  179. }
  180. func projects(b *build) {
  181. for _, v := range b.Projects {
  182. wd := path.Dir(v)
  183. f := path.Base(v)
  184. cwd, err := os.Getwd()
  185. fail(err)
  186. os.Chdir(wd)
  187. project(f)
  188. os.Chdir(cwd)
  189. }
  190. }
  191. // assumes we are in the wd of the project.
  192. func project(root string) {
  193. b := &build{}
  194. b.jsons = map[string]bool{}
  195. process(root, b)
  196. projects(b)
  197. run(b, b.Pre)
  198. if len(b.SourceFiles) > 0 {
  199. compile(b)
  200. }
  201. if len(b.SourceFilesCmd) > 0 {
  202. compile(b)
  203. }
  204. log.Printf("root %v program %v\n", root, b.Program)
  205. if b.Program != "" {
  206. link(b)
  207. }
  208. if b.Library != "" {
  209. //library(b)
  210. log.Printf("\n\n*** Building %v ***\n\n", b.Library)
  211. }
  212. if len(b.Programs ) > 0 {
  213. link(b)
  214. }
  215. run(b, b.Post)
  216. }
  217. func main() {
  218. var err error
  219. cwd, err = os.Getwd()
  220. fail(err)
  221. harvey = os.Getenv("HARVEY")
  222. if harvey == "" {
  223. log.Fatalf("You need to set the HARVEY environment variable")
  224. }
  225. f := path.Join(cwd, os.Args[1])
  226. project(f)
  227. }