build.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  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. "bytes"
  8. "debug/elf"
  9. "encoding/json"
  10. "fmt"
  11. "io/ioutil"
  12. "log"
  13. "os"
  14. "os/exec"
  15. "path"
  16. "path/filepath"
  17. "text/template"
  18. )
  19. type kernconfig struct {
  20. Code []string
  21. Dev []string
  22. Ip []string
  23. Link []string
  24. Sd []string
  25. Uart []string
  26. }
  27. type kernel struct {
  28. Systab string
  29. Config kernconfig
  30. Ramfiles map[string]string
  31. }
  32. type build struct {
  33. // jsons is unexported so can not be set in a .json file
  34. jsons map[string]bool
  35. path string
  36. Name string
  37. // Projects name a whole subproject which is built independently of
  38. // this one. We'll need to be able to use environment variables at some point.
  39. Projects []string
  40. Pre []string
  41. Post []string
  42. Cflags []string
  43. Oflags []string
  44. Include []string
  45. SourceFiles []string
  46. ObjectFiles []string
  47. Libs []string
  48. Env []string
  49. // cmd's
  50. SourceFilesCmd []string
  51. // Targets.
  52. Program string
  53. Library string
  54. Install string // where to place the resulting binary/lib
  55. Kernel *kernel
  56. }
  57. var (
  58. cwd string
  59. harvey string
  60. toolprefix string
  61. )
  62. func fail(err error) {
  63. if err != nil {
  64. log.Fatalf("%v", err)
  65. }
  66. }
  67. func adjust1(s string) string {
  68. if path.IsAbs(s) {
  69. return path.Join(harvey, s)
  70. }
  71. return s
  72. }
  73. func adjust(s []string) (r []string) {
  74. for _, v := range s {
  75. r = append(r, adjust1(v))
  76. }
  77. return
  78. }
  79. func process(f string, b *build) {
  80. if b.jsons[f] {
  81. return
  82. }
  83. log.Printf("Processing %v", f)
  84. d, err := ioutil.ReadFile(f)
  85. fail(err)
  86. var build build
  87. err = json.Unmarshal(d, &build)
  88. fail(err)
  89. b.jsons[f] = true
  90. if len(b.jsons) == 1 {
  91. cwd, err := os.Getwd()
  92. if err != nil {
  93. log.Fatalf("%v", err)
  94. }
  95. b.path = path.Join(cwd, f)
  96. b.Name = build.Name
  97. b.Kernel = build.Kernel
  98. }
  99. b.SourceFiles = append(b.SourceFiles, build.SourceFiles...)
  100. b.Cflags = append(b.Cflags, build.Cflags...)
  101. b.Oflags = append(b.Oflags, build.Oflags...)
  102. b.Pre = append(b.Pre, build.Pre...)
  103. b.Post = append(b.Post, build.Post...)
  104. b.Libs = append(b.Libs, adjust(build.Libs)...)
  105. b.Projects = append(b.Projects, adjust(build.Projects)...)
  106. b.Env = append(b.Env, build.Env...)
  107. b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
  108. b.Program += build.Program
  109. b.Library += build.Library
  110. b.Install += build.Install
  111. // For each source file, assume we create an object file with the last char replaced
  112. // with 'o'. We can get smarter later.
  113. for _, v := range build.SourceFiles {
  114. f := path.Base(v)
  115. o := f[:len(f)-1] + "o"
  116. b.ObjectFiles = append(b.ObjectFiles, o)
  117. }
  118. b.ObjectFiles = append(b.ObjectFiles, adjust(build.ObjectFiles)...)
  119. includes := adjust(build.Include)
  120. for _, v := range includes {
  121. if !path.IsAbs(v) {
  122. wd := path.Dir(f)
  123. v = path.Join(wd, v)
  124. }
  125. process(v, b)
  126. }
  127. }
  128. func compile(b *build) {
  129. // N.B. Plan 9 has a very well defined include structure, just three things:
  130. // /amd64/include, /sys/include, .
  131. // TODO: replace amd64 with an arch variable. Later.
  132. args := []string{"-c"}
  133. args = append(args, adjust([]string{"-I", "/amd64/include", "-I", "/sys/include", "-I", "."})...)
  134. args = append(args, b.Cflags...)
  135. if len(b.SourceFilesCmd) > 0 {
  136. for _, i := range b.SourceFilesCmd {
  137. argscmd := append(args, []string{i}...)
  138. cmd := exec.Command(toolprefix+"gcc", argscmd...)
  139. cmd.Env = append(os.Environ(), b.Env...)
  140. cmd.Stdin = os.Stdin
  141. cmd.Stderr = os.Stderr
  142. cmd.Stdout = os.Stdout
  143. log.Printf("%v", cmd.Args)
  144. err := cmd.Run()
  145. if err != nil {
  146. log.Fatalf("%v\n", err)
  147. }
  148. argscmd = args
  149. }
  150. } else {
  151. args = append(args, b.SourceFiles...)
  152. cmd := exec.Command(toolprefix+"gcc", args...)
  153. cmd.Env = append(os.Environ(), b.Env...)
  154. cmd.Stdin = os.Stdin
  155. cmd.Stderr = os.Stderr
  156. cmd.Stdout = os.Stdout
  157. log.Printf("%v", cmd.Args)
  158. err := cmd.Run()
  159. if err != nil {
  160. log.Fatalf("%v\n", err)
  161. }
  162. }
  163. }
  164. func link(b *build) {
  165. if len(b.SourceFilesCmd) > 0 {
  166. for _, n := range b.SourceFilesCmd {
  167. // Split off the last element of the file
  168. var ext = filepath.Ext(n)
  169. if len(ext) == 0 {
  170. log.Fatalf("refusing to overwrite extension-less source file %v", n)
  171. continue
  172. }
  173. n = n[0 : len(n)-len(ext)]
  174. args := []string{"-o", n}
  175. f := path.Base(n)
  176. o := f[:len(f)] + ".o"
  177. args = append(args, []string{o}...)
  178. args = append(args, b.Oflags...)
  179. args = append(args, adjust([]string{"-L", "/amd64/lib"})...)
  180. args = append(args, b.Libs...)
  181. cmd := exec.Command(toolprefix+"ld", args...)
  182. cmd.Env = append(os.Environ(), b.Env...)
  183. cmd.Stdin = os.Stdin
  184. cmd.Stderr = os.Stderr
  185. cmd.Stdout = os.Stdout
  186. log.Printf("%v", cmd.Args)
  187. err := cmd.Run()
  188. if err != nil {
  189. log.Fatalf("%v\n", err)
  190. }
  191. }
  192. } else {
  193. args := []string{"-o", b.Program}
  194. args = append(args, b.ObjectFiles...)
  195. args = append(args, b.Oflags...)
  196. args = append(args, adjust([]string{"-L", "/amd64/lib"})...)
  197. args = append(args, b.Libs...)
  198. cmd := exec.Command(toolprefix+"ld", args...)
  199. cmd.Env = append(os.Environ(), b.Env...)
  200. cmd.Stdin = os.Stdin
  201. cmd.Stderr = os.Stderr
  202. cmd.Stdout = os.Stdout
  203. log.Printf("%v", cmd.Args)
  204. err := cmd.Run()
  205. if err != nil {
  206. log.Fatalf("%v\n", err)
  207. }
  208. }
  209. }
  210. func install(b *build) {
  211. if b.Install == "" {
  212. return
  213. }
  214. installpath := adjust([]string{os.ExpandEnv(b.Install)})
  215. // Make sure they're all there.
  216. for _, v := range installpath {
  217. if err := os.MkdirAll(v, 0755); err != nil {
  218. log.Fatalf("%v", err)
  219. }
  220. }
  221. if len(b.SourceFilesCmd) > 0 {
  222. for _, n := range b.SourceFilesCmd {
  223. // Split off the last element of the file
  224. var ext = filepath.Ext(n)
  225. if len(ext) == 0 {
  226. log.Fatalf("refusing to overwrite extension-less source file %v", n)
  227. continue
  228. }
  229. n = n[0 : len(n)-len(ext)]
  230. args := []string{n}
  231. args = append(args, installpath...)
  232. cmd := exec.Command("mv", args...)
  233. cmd.Stdin = os.Stdin
  234. cmd.Stderr = os.Stderr
  235. cmd.Stdout = os.Stdout
  236. log.Printf("%v", cmd.Args)
  237. err := cmd.Run()
  238. if err != nil {
  239. log.Fatalf("%v\n", err)
  240. }
  241. }
  242. } else if len(b.Program) > 0 {
  243. args := []string{b.Program}
  244. args = append(args, installpath...)
  245. cmd := exec.Command("mv", args...)
  246. cmd.Stdin = os.Stdin
  247. cmd.Stderr = os.Stderr
  248. cmd.Stdout = os.Stdout
  249. log.Printf("%v", cmd.Args)
  250. err := cmd.Run()
  251. if err != nil {
  252. log.Fatalf("%v\n", err)
  253. }
  254. } else if len(b.Library) > 0 {
  255. args := []string{"-rvs"}
  256. libpath := installpath[0] + "/" + b.Library
  257. args = append(args, libpath)
  258. for _, n := range b.SourceFiles {
  259. // All .o files end up in the top-level directory
  260. n = filepath.Base(n)
  261. // Split off the last element of the file
  262. var ext = filepath.Ext(n)
  263. if len(ext) == 0 {
  264. log.Fatalf("confused by extension-less file %v", n)
  265. continue
  266. }
  267. n = n[0 : len(n)-len(ext)]
  268. n = n + ".o"
  269. args = append(args, n)
  270. }
  271. cmd := exec.Command(toolprefix+"ar", args...)
  272. cmd.Stdin = os.Stdin
  273. cmd.Stderr = os.Stderr
  274. cmd.Stdout = os.Stdout
  275. log.Printf("*** Installing %v ***", b.Library)
  276. log.Printf("%v", cmd.Args)
  277. err := cmd.Run()
  278. if err != nil {
  279. log.Fatalf("%v\n", err)
  280. }
  281. cmd = exec.Command(toolprefix+"ranlib", libpath)
  282. err = cmd.Run()
  283. if err != nil {
  284. log.Fatalf("%v\n", err)
  285. }
  286. }
  287. }
  288. func run(b *build, cmd []string) {
  289. for _, v := range cmd {
  290. cmd := exec.Command("bash", "-c", v)
  291. cmd.Env = append(os.Environ(), b.Env...)
  292. cmd.Stderr = os.Stderr
  293. cmd.Stdout = os.Stdout
  294. log.Printf("%v", cmd.Args)
  295. err := cmd.Run()
  296. if err != nil {
  297. log.Fatalf("%v\n", err)
  298. }
  299. }
  300. }
  301. func projects(b *build) {
  302. for _, v := range b.Projects {
  303. wd := path.Dir(v)
  304. f := path.Base(v)
  305. cwd, err := os.Getwd()
  306. fail(err)
  307. os.Chdir(wd)
  308. project(f)
  309. os.Chdir(cwd)
  310. }
  311. }
  312. func data2c(name string, path string) (string, error) {
  313. var out []byte
  314. var in []byte
  315. if elf, err := elf.Open(path); err == nil {
  316. elf.Close()
  317. cwd, err := os.Getwd()
  318. tmpf, err := ioutil.TempFile(cwd, name)
  319. if err != nil {
  320. log.Fatalf("%v\n", err)
  321. }
  322. args := []string{"-o", tmpf.Name(), path}
  323. cmd := exec.Command(toolprefix+"strip", args...)
  324. cmd.Env = nil
  325. cmd.Stdin = os.Stdin
  326. cmd.Stderr = os.Stderr
  327. cmd.Stdout = os.Stdout
  328. log.Printf("%v", cmd.Args)
  329. err = cmd.Run()
  330. if err != nil {
  331. log.Fatalf("%v\n", err)
  332. }
  333. in, err = ioutil.ReadAll(tmpf)
  334. if err != nil {
  335. log.Fatalf("%v\n", err)
  336. }
  337. tmpf.Close()
  338. os.Remove(tmpf.Name())
  339. } else {
  340. var file *os.File
  341. var err error
  342. if file, err = os.Open(path); err != nil {
  343. log.Fatalf("%v", err)
  344. }
  345. in, err = ioutil.ReadAll(file)
  346. if err != nil {
  347. log.Fatalf("%v\n", err)
  348. }
  349. file.Close()
  350. }
  351. total := len(in)
  352. out = []byte(fmt.Sprintf("static unsigned char ramfs_%s_code[] = {\n", name))
  353. for len(in) > 0 {
  354. for j := 0; j < 16 && len(in) > 0; j++ {
  355. out = append(out, []byte(fmt.Sprintf("0x%02x, ", in[0]))...)
  356. in = in[1:]
  357. }
  358. out = append(out, '\n')
  359. }
  360. out = append(out, []byte(fmt.Sprintf("0,\n};\nint ramfs_%s_len = %v;\n", name, total))...)
  361. return string(out), nil
  362. }
  363. func confcode(path string, kern *kernel) []byte {
  364. var rootcodes []string
  365. var rootnames []string
  366. if kern.Ramfiles != nil {
  367. for name, path := range kern.Ramfiles {
  368. code, err := data2c(name, adjust1(path))
  369. if err != nil {
  370. log.Fatalf("%v\n", err)
  371. }
  372. rootcodes = append(rootcodes, code)
  373. rootnames = append(rootnames, name)
  374. }
  375. }
  376. vars := struct {
  377. Path string
  378. Config kernconfig
  379. Rootnames []string
  380. Rootcodes []string
  381. }{
  382. path,
  383. kern.Config,
  384. rootnames,
  385. rootcodes,
  386. }
  387. tmpl, err := template.New("kernconf").Parse(`
  388. #include "u.h"
  389. #include "../port/lib.h"
  390. #include "mem.h"
  391. #include "dat.h"
  392. #include "fns.h"
  393. #include "../port/error.h"
  394. #include "io.h"
  395. void
  396. rdb(void)
  397. {
  398. splhi();
  399. iprint("rdb...not installed\n");
  400. for(;;);
  401. }
  402. {{ range .Rootcodes }}
  403. {{ . }}
  404. {{ end }}
  405. {{ range .Config.Dev }}extern Dev {{ . }}devtab;
  406. {{ end }}
  407. Dev *devtab[] = {
  408. {{ range .Config.Dev }}
  409. &{{ . }}devtab,
  410. {{ end }}
  411. nil,
  412. };
  413. {{ range .Config.Link }}extern void {{ . }}link(void);
  414. {{ end }}
  415. void
  416. links(void)
  417. {
  418. {{ range .Rootnames }}addbootfile("{{ . }}", ramfs_{{ . }}_code, ramfs_{{ . }}_len);
  419. {{ end }}
  420. {{ range .Config.Link }}{{ . }}link();
  421. {{ end }}
  422. }
  423. #include "../ip/ip.h"
  424. {{ range .Config.Ip }}extern void {{ . }}init(Fs*);
  425. {{ end }}
  426. void (*ipprotoinit[])(Fs*) = {
  427. {{ range .Config.Ip }} {{ . }}init,
  428. {{ end }}
  429. nil,
  430. };
  431. #include "../port/sd.h"
  432. {{ range .Config.Sd }}extern SDifc {{ . }}ifc;
  433. {{ end }}
  434. SDifc* sdifc[] = {
  435. {{ range .Config.Sd }} &{{ . }}ifc,
  436. {{ end }}
  437. nil,
  438. };
  439. {{ range .Config.Uart }}extern PhysUart {{ . }}physuart;
  440. {{ end }}
  441. PhysUart* physuart[] = {
  442. {{ range .Config.Uart }} &{{ . }}physuart,
  443. {{ end }}
  444. nil,
  445. };
  446. Physseg physseg[8] = {
  447. {
  448. .attr = SG_SHARED,
  449. .name = "shared",
  450. .size = SEGMAXPG,
  451. },
  452. {
  453. .attr = SG_BSS,
  454. .name = "memory",
  455. .size = SEGMAXPG,
  456. },
  457. };
  458. int nphysseg = 8;
  459. {{ range .Config.Code }}{{ . }}
  460. {{ end }}
  461. char* conffile = "{{ .Path }}";
  462. `)
  463. codebuf := bytes.NewBuffer(nil)
  464. if err != nil {
  465. log.Fatalf("%v\n", err)
  466. }
  467. err = tmpl.Execute(codebuf, vars)
  468. if err != nil {
  469. log.Fatalf("%v\n", err)
  470. }
  471. return codebuf.Bytes()
  472. }
  473. func buildkernel(b *build) {
  474. if b.Kernel == nil {
  475. return
  476. }
  477. codebuf := confcode(b.path, b.Kernel)
  478. if err := ioutil.WriteFile(b.Name+".c", codebuf, 0666); err != nil {
  479. log.Fatalf("Writing %s.c: %v", b.Name, err)
  480. }
  481. }
  482. // assumes we are in the wd of the project.
  483. func project(root string) {
  484. b := &build{}
  485. b.jsons = map[string]bool{}
  486. process(root, b)
  487. projects(b)
  488. run(b, b.Pre)
  489. buildkernel(b)
  490. if len(b.SourceFiles) > 0 {
  491. compile(b)
  492. }
  493. if len(b.SourceFilesCmd) > 0 {
  494. compile(b)
  495. }
  496. log.Printf("root %v program %v\n", root, b.Program)
  497. if b.Program != "" {
  498. link(b)
  499. }
  500. if b.Library != "" {
  501. //library(b)
  502. log.Printf("\n\n*** Building %v ***\n\n", b.Library)
  503. }
  504. if len(b.SourceFilesCmd) > 0 {
  505. link(b)
  506. }
  507. install(b)
  508. run(b, b.Post)
  509. }
  510. func main() {
  511. var badsetup bool
  512. var err error
  513. cwd, err = os.Getwd()
  514. fail(err)
  515. harvey = os.Getenv("HARVEY")
  516. toolprefix = os.Getenv("TOOLPREFIX")
  517. if harvey == "" {
  518. log.Printf("You need to set the HARVEY environment variable")
  519. badsetup = true
  520. }
  521. if os.Getenv("ARCH") == "" {
  522. log.Printf("You need to set the ARCH environment variable")
  523. badsetup = true
  524. }
  525. if badsetup {
  526. os.Exit(1)
  527. }
  528. dir := path.Dir(os.Args[1])
  529. file := path.Base(os.Args[1])
  530. err = os.Chdir(dir)
  531. fail(err)
  532. project(file)
  533. }