build.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. /*
  2. Copyright 2018 Harvey OS Team
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. 1. Redistributions of source code must retain the above copyright notice,
  6. this list of conditions and the following disclaimer.
  7. 2. Redistributions in binary form must reproduce the above copyright notice,
  8. this list of conditions and the following disclaimer in the documentation
  9. and/or other materials provided with the distribution.
  10. 3. Neither the name of the copyright holder nor the names of its contributors
  11. may be used to endorse or promote products derived from this software
  12. without specific prior written permission.
  13. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  14. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  15. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  17. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18. EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20. OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21. WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22. OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  23. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. // Build builds code as directed by json files.
  26. // We slurp in the JSON, and recursively process includes.
  27. // At the end, we issue a single cc command for all the files.
  28. // Compilers are fast.
  29. //
  30. // ENVIRONMENT
  31. //
  32. // Needed: CC, HARVEY, ARCH
  33. //
  34. // HARVEY should point to a harvey root.
  35. // A best-effort to autodetect the harvey root is made if not explicitly set.
  36. //
  37. // Optional: AR, LD, RANLIB, STRIP, SH, TOOLPREFIX
  38. //
  39. // These all control how the needed tools are found.
  40. //
  41. package main
  42. import (
  43. "encoding/json"
  44. "errors"
  45. "flag"
  46. "fmt"
  47. "io"
  48. "io/ioutil"
  49. "log"
  50. "os"
  51. "os/exec"
  52. "path"
  53. "path/filepath"
  54. "regexp"
  55. "strings"
  56. )
  57. type kernconfig struct {
  58. Code []string
  59. Dev []string
  60. Ip []string
  61. Link []string
  62. Sd []string
  63. Uart []string
  64. VGA []string
  65. }
  66. type kernel struct {
  67. Systab string
  68. Config kernconfig
  69. Ramfiles map[string]string
  70. }
  71. type build struct {
  72. // jsons is unexported so can not be set in a .json file
  73. jsons []string
  74. path string
  75. Name string
  76. // Projects name a whole subproject which is built independently of
  77. // this one. We'll need to be able to use environment variables at some point.
  78. Projects []string
  79. Pre []string
  80. Post []string
  81. Cflags []string
  82. Oflags []string
  83. Include []string
  84. SourceFiles []string
  85. ObjectFiles []string
  86. Libs []string
  87. Env []string
  88. SourceDeps []string
  89. // cmd's
  90. SourceFilesCmd []string
  91. // Targets.
  92. Program string
  93. Library string
  94. Install string // where to place the resulting binary/lib
  95. Kernel *kernel
  96. }
  97. type buildfile []build
  98. func globby(s []string) []string {
  99. all := []string{}
  100. for _, n := range s {
  101. l, err := filepath.Glob(n)
  102. if err != nil || len(l) == 0 {
  103. all = append(all, n)
  104. } else {
  105. all = append(all, l...)
  106. }
  107. }
  108. debug("Glob of '%v' is '%v'", s, all)
  109. return all
  110. }
  111. // UnmarshalJSON works like the stdlib unmarshal would, except it adjusts all
  112. // paths.
  113. func (bf *buildfile) UnmarshalJSON(s []byte) error {
  114. r := make([]build, 0)
  115. if err := json.Unmarshal(s, &r); err != nil {
  116. return err
  117. }
  118. for k, b := range r {
  119. // we're getting a copy of the struct, remember.
  120. b.jsons = []string{}
  121. b.Projects = adjust(b.Projects)
  122. b.Libs = adjust(b.Libs)
  123. b.SourceDeps = adjust(b.SourceDeps)
  124. b.Cflags = adjust(b.Cflags)
  125. b.Oflags = adjust(b.Oflags)
  126. b.SourceFiles = globby(adjust(b.SourceFiles))
  127. b.SourceFilesCmd = globby(adjust(b.SourceFilesCmd))
  128. b.ObjectFiles = adjust(b.ObjectFiles)
  129. b.Include = adjust(b.Include)
  130. b.Install = fromRoot(b.Install)
  131. for i, e := range b.Env {
  132. b.Env[i] = os.ExpandEnv(e)
  133. }
  134. r[k] = b
  135. }
  136. *bf = r
  137. return nil
  138. }
  139. func (b *build) includedJson(filename string) bool {
  140. for _, includedJson := range b.jsons {
  141. if includedJson == filename {
  142. return true
  143. }
  144. }
  145. return false
  146. }
  147. var (
  148. harvey string
  149. regexpAll = []*regexp.Regexp{regexp.MustCompile(".")}
  150. // findTools looks at all env vars and absolutizes these paths
  151. // also respects TOOLPREFIX
  152. tools = map[string]string{
  153. "cc": "gcc",
  154. "ar": "ar",
  155. "ld": "ld",
  156. "ranlib": "ranlib",
  157. "strip": "strip",
  158. }
  159. arch = map[string]bool{
  160. "amd64": true,
  161. "riscv": true,
  162. "aarch64": true,
  163. }
  164. debugPrint = flag.Bool("debug", false, "enable debug prints")
  165. depends = flag.Bool("D", false, "Do dependency checking")
  166. shellhack = flag.Bool("shellhack", false, "spawn every command in a shell (forced on if LD_PRELOAD is set)")
  167. )
  168. func debug(fmt string, s ...interface{}) {
  169. if *debugPrint {
  170. log.Printf(fmt, s...)
  171. }
  172. }
  173. func fail(err error) {
  174. if err != nil {
  175. log.Fatalf("%s\n", err.Error())
  176. }
  177. }
  178. func adjust(s []string) []string {
  179. for i, v := range s {
  180. s[i] = fromRoot(v)
  181. }
  182. return s
  183. }
  184. // return the given absolute path as an absolute path rooted at the harvey tree.
  185. func fromRoot(p string) string {
  186. expandedPath := os.ExpandEnv(p)
  187. if path.IsAbs(p) {
  188. expandedPath = path.Join(harvey, expandedPath)
  189. }
  190. // Travis has versioned CCs of the form clang-X.Y. We don't want to have
  191. // a file for each version of the compilers, so check if the versioned
  192. // file exists first. If it doesn't, fall back to the unversioned file.
  193. expandedCc := os.Getenv("CC")
  194. expandedCcTokens := strings.Split(expandedCc, "-")
  195. fallbackCc := expandedCcTokens[0]
  196. if strings.Contains(expandedPath, "$CC") && len(expandedCcTokens) > 1 {
  197. if _, err := os.Stat(expandedPath); err != nil {
  198. if os.IsNotExist(err) {
  199. oldCc := os.Getenv("CC")
  200. os.Setenv("CC", fallbackCc)
  201. expandedPath = fromRoot(p)
  202. os.Setenv("CC", oldCc)
  203. }
  204. }
  205. }
  206. return expandedPath
  207. }
  208. func include(f string, targ string, b *build) {
  209. debug("include(%s, %s, %v)", f, targ, b)
  210. if b.includedJson(f) {
  211. return
  212. }
  213. b.jsons = append(b.jsons, f)
  214. log.Printf("Including %s", f)
  215. builds := unmarshalBuild(f)
  216. for _, build := range builds {
  217. t := target(&build)
  218. if t != "" {
  219. targ = t
  220. break
  221. }
  222. }
  223. for _, build := range builds {
  224. log.Printf("Merging %s", build.Name)
  225. b.Cflags = append(b.Cflags, build.Cflags...)
  226. b.Oflags = append(b.Oflags, build.Oflags...)
  227. b.Pre = append(b.Pre, build.Pre...)
  228. b.Post = append(b.Post, build.Post...)
  229. b.Libs = append(b.Libs, build.Libs...)
  230. b.Projects = append(b.Projects, build.Projects...)
  231. b.Env = append(b.Env, build.Env...)
  232. for _, v := range build.SourceFilesCmd {
  233. _, t := cmdTarget(&build, v)
  234. if uptodate(t, append(b.SourceDeps, v)) {
  235. continue
  236. }
  237. b.SourceFilesCmd = append(b.SourceFilesCmd, v)
  238. }
  239. if build.Install != "" {
  240. if b.Install != "" && build.Install != b.Install {
  241. log.Fatalf("In file %s (target %s) included by %s (target %s): redefined Install: was %s, redefined to %s.", f, b.Name, build.path, build.Name, b.Install, build.Install)
  242. }
  243. b.Install = build.Install
  244. }
  245. b.ObjectFiles = append(b.ObjectFiles, build.ObjectFiles...)
  246. // For each source file, assume we create an object file with the last char replaced
  247. // with 'o'. We can get smarter later.
  248. b.SourceDeps = append(b.SourceDeps, build.SourceDeps...)
  249. var s []string
  250. for _, v := range build.SourceFiles {
  251. if uptodate(targ, append(b.SourceDeps, v)) {
  252. continue
  253. }
  254. s = append(s, v)
  255. }
  256. if *depends && build.Program != "" {
  257. if len(s) == 0 {
  258. build.SourceFiles = []string{}
  259. build.Program = ""
  260. }
  261. }
  262. if *depends && b.Library != "" {
  263. build.SourceFiles = s
  264. }
  265. for _, v := range build.SourceFiles {
  266. b.SourceFiles = append(b.SourceFiles, v)
  267. fi := path.Base(v)
  268. ext := path.Ext(v)
  269. o := fi[:len(fi)-len(ext)+1] + "o"
  270. b.ObjectFiles = append(b.ObjectFiles, o)
  271. }
  272. for _, v := range build.Include {
  273. if !path.IsAbs(v) {
  274. wd := path.Dir(f)
  275. v = path.Join(wd, v)
  276. }
  277. include(v, targ, b)
  278. }
  279. b.Program += build.Program
  280. b.Library += build.Library
  281. }
  282. }
  283. func contains(a []string, s string) bool {
  284. for i := range a {
  285. if a[i] == s {
  286. return true
  287. }
  288. }
  289. return false
  290. }
  291. func unmarshalBuild(f string) buildfile {
  292. d, err := ioutil.ReadFile(f)
  293. fail(err)
  294. var builds buildfile
  295. fail(json.Unmarshal(d, &builds))
  296. return builds
  297. }
  298. func process(f string, r []*regexp.Regexp) []build {
  299. log.Printf("Processing %s", f)
  300. var results []build
  301. builds := unmarshalBuild(f)
  302. for _, build := range builds {
  303. build.jsons = []string{}
  304. skip := true
  305. for _, re := range r {
  306. if re.MatchString(build.Name) {
  307. skip = false
  308. break
  309. }
  310. }
  311. if skip {
  312. continue
  313. }
  314. log.Printf("Running %s", build.Name)
  315. build.jsons = append(build.jsons, f)
  316. build.path = path.Dir(f)
  317. // Figure out which of these are up to date.
  318. var s []string
  319. for _, v := range build.SourceFilesCmd {
  320. _, targ := cmdTarget(&build, v)
  321. if uptodate(targ, append(build.SourceDeps, v)) {
  322. continue
  323. }
  324. s = append(s, v)
  325. }
  326. build.SourceFilesCmd = s
  327. // For each source file, assume we create an object file with the last char replaced
  328. // with 'o'. We can get smarter later.
  329. t := target(&build)
  330. deps := targetDepends(&build)
  331. debug("\ttarget is '%s', deps are '%v'", t, deps)
  332. for _, v := range build.SourceFiles {
  333. if uptodate(t, append(deps, v)) {
  334. continue
  335. }
  336. s = append(s, v)
  337. }
  338. if *depends && build.Program != "" {
  339. if len(s) == 0 {
  340. build.SourceFiles = []string{}
  341. build.Program = ""
  342. }
  343. }
  344. if *depends && build.Library != "" {
  345. build.SourceFiles = s
  346. }
  347. for _, v := range build.SourceFiles {
  348. f := path.Base(v)
  349. ext := path.Ext(f)
  350. l := len(f) - len(ext) + 1
  351. o := f[:l]
  352. o += "o"
  353. if !contains(build.ObjectFiles, o) {
  354. build.ObjectFiles = append(build.ObjectFiles, o)
  355. }
  356. }
  357. for _, v := range build.Include {
  358. include(v, t, &build)
  359. }
  360. results = append(results, build)
  361. }
  362. return results
  363. }
  364. func buildkernel(b *build) {
  365. if b.Kernel == nil {
  366. return
  367. }
  368. codebuf := confcode(b.path, b.Kernel)
  369. fail(ioutil.WriteFile(b.Name+".c", codebuf, 0666))
  370. }
  371. func uptodate(n string, d []string) bool {
  372. debug("uptodate: %s, %v\n", n, d)
  373. if !*depends {
  374. debug("\t no\n")
  375. return false
  376. }
  377. fi, err := os.Stat(n)
  378. // If it does not exist, by definition it's not up to date
  379. if err != nil {
  380. debug("\t target '%s' doesn't exist\n", n)
  381. return false
  382. }
  383. m := fi.ModTime()
  384. debug("older: time is %v\n", m)
  385. if len(d) == 0 {
  386. log.Fatalf("update has nothing to check for %v", n)
  387. }
  388. for _, d := range d {
  389. debug("\tCheck %s:", d)
  390. di, err := os.Stat(d)
  391. if err != nil {
  392. return false
  393. }
  394. if !di.ModTime().Before(m) {
  395. debug("%v is newer\n", di.ModTime())
  396. return false
  397. }
  398. debug("%v is older\n", di.ModTime())
  399. }
  400. debug("all is older\n")
  401. return true
  402. }
  403. func targetDepends(b *build) []string {
  404. return append(b.SourceDeps, fromRoot("/sys/include/libc.h"), fromRoot("/$ARCH/include/u.h"))
  405. }
  406. func target(b *build) string {
  407. if b.Program != "" {
  408. return path.Join(b.Install, b.Program)
  409. }
  410. if b.Library != "" {
  411. return path.Join(b.Install, b.Library)
  412. }
  413. return ""
  414. }
  415. func cmdTarget(b *build, n string) (string, string) {
  416. ext := filepath.Ext(n)
  417. exe := n[:len(n)-len(ext)]
  418. return exe, b.Install
  419. }
  420. func compile(b *build) {
  421. log.Printf("Building %s\n", b.Name)
  422. // N.B. Plan 9 has a very well defined include structure, just three things:
  423. // /amd64/include, /sys/include, .
  424. args := []string{
  425. "-std=c11", "-c",
  426. "-I", fromRoot("/$ARCH/include"),
  427. "-I", fromRoot("/sys/include"),
  428. "-I", ".",
  429. }
  430. args = append(args, b.Cflags...)
  431. if len(b.SourceFilesCmd) > 0 {
  432. for _, i := range b.SourceFilesCmd {
  433. cmd := exec.Command(tools["cc"], append(args, i)...)
  434. run(b, *shellhack, cmd)
  435. }
  436. return
  437. }
  438. args = append(args, b.SourceFiles...)
  439. cmd := exec.Command(tools["cc"], args...)
  440. run(b, *shellhack, cmd)
  441. }
  442. func link(b *build) {
  443. log.Printf("Linking %s\n", b.Name)
  444. if len(b.SourceFilesCmd) > 0 {
  445. for _, n := range b.SourceFilesCmd {
  446. // Split off the last element of the file
  447. var ext = filepath.Ext(n)
  448. if len(ext) == 0 {
  449. log.Fatalf("refusing to overwrite extension-less source file %v", n)
  450. continue
  451. }
  452. n = n[:len(n)-len(ext)]
  453. f := path.Base(n)
  454. o := f[:len(f)] + ".o"
  455. args := []string{"-o", n, o}
  456. args = append(args, "-L", fromRoot("/$ARCH/lib"))
  457. args = append(args, b.Libs...)
  458. args = append(args, b.Oflags...)
  459. run(b, *shellhack, exec.Command(tools["ld"], args...))
  460. }
  461. return
  462. }
  463. args := []string{"-o", b.Program}
  464. args = append(args, b.ObjectFiles...)
  465. args = append(args, "-L", fromRoot("/$ARCH/lib"))
  466. args = append(args, b.Libs...)
  467. args = append(args, b.Oflags...)
  468. run(b, *shellhack, exec.Command(tools["ld"], args...))
  469. }
  470. func install(b *build) {
  471. if b.Install == "" {
  472. return
  473. }
  474. log.Printf("Installing %s\n", b.Name)
  475. fail(os.MkdirAll(b.Install, 0755))
  476. switch {
  477. case len(b.SourceFilesCmd) > 0:
  478. for _, n := range b.SourceFilesCmd {
  479. move(cmdTarget(b, n))
  480. }
  481. case len(b.Program) > 0:
  482. move(b.Program, b.Install)
  483. case len(b.Library) > 0:
  484. libpath := path.Join(b.Install, b.Library)
  485. args := append([]string{"-rs", libpath}, b.ObjectFiles...)
  486. run(b, *shellhack, exec.Command(tools["ar"], args...))
  487. run(b, *shellhack, exec.Command(tools["ranlib"], libpath))
  488. }
  489. }
  490. func move(from, to string) {
  491. final := path.Join(to, from)
  492. log.Printf("move %s %s\n", from, final)
  493. _ = os.Remove(final)
  494. fail(os.Link(from, final))
  495. fail(os.Remove(from))
  496. }
  497. func run(b *build, pipe bool, cmd *exec.Cmd) {
  498. sh := os.Getenv("SHELL")
  499. if sh == "" {
  500. sh = "sh"
  501. }
  502. if b != nil {
  503. cmd.Env = append(os.Environ(), b.Env...)
  504. }
  505. cmd.Stdout = os.Stdout
  506. cmd.Stderr = os.Stderr
  507. if pipe {
  508. // Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks, see https://github.com/Harvey-OS/harvey/issues/8#issuecomment-131235178
  509. shell := exec.Command(sh)
  510. shell.Env = cmd.Env
  511. shell.Stderr = os.Stderr
  512. shell.Stdout = os.Stdout
  513. commandString := cmd.Args[0]
  514. for _, a := range cmd.Args[1:] {
  515. if strings.Contains(a, "=") {
  516. commandString += " '" + a + "'"
  517. } else {
  518. commandString += " " + a
  519. }
  520. }
  521. shStdin, err := shell.StdinPipe()
  522. if err != nil {
  523. log.Fatalf("cannot pipe [%v] to %s: %v", commandString, sh, err)
  524. }
  525. go func() {
  526. defer shStdin.Close()
  527. io.WriteString(shStdin, commandString)
  528. }()
  529. log.Printf("%q | %s\n", commandString, sh)
  530. fail(shell.Run())
  531. return
  532. }
  533. log.Println(strings.Join(cmd.Args, " "))
  534. fail(cmd.Run())
  535. }
  536. func projects(b *build, r []*regexp.Regexp) {
  537. for _, v := range b.Projects {
  538. f, err := findBuildfile(v)
  539. log.Printf("Doing %s\n", f)
  540. if err != nil {
  541. log.Println(err)
  542. }
  543. project(f, r)
  544. }
  545. }
  546. func dirPop(s string) {
  547. fmt.Printf("Leaving directory `%v'\n", s)
  548. fail(os.Chdir(s))
  549. }
  550. func dirPush(s string) {
  551. fmt.Printf("Entering directory `%v'\n", s)
  552. fail(os.Chdir(s))
  553. }
  554. func runCmds(b *build, s []string) {
  555. for _, c := range s {
  556. args := adjust(strings.Split(c, " "))
  557. var exp []string
  558. for _, v := range args {
  559. e, err := filepath.Glob(v)
  560. debug("glob %v to %v err %v", v, e, err)
  561. if len(e) == 0 || err != nil {
  562. exp = append(exp, v)
  563. } else {
  564. exp = append(exp, e...)
  565. }
  566. }
  567. run(b, *shellhack, exec.Command(exp[0], exp[1:]...))
  568. }
  569. }
  570. // assumes we are in the wd of the project.
  571. func project(bf string, which []*regexp.Regexp) {
  572. cwd, err := os.Getwd()
  573. fail(err)
  574. debug("Start new project cwd is %v", cwd)
  575. defer dirPop(cwd)
  576. dir := path.Dir(bf)
  577. root := path.Base(bf)
  578. debug("CD to %v and build using %v", dir, root)
  579. dirPush(dir)
  580. builds := process(root, which)
  581. debug("Processing %v: %d target", root, len(builds))
  582. for _, b := range builds {
  583. debug("Processing %v: %v", b.Name, b)
  584. projects(&b, regexpAll)
  585. runCmds(&b, b.Pre)
  586. buildkernel(&b)
  587. if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
  588. compile(&b)
  589. }
  590. if b.Program != "" || len(b.SourceFilesCmd) > 0 {
  591. link(&b)
  592. }
  593. install(&b)
  594. runCmds(&b, b.Post)
  595. }
  596. }
  597. func main() {
  598. log.SetFlags(0)
  599. // A small amount of setup is done in the paths*.go files. They are
  600. // OS-specific path setup/manipulation. "harvey" is set there and $PATH is
  601. // adjusted.
  602. flag.Parse()
  603. findTools(os.Getenv("TOOLPREFIX"))
  604. if os.Getenv("CC") == "" {
  605. log.Fatalf("You need to set the CC environment variable (e.g. gcc, clang, clang-3.6, ...)")
  606. }
  607. a := os.Getenv("ARCH")
  608. if a == "" || !arch[a] {
  609. s := []string{}
  610. for i := range arch {
  611. s = append(s, i)
  612. }
  613. log.Fatalf("You need to set the ARCH environment variable from: %v", s)
  614. }
  615. // ensure this is exported, in case we used a default value
  616. os.Setenv("HARVEY", harvey)
  617. if os.Getenv("LD_PRELOAD") != "" {
  618. log.Println("Using shellhack")
  619. *shellhack = true
  620. }
  621. // If there is'n args, we search for a 'build.json' file
  622. // Otherwise the first argument could be
  623. // A path to a json file
  624. // or a directory containing the 'build.json' file
  625. // or a regular expression to apply assuming 'build.json'
  626. // Further arguments are considered regex.
  627. consumedArgs := 0
  628. var bf string
  629. if flag.NArg() > 0 {
  630. f, err := findBuildfile(flag.Arg(0))
  631. fail(err)
  632. if f == "" {
  633. fb, err := findBuildfile("build.json")
  634. fail(err)
  635. bf = fb
  636. } else {
  637. consumedArgs = 1
  638. bf = f
  639. }
  640. } else {
  641. f, err := findBuildfile("build.json")
  642. fail(err)
  643. bf = f
  644. }
  645. re := []*regexp.Regexp{regexp.MustCompile(".")}
  646. if len(flag.Args()) > consumedArgs {
  647. re = re[:0]
  648. for _, r := range flag.Args()[consumedArgs:] {
  649. rx, err := regexp.Compile(r)
  650. fail(err)
  651. re = append(re, rx)
  652. }
  653. }
  654. project(bf, re)
  655. }
  656. func findTools(toolprefix string) {
  657. var err error
  658. for k, v := range tools {
  659. if x := os.Getenv(strings.ToUpper(k)); x != "" {
  660. v = x
  661. }
  662. v, err = exec.LookPath(toolprefix + v)
  663. fail(err)
  664. tools[k] = v
  665. }
  666. }
  667. func isDir(f string) (bool, error) {
  668. fi, err := os.Stat(f)
  669. if err != nil {
  670. return false, err
  671. }
  672. return fi.IsDir(), nil
  673. }
  674. // disambiguate the buildfile argument
  675. func findBuildfile(f string) (string, error) {
  676. if !strings.HasSuffix(f, ".json") {
  677. b, err := isDir(f)
  678. if err != nil {
  679. // the path didn't exist
  680. return "", errors.New("unable to find buildfile " + f)
  681. }
  682. if b {
  683. f = path.Join(f, "build.json")
  684. } else {
  685. // this is a file without .json suffix
  686. return "", errors.New("buildfile must be a .json file, " + f + " is not a valid name")
  687. }
  688. }
  689. if b, err := isDir(f); err != nil || b {
  690. return "", errors.New("unable to find buildfile " + f)
  691. }
  692. return f, nil
  693. }