build.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  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. p = os.ExpandEnv(p)
  187. if path.IsAbs(p) {
  188. return path.Join(harvey, p)
  189. }
  190. return p
  191. }
  192. func include(f string, targ string, b *build) {
  193. debug("include(%s, %s, %v)", f, targ, b)
  194. if b.includedJson(f) {
  195. return
  196. }
  197. b.jsons = append(b.jsons, f)
  198. log.Printf("Including %s", f)
  199. builds := unmarshalBuild(f)
  200. for _, build := range builds {
  201. t := target(&build)
  202. if t != "" {
  203. targ = t
  204. break
  205. }
  206. }
  207. for _, build := range builds {
  208. log.Printf("Merging %s", build.Name)
  209. b.Cflags = append(b.Cflags, build.Cflags...)
  210. b.Oflags = append(b.Oflags, build.Oflags...)
  211. b.Pre = append(b.Pre, build.Pre...)
  212. b.Post = append(b.Post, build.Post...)
  213. b.Libs = append(b.Libs, build.Libs...)
  214. b.Projects = append(b.Projects, build.Projects...)
  215. b.Env = append(b.Env, build.Env...)
  216. for _, v := range build.SourceFilesCmd {
  217. _, t := cmdTarget(&build, v)
  218. if uptodate(t, append(b.SourceDeps, v)) {
  219. continue
  220. }
  221. b.SourceFilesCmd = append(b.SourceFilesCmd, v)
  222. }
  223. if build.Install != "" {
  224. if b.Install != "" && build.Install != b.Install {
  225. 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)
  226. }
  227. b.Install = build.Install
  228. }
  229. b.ObjectFiles = append(b.ObjectFiles, build.ObjectFiles...)
  230. // For each source file, assume we create an object file with the last char replaced
  231. // with 'o'. We can get smarter later.
  232. b.SourceDeps = append(b.SourceDeps, build.SourceDeps...)
  233. var s []string
  234. for _, v := range build.SourceFiles {
  235. if uptodate(targ, append(b.SourceDeps, v)) {
  236. continue
  237. }
  238. s = append(s, v)
  239. }
  240. if *depends && build.Program != "" {
  241. if len(s) == 0 {
  242. build.SourceFiles = []string{}
  243. build.Program = ""
  244. }
  245. }
  246. if *depends && b.Library != "" {
  247. build.SourceFiles = s
  248. }
  249. for _, v := range build.SourceFiles {
  250. b.SourceFiles = append(b.SourceFiles, v)
  251. fi := path.Base(v)
  252. ext := path.Ext(v)
  253. o := fi[:len(fi)-len(ext)+1] + "o"
  254. b.ObjectFiles = append(b.ObjectFiles, o)
  255. }
  256. for _, v := range build.Include {
  257. if !path.IsAbs(v) {
  258. wd := path.Dir(f)
  259. v = path.Join(wd, v)
  260. }
  261. include(v, targ, b)
  262. }
  263. b.Program += build.Program
  264. b.Library += build.Library
  265. }
  266. }
  267. func contains(a []string, s string) bool {
  268. for i := range a {
  269. if a[i] == s {
  270. return true
  271. }
  272. }
  273. return false
  274. }
  275. func unmarshalBuild(f string) buildfile {
  276. d, err := ioutil.ReadFile(f)
  277. fail(err)
  278. var builds buildfile
  279. fail(json.Unmarshal(d, &builds))
  280. return builds
  281. }
  282. func process(f string, r []*regexp.Regexp) []build {
  283. log.Printf("Processing %s", f)
  284. var results []build
  285. builds := unmarshalBuild(f)
  286. for _, build := range builds {
  287. build.jsons = []string{}
  288. skip := true
  289. for _, re := range r {
  290. if re.MatchString(build.Name) {
  291. skip = false
  292. break
  293. }
  294. }
  295. if skip {
  296. continue
  297. }
  298. log.Printf("Running %s", build.Name)
  299. build.jsons = append(build.jsons, f)
  300. build.path = path.Dir(f)
  301. // Figure out which of these are up to date.
  302. var s []string
  303. for _, v := range build.SourceFilesCmd {
  304. _, targ := cmdTarget(&build, v)
  305. if uptodate(targ, append(build.SourceDeps, v)) {
  306. continue
  307. }
  308. s = append(s, v)
  309. }
  310. build.SourceFilesCmd = s
  311. // For each source file, assume we create an object file with the last char replaced
  312. // with 'o'. We can get smarter later.
  313. t := target(&build)
  314. deps := targetDepends(&build)
  315. debug("\ttarget is '%s', deps are '%v'", t, deps)
  316. for _, v := range build.SourceFiles {
  317. if uptodate(t, append(deps, v)) {
  318. continue
  319. }
  320. s = append(s, v)
  321. }
  322. if *depends && build.Program != "" {
  323. if len(s) == 0 {
  324. build.SourceFiles = []string{}
  325. build.Program = ""
  326. }
  327. }
  328. if *depends && build.Library != "" {
  329. build.SourceFiles = s
  330. }
  331. for _, v := range build.SourceFiles {
  332. f := path.Base(v)
  333. ext := path.Ext(f)
  334. l := len(f) - len(ext) + 1
  335. o := f[:l]
  336. o += "o"
  337. if !contains(build.ObjectFiles, o) {
  338. build.ObjectFiles = append(build.ObjectFiles, o)
  339. }
  340. }
  341. for _, v := range build.Include {
  342. include(v, t, &build)
  343. }
  344. results = append(results, build)
  345. }
  346. return results
  347. }
  348. func buildkernel(b *build) {
  349. if b.Kernel == nil {
  350. return
  351. }
  352. codebuf := confcode(b.path, b.Kernel)
  353. fail(ioutil.WriteFile(b.Name+".c", codebuf, 0666))
  354. }
  355. func uptodate(n string, d []string) bool {
  356. debug("uptodate: %s, %v\n", n, d)
  357. if !*depends {
  358. debug("\t no\n")
  359. return false
  360. }
  361. fi, err := os.Stat(n)
  362. // If it does not exist, by definition it's not up to date
  363. if err != nil {
  364. debug("\t target '%s' doesn't exist\n", n)
  365. return false
  366. }
  367. m := fi.ModTime()
  368. debug("older: time is %v\n", m)
  369. if len(d) == 0 {
  370. log.Fatalf("update has nothing to check for %v", n)
  371. }
  372. for _, d := range d {
  373. debug("\tCheck %s:", d)
  374. di, err := os.Stat(d)
  375. if err != nil {
  376. return false
  377. }
  378. if !di.ModTime().Before(m) {
  379. debug("%v is newer\n", di.ModTime())
  380. return false
  381. }
  382. debug("%v is older\n", di.ModTime())
  383. }
  384. debug("all is older\n")
  385. return true
  386. }
  387. func targetDepends(b *build) []string {
  388. return append(b.SourceDeps, fromRoot("/sys/include/libc.h"), fromRoot("/$ARCH/include/u.h"))
  389. }
  390. func target(b *build) string {
  391. if b.Program != "" {
  392. return path.Join(b.Install, b.Program)
  393. }
  394. if b.Library != "" {
  395. return path.Join(b.Install, b.Library)
  396. }
  397. return ""
  398. }
  399. func cmdTarget(b *build, n string) (string, string) {
  400. ext := filepath.Ext(n)
  401. exe := n[:len(n)-len(ext)]
  402. return exe, b.Install
  403. }
  404. func compile(b *build) {
  405. log.Printf("Building %s\n", b.Name)
  406. // N.B. Plan 9 has a very well defined include structure, just three things:
  407. // /amd64/include, /sys/include, .
  408. args := []string{
  409. "-std=c11", "-c",
  410. "-I", fromRoot("/$ARCH/include"),
  411. "-I", fromRoot("/sys/include"),
  412. "-I", ".",
  413. }
  414. args = append(args, b.Cflags...)
  415. if len(b.SourceFilesCmd) > 0 {
  416. for _, i := range b.SourceFilesCmd {
  417. cmd := exec.Command(tools["cc"], append(args, i)...)
  418. run(b, *shellhack, cmd)
  419. }
  420. return
  421. }
  422. args = append(args, b.SourceFiles...)
  423. cmd := exec.Command(tools["cc"], args...)
  424. run(b, *shellhack, cmd)
  425. }
  426. func link(b *build) {
  427. log.Printf("Linking %s\n", b.Name)
  428. if len(b.SourceFilesCmd) > 0 {
  429. for _, n := range b.SourceFilesCmd {
  430. // Split off the last element of the file
  431. var ext = filepath.Ext(n)
  432. if len(ext) == 0 {
  433. log.Fatalf("refusing to overwrite extension-less source file %v", n)
  434. continue
  435. }
  436. n = n[:len(n)-len(ext)]
  437. f := path.Base(n)
  438. o := f[:len(f)] + ".o"
  439. args := []string{"-o", n, o}
  440. args = append(args, "-L", fromRoot("/$ARCH/lib"))
  441. args = append(args, b.Libs...)
  442. args = append(args, b.Oflags...)
  443. run(b, *shellhack, exec.Command(tools["ld"], args...))
  444. }
  445. return
  446. }
  447. args := []string{"-o", b.Program}
  448. args = append(args, b.ObjectFiles...)
  449. args = append(args, "-L", fromRoot("/$ARCH/lib"))
  450. args = append(args, b.Libs...)
  451. args = append(args, b.Oflags...)
  452. run(b, *shellhack, exec.Command(tools["ld"], args...))
  453. }
  454. func install(b *build) {
  455. if b.Install == "" {
  456. return
  457. }
  458. log.Printf("Installing %s\n", b.Name)
  459. fail(os.MkdirAll(b.Install, 0755))
  460. switch {
  461. case len(b.SourceFilesCmd) > 0:
  462. for _, n := range b.SourceFilesCmd {
  463. move(cmdTarget(b, n))
  464. }
  465. case len(b.Program) > 0:
  466. move(b.Program, b.Install)
  467. case len(b.Library) > 0:
  468. libpath := path.Join(b.Install, b.Library)
  469. args := append([]string{"-rs", libpath}, b.ObjectFiles...)
  470. run(b, *shellhack, exec.Command(tools["ar"], args...))
  471. run(b, *shellhack, exec.Command(tools["ranlib"], libpath))
  472. }
  473. }
  474. func move(from, to string) {
  475. final := path.Join(to, from)
  476. log.Printf("move %s %s\n", from, final)
  477. _ = os.Remove(final)
  478. fail(os.Link(from, final))
  479. fail(os.Remove(from))
  480. }
  481. func run(b *build, pipe bool, cmd *exec.Cmd) {
  482. sh := os.Getenv("SHELL")
  483. if sh == "" {
  484. sh = "sh"
  485. }
  486. if b != nil {
  487. cmd.Env = append(os.Environ(), b.Env...)
  488. }
  489. cmd.Stdout = os.Stdout
  490. cmd.Stderr = os.Stderr
  491. if pipe {
  492. // 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
  493. shell := exec.Command(sh)
  494. shell.Env = cmd.Env
  495. shell.Stderr = os.Stderr
  496. shell.Stdout = os.Stdout
  497. commandString := cmd.Args[0]
  498. for _, a := range cmd.Args[1:] {
  499. if strings.Contains(a, "=") {
  500. commandString += " '" + a + "'"
  501. } else {
  502. commandString += " " + a
  503. }
  504. }
  505. shStdin, err := shell.StdinPipe()
  506. if err != nil {
  507. log.Fatalf("cannot pipe [%v] to %s: %v", commandString, sh, err)
  508. }
  509. go func() {
  510. defer shStdin.Close()
  511. io.WriteString(shStdin, commandString)
  512. }()
  513. log.Printf("%q | %s\n", commandString, sh)
  514. fail(shell.Run())
  515. return
  516. }
  517. log.Println(strings.Join(cmd.Args, " "))
  518. fail(cmd.Run())
  519. }
  520. func projects(b *build, r []*regexp.Regexp) {
  521. for _, v := range b.Projects {
  522. f, err := findBuildfile(v)
  523. log.Printf("Doing %s\n", f)
  524. if err != nil {
  525. log.Println(err)
  526. }
  527. project(f, r)
  528. }
  529. }
  530. func dirPop(s string) {
  531. fmt.Printf("Leaving directory `%v'\n", s)
  532. fail(os.Chdir(s))
  533. }
  534. func dirPush(s string) {
  535. fmt.Printf("Entering directory `%v'\n", s)
  536. fail(os.Chdir(s))
  537. }
  538. func runCmds(b *build, s []string) {
  539. for _, c := range s {
  540. args := adjust(strings.Split(c, " "))
  541. var exp []string
  542. for _, v := range args {
  543. e, err := filepath.Glob(v)
  544. debug("glob %v to %v err %v", v, e, err)
  545. if len(e) == 0 || err != nil {
  546. exp = append(exp, v)
  547. } else {
  548. exp = append(exp, e...)
  549. }
  550. }
  551. run(b, *shellhack, exec.Command(exp[0], exp[1:]...))
  552. }
  553. }
  554. // assumes we are in the wd of the project.
  555. func project(bf string, which []*regexp.Regexp) {
  556. cwd, err := os.Getwd()
  557. fail(err)
  558. debug("Start new project cwd is %v", cwd)
  559. defer dirPop(cwd)
  560. dir := path.Dir(bf)
  561. root := path.Base(bf)
  562. debug("CD to %v and build using %v", dir, root)
  563. dirPush(dir)
  564. builds := process(root, which)
  565. debug("Processing %v: %d target", root, len(builds))
  566. for _, b := range builds {
  567. debug("Processing %v: %v", b.Name, b)
  568. projects(&b, regexpAll)
  569. runCmds(&b, b.Pre)
  570. buildkernel(&b)
  571. if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
  572. compile(&b)
  573. }
  574. if b.Program != "" || len(b.SourceFilesCmd) > 0 {
  575. link(&b)
  576. }
  577. install(&b)
  578. runCmds(&b, b.Post)
  579. }
  580. }
  581. func main() {
  582. log.SetFlags(0)
  583. // A small amount of setup is done in the paths*.go files. They are
  584. // OS-specific path setup/manipulation. "harvey" is set there and $PATH is
  585. // adjusted.
  586. flag.Parse()
  587. findTools(os.Getenv("TOOLPREFIX"))
  588. if os.Getenv("CC") == "" {
  589. log.Fatalf("You need to set the CC environment variable (e.g. gcc, clang, clang-3.6, ...)")
  590. }
  591. a := os.Getenv("ARCH")
  592. if a == "" || !arch[a] {
  593. s := []string{}
  594. for i := range arch {
  595. s = append(s, i)
  596. }
  597. log.Fatalf("You need to set the ARCH environment variable from: %v", s)
  598. }
  599. // ensure this is exported, in case we used a default value
  600. os.Setenv("HARVEY", harvey)
  601. if os.Getenv("LD_PRELOAD") != "" {
  602. log.Println("Using shellhack")
  603. *shellhack = true
  604. }
  605. // If there is'n args, we search for a 'build.json' file
  606. // Otherwise the first argument could be
  607. // A path to a json file
  608. // or a directory containing the 'build.json' file
  609. // or a regular expression to apply assuming 'build.json'
  610. // Further arguments are considered regex.
  611. consumedArgs := 0
  612. var bf string
  613. if flag.NArg() > 0 {
  614. f, err := findBuildfile(flag.Arg(0))
  615. fail(err)
  616. if f == "" {
  617. fb, err := findBuildfile("build.json")
  618. fail(err)
  619. bf = fb
  620. } else {
  621. consumedArgs = 1
  622. bf = f
  623. }
  624. } else {
  625. f, err := findBuildfile("build.json")
  626. fail(err)
  627. bf = f
  628. }
  629. re := []*regexp.Regexp{regexp.MustCompile(".")}
  630. if len(flag.Args()) > consumedArgs {
  631. re = re[:0]
  632. for _, r := range flag.Args()[consumedArgs:] {
  633. rx, err := regexp.Compile(r)
  634. fail(err)
  635. re = append(re, rx)
  636. }
  637. }
  638. project(bf, re)
  639. }
  640. func findTools(toolprefix string) {
  641. var err error
  642. for k, v := range tools {
  643. if x := os.Getenv(strings.ToUpper(k)); x != "" {
  644. v = x
  645. }
  646. v, err = exec.LookPath(toolprefix + v)
  647. fail(err)
  648. tools[k] = v
  649. }
  650. }
  651. func isDir(f string) (bool, error) {
  652. fi, err := os.Stat(f)
  653. if err != nil {
  654. return false, err
  655. }
  656. return fi.IsDir(), nil
  657. }
  658. // disambiguate the buildfile argument
  659. func findBuildfile(f string) (string, error) {
  660. if !strings.HasSuffix(f, ".json") {
  661. b, err := isDir(f)
  662. if err != nil {
  663. // the path didn't exist
  664. return "", errors.New("unable to find buildfile " + f)
  665. }
  666. if b {
  667. f = path.Join(f, "build.json")
  668. } else {
  669. // this is a file without .json suffix
  670. return "", errors.New("buildfile must be a .json file, " + f + " is not a valid name")
  671. }
  672. }
  673. if b, err := isDir(f); err != nil || b {
  674. return "", errors.New("unable to find buildfile " + f)
  675. }
  676. return f, nil
  677. }