PageRenderTime 27ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/cmplr/compiler.go

https://code.google.com/p/godag/
Go | 644 lines | 477 code | 134 blank | 33 comment | 145 complexity | c80d610158fea60e85fb45a1bcb91c17 MD5 | raw file
Possible License(s): GPL-3.0
  1. // Copyright Š 2009 bjarneh
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. package compiler
  16. import (
  17. "cmplr/dag"
  18. "fmt"
  19. "log"
  20. "os"
  21. "os/exec"
  22. "path/filepath"
  23. "regexp"
  24. "strings"
  25. "utilz/global"
  26. "utilz/handy"
  27. "utilz/say"
  28. "utilz/stringset"
  29. "utilz/walker"
  30. )
  31. var includes []string
  32. var srcroot string
  33. var libroot string
  34. var pathLinker string
  35. var pathCompiler string
  36. var suffix string
  37. func Init(srcdir string, include []string) {
  38. srcroot = srcdir
  39. includes = include
  40. if global.GetString("-lib") != "" {
  41. libroot = global.GetString("-lib")
  42. } else {
  43. libroot = srcroot
  44. }
  45. InitBackend()
  46. }
  47. func InitBackend(){
  48. switch global.GetString("-backend") {
  49. case "gcc", "gccgo":
  50. gcc()
  51. case "gc":
  52. gc()
  53. case "express":
  54. express()
  55. default:
  56. log.Fatalf("[ERROR] '%s' unknown backend\n",
  57. global.GetString("-backend"))
  58. }
  59. }
  60. func express() {
  61. var err error
  62. pathCompiler, err = exec.LookPath("vmgc")
  63. if err != nil {
  64. log.Fatalf("[ERROR] could not find compiler: %s\n", pathCompiler)
  65. }
  66. pathLinker, err = exec.LookPath("vmld")
  67. if err != nil {
  68. log.Fatalf("[ERROR] could not find linker: %s\n", pathLinker)
  69. }
  70. suffix = ".vmo"
  71. }
  72. //TODO fix this mess
  73. func gc() {
  74. var (
  75. A string // A:architecture
  76. S string // S:suffix
  77. C string // C:compiler
  78. L string // L:linker
  79. R string // R:goroot
  80. O string // O:goOS
  81. )
  82. var err error
  83. A = handy.GOARCH()
  84. R = handy.GOROOT()
  85. O = handy.GOOS()
  86. switch A {
  87. case "arm":
  88. S = ".5"
  89. C = "5g"
  90. L = "5l"
  91. case "amd64":
  92. S = ".6"
  93. C = "6g"
  94. L = "6l"
  95. case "386":
  96. S = ".8"
  97. C = "8g"
  98. L = "8l"
  99. default:
  100. log.Fatalf("[ERROR] unknown architecture: %s\n", A)
  101. }
  102. path_C := filepath.Join(R, "pkg", "tool", (O + "_" + A), C)
  103. pathCompiler, err = exec.LookPath(path_C)
  104. if err != nil {
  105. log.Fatalf("[ERROR] could not find compiler: %s\n", C)
  106. }
  107. path_L := filepath.Join(R, "pkg", "tool", (O + "_" + A), L)
  108. pathLinker, err = exec.LookPath(path_L)
  109. if err != nil {
  110. log.Fatalf("[ERROR] could not find linker: %s\n", L)
  111. }
  112. suffix = S
  113. }
  114. func gcc() {
  115. var err error
  116. pathCompiler, err = exec.LookPath("gccgo")
  117. if err != nil {
  118. log.Fatalf("[ERROR] could not find compiler: %s\n", err)
  119. }
  120. pathLinker = pathCompiler
  121. suffix = ".o"
  122. }
  123. func CreateArgv(pkgs []*dag.Package) {
  124. var argv []string
  125. includeLen := len(includes)
  126. for y := 0; y < len(pkgs); y++ {
  127. argv = make([]string, 0)
  128. argv = append(argv, pathCompiler)
  129. argv = append(argv, "-I")
  130. argv = append(argv, libroot)
  131. for y := 0; y < includeLen; y++ {
  132. argv = append(argv, "-I")
  133. argv = append(argv, includes[y])
  134. }
  135. golibs := handy.GoPathImports(global.GetString("-backend"))
  136. for j := 0; j < len(golibs); j++ {
  137. argv = append(argv, "-I")
  138. argv = append(argv, golibs[j])
  139. }
  140. switch global.GetString("-backend") {
  141. case "gcc", "gccgo":
  142. argv = append(argv, "-c")
  143. }
  144. argv = append(argv, "-o")
  145. argv = append(argv, filepath.Join(libroot, pkgs[y].Name)+suffix)
  146. for z := 0; z < len(pkgs[y].Files); z++ {
  147. argv = append(argv, pkgs[y].Files[z])
  148. }
  149. pkgs[y].Argv = argv
  150. }
  151. }
  152. func CreateLibArgv(pkgs []*dag.Package) {
  153. ss := stringset.New()
  154. for i := range pkgs {
  155. if len(pkgs[i].Name) > len(pkgs[i].ShortName) {
  156. ss.Add(pkgs[i].Name[:(len(pkgs[i].Name) - len(pkgs[i].ShortName))])
  157. }
  158. }
  159. slice := ss.Slice()
  160. for i := 0; i < len(slice); i++ {
  161. slice[i] = filepath.Join(libroot, slice[i])
  162. handy.DirOrMkdir(slice[i])
  163. }
  164. CreateArgv(pkgs)
  165. }
  166. func Dryrun(pkgs []*dag.Package) {
  167. binary := filepath.Base(pathCompiler)
  168. for y := 0; y < len(pkgs); y++ {
  169. args := strings.Join(pkgs[y].Argv[1:], " ")
  170. fmt.Printf("%s %s || exit 1\n", binary, args)
  171. }
  172. }
  173. // this is faster than ParallelCompile i.e. (the old version).
  174. // after release.r60.1 this is used for all compile jobs
  175. func Compile(pkgs []*dag.Package) bool {
  176. // set indegree, i.e. how many jobs to wait for
  177. for y := 0; y < len(pkgs); y++ {
  178. pkgs[y].ResetIndegree()
  179. }
  180. // init waitgroup to number of jobs calculated above
  181. for y := 0; y < len(pkgs); y++ {
  182. pkgs[y].InitWaitGroup()
  183. }
  184. // start up one go-routine for each package
  185. ch := make(chan int)
  186. for y := 0; y < len(pkgs); y++ {
  187. go pkgs[y].Compile(ch)
  188. }
  189. // make sure all jobs finished, i.e. drain channel
  190. for y := 0; y < len(pkgs); y++ {
  191. _ = <-ch
  192. }
  193. close(ch)
  194. return !dag.OldPkgYet()
  195. }
  196. // for removal of temoprary packages created for testing and so on..
  197. func DeletePackages(pkgs []*dag.Package) bool {
  198. var ok = true
  199. for i := 0; i < len(pkgs); i++ {
  200. for y := 0; y < len(pkgs[i].Files); y++ {
  201. handy.Delete(pkgs[i].Files[y], false)
  202. }
  203. if !global.GetBool("-dryrun") {
  204. pcompile := filepath.Join(libroot, pkgs[i].Name) + suffix
  205. ok = handy.Delete(pcompile, false)
  206. }
  207. }
  208. return ok
  209. }
  210. // Recompile packages that contain test files (*_test.go),
  211. // i.e. test-code should not be part of the packages after compilation.
  212. func ReCompile(pkgs []*dag.Package) bool {
  213. var doRecompile bool
  214. for i := 0; i < len(pkgs); i++ {
  215. if pkgs[i].HasTestAndInit() {
  216. doRecompile = true
  217. }
  218. }
  219. return doRecompile
  220. }
  221. //TODO rewrite the whole link stuff, make i run in parallel
  222. func ForkLinkAll(pkgs []*dag.Package, up2date bool) {
  223. mainPkgs := make([]*dag.Package, 0)
  224. for i := 0; i < len(pkgs); i++ {
  225. if pkgs[i].ShortName == "main" {
  226. mainPkgs = append(mainPkgs, pkgs[i])
  227. }
  228. }
  229. if len(mainPkgs) == 0 {
  230. log.Fatal("[ERROR] (linking) no main package found\n")
  231. }
  232. handy.DirOrMkdir("bin")
  233. for i := 0; i < len(mainPkgs); i++ {
  234. toks := strings.Split(mainPkgs[i].Name, "/")
  235. // do this for main packages which are placed in directories
  236. // if toks < 2 this cannot be true, i.e. then the main package
  237. // lives under the src-root and cannot be filtered
  238. if len(toks) >= 2 {
  239. nameOfBinary := toks[len(toks)-2]
  240. pathToBinary := filepath.Join("bin", nameOfBinary)
  241. global.SetString("-main", nameOfBinary)
  242. ForkLink(pathToBinary, pkgs, nil, up2date)
  243. }
  244. }
  245. }
  246. func ForkLink(output string, pkgs []*dag.Package, extra []*dag.Package, up2date bool) {
  247. var mainPKG *dag.Package
  248. gotMain := make([]*dag.Package, 0)
  249. for i := 0; i < len(pkgs); i++ {
  250. if pkgs[i].ShortName == "main" {
  251. gotMain = append(gotMain, pkgs[i])
  252. }
  253. }
  254. if len(gotMain) == 0 {
  255. log.Fatal("[ERROR] (linking) no main package found\n")
  256. }
  257. if len(gotMain) > 1 {
  258. choice := mainChoice(gotMain)
  259. mainPKG = gotMain[choice]
  260. } else {
  261. mainPKG = gotMain[0]
  262. }
  263. compiled := filepath.Join(libroot, mainPKG.Name) + suffix
  264. if up2date && !global.GetBool("-dryrun") && handy.IsFile(output) {
  265. if handy.ModifyTimestamp(compiled) < handy.ModifyTimestamp(output) {
  266. say.Printf("up 2 date: %s\n", output)
  267. return
  268. }
  269. }
  270. argv := make([]string, 0)
  271. argv = append(argv, pathLinker)
  272. switch global.GetString("-backend") {
  273. case "gc", "express":
  274. argv = append(argv, "-L")
  275. argv = append(argv, libroot)
  276. if global.GetString("-backend") == "gc" {
  277. golibs := handy.GoPathImports("gc")
  278. for j := 0; j < len(golibs); j++ {
  279. argv = append(argv, "-L")
  280. argv = append(argv, golibs[j])
  281. }
  282. if global.GetBool("-strip") {
  283. argv = append(argv, "-s")
  284. }
  285. }
  286. }
  287. argv = append(argv, "-o")
  288. argv = append(argv, output)
  289. // gcc get's this no matter what...
  290. if global.GetString("-backend") == "gcc" ||
  291. global.GetString("-backend") == "gccgo" {
  292. argv = append(argv, "-static")
  293. } else if global.GetBool("-static") {
  294. argv = append(argv, "-d")
  295. }
  296. switch global.GetString("-backend") {
  297. case "gccgo", "gcc":
  298. walker.IncludeFile = func(s string) bool {
  299. return strings.HasSuffix(s, ".o")
  300. }
  301. walker.IncludeDir = func(s string) bool { return true }
  302. for y := 0; y < len(includes); y++ {
  303. argv = append(argv, walker.PathWalk(includes[y])...)
  304. }
  305. case "gc", "express":
  306. for y := 0; y < len(includes); y++ {
  307. argv = append(argv, "-L")
  308. argv = append(argv, includes[y])
  309. }
  310. }
  311. argv = append(argv, compiled)
  312. if global.GetString("-backend") == "gcc" ||
  313. global.GetString("-backend") == "gccgo" {
  314. ss := stringset.New()
  315. if len(extra) > 0 {
  316. for j := 0; j < len(extra); j++ {
  317. // main package untestable using GCC
  318. if extra[j].ShortName != "main" {
  319. ss.Add(filepath.Join(libroot, extra[j].Name) + suffix)
  320. }
  321. }
  322. } else {
  323. for k := 0; k < len(pkgs); k++ {
  324. ss.Add(filepath.Join(libroot, pkgs[k].Name) + suffix)
  325. }
  326. ss.Remove(compiled)
  327. }
  328. if ss.Len() > 0 {
  329. argv = append(argv, ss.Slice()...)
  330. }
  331. }
  332. if global.GetBool("-dryrun") {
  333. linker := filepath.Base(pathLinker)
  334. fmt.Printf("%s %s || exit 1\n", linker, strings.Join(argv[1:], " "))
  335. } else {
  336. say.Println("linking :", output)
  337. handy.StdExecve(argv, true)
  338. }
  339. }
  340. func mainChoice(pkgs []*dag.Package) int {
  341. var cnt int
  342. var choice int
  343. for i := 0; i < len(pkgs); i++ {
  344. ok, _ := regexp.MatchString(global.GetString("-main"), pkgs[i].Name)
  345. if ok {
  346. cnt++
  347. choice = i
  348. }
  349. }
  350. if cnt == 1 {
  351. return choice
  352. }
  353. fmt.Println("\n More than one main package found\n")
  354. for i := 0; i < len(pkgs); i++ {
  355. fmt.Printf(" type %2d for: %s\n", i, pkgs[i].Name)
  356. }
  357. fmt.Printf("\n type your choice: ")
  358. n, e := fmt.Scanf("%d", &choice)
  359. if e != nil {
  360. log.Fatalf("%s\n", e)
  361. }
  362. if n != 1 {
  363. log.Fatal("failed to read input\n")
  364. }
  365. if choice >= len(pkgs) || choice < 0 {
  366. log.Fatalf(" bad choice: %d\n", choice)
  367. }
  368. fmt.Printf(" chosen main-package: %s\n\n", pkgs[choice].Name)
  369. return choice
  370. }
  371. func CreateTestArgv() []string {
  372. pwd, e := os.Getwd()
  373. if e != nil {
  374. log.Fatal("[ERROR] could not locate working directory\n")
  375. }
  376. argv := make([]string, 0)
  377. if global.GetString("-backend") == "express" {
  378. vmrun, e := exec.LookPath("vmrun")
  379. if e != nil {
  380. log.Fatalf("[ERROR] %s\n", e)
  381. }
  382. argv = append(argv, vmrun)
  383. }
  384. argv = append(argv, filepath.Join(pwd, global.GetString("-test-bin")))
  385. if global.GetString("-bench") != "" {
  386. argv = append(argv, "-test.bench")
  387. argv = append(argv, global.GetString("-bench"))
  388. } else if global.GetString("-test.bench") != "" {
  389. argv = append(argv, "-test.bench")
  390. argv = append(argv, global.GetString("-test.bench"))
  391. }
  392. if global.GetString("-match") != "" {
  393. argv = append(argv, "-test.run")
  394. argv = append(argv, global.GetString("-match"))
  395. } else if global.GetString("-test.run") != "" {
  396. argv = append(argv, "-test.run")
  397. argv = append(argv, global.GetString("-test.run"))
  398. }
  399. if global.GetString("-test.timeout") != "" {
  400. argv = append(argv, "-test.timeout")
  401. argv = append(argv, global.GetString("-test.timeout"))
  402. }
  403. if global.GetString("-test.benchtime") != "" {
  404. argv = append(argv, "-test.benchtime")
  405. argv = append(argv, global.GetString("-test.benchtime"))
  406. }
  407. if global.GetString("-test.parallel") != "" {
  408. argv = append(argv, "-test.parallel")
  409. argv = append(argv, global.GetString("-test.parallel"))
  410. }
  411. if global.GetString("-test.cpu") != "" {
  412. argv = append(argv, "-test.cpu")
  413. argv = append(argv, global.GetString("-test.cpu"))
  414. }
  415. if global.GetString("-test.cpuprofile") != "" {
  416. argv = append(argv, "-test.cpuprofile")
  417. argv = append(argv, global.GetString("-test.cpuprofile"))
  418. }
  419. if global.GetString("-test.memprofile") != "" {
  420. argv = append(argv, "-test.memprofile")
  421. argv = append(argv, global.GetString("-test.memprofile"))
  422. }
  423. if global.GetString("-test.memprofilerate") != "" {
  424. argv = append(argv, "-test.memprofilerate")
  425. argv = append(argv, global.GetString("-test.memprofilerate"))
  426. }
  427. if global.GetBool("-verbose") || global.GetBool("-test.v") {
  428. argv = append(argv, "-test.v")
  429. }
  430. if global.GetBool("-test.short") {
  431. argv = append(argv, "-test.short")
  432. }
  433. return argv
  434. }
  435. func FormatFiles(files []string) {
  436. var i int
  437. var argv []string
  438. var tabWidth string = "-tabwidth=4"
  439. var useTabs string = "-tabs=false"
  440. var rewRule string = global.GetString("-rew-rule")
  441. var fmtexec string
  442. var err error
  443. fmtexec, err = exec.LookPath("gofmt")
  444. if err != nil {
  445. log.Fatal("[ERROR] could not find 'gofmt' in $PATH")
  446. }
  447. if global.GetString("-tabwidth") != "" {
  448. tabWidth = "-tabwidth=" + global.GetString("-tabwidth")
  449. }
  450. if global.GetBool("-tab") {
  451. useTabs = "-tabs=true"
  452. }
  453. argv = make([]string, 0)
  454. argv = append(argv, fmtexec)
  455. argv = append(argv, "-w=true")
  456. argv = append(argv, tabWidth)
  457. argv = append(argv, useTabs)
  458. if rewRule != "" {
  459. argv = append(argv, fmt.Sprintf("-r='%s'", rewRule))
  460. }
  461. argv = append(argv, "") // dummy
  462. i = len(argv) - 1
  463. for y := 0; y < len(files); y++ {
  464. argv[i] = files[y]
  465. if global.GetBool("-dryrun") {
  466. argv[0] = filepath.Base(argv[0])
  467. fmt.Printf(" %s\n", strings.Join(argv, " "))
  468. } else {
  469. say.Printf("gofmt: %s\n", files[y])
  470. _ = handy.StdExecve(argv, true)
  471. }
  472. }
  473. }
  474. func DeleteObjects(dir string, pkgs []*dag.Package) {
  475. var stub, tmp string
  476. suffixes := []string{".8", ".6", ".5", ".o", ".vmo"}
  477. libdir := global.GetString("-lib")
  478. if libdir != "" {
  479. dir = libdir
  480. }
  481. for i := 0; i < len(pkgs); i++ {
  482. stub = filepath.Join(dir, pkgs[i].Name)
  483. for j := 0; j < len(suffixes); j++ {
  484. tmp = stub + suffixes[j]
  485. if handy.IsFile(tmp) {
  486. if global.GetBool("-dryrun") {
  487. say.Printf("[dryrun] rm: %s\n", tmp)
  488. } else {
  489. say.Printf("rm: %s\n", tmp)
  490. handy.Delete(tmp, false)
  491. }
  492. }
  493. }
  494. }
  495. // remove entire dir if empty after objects are deleted.
  496. // only do this if -lib is present, there is no reason to
  497. // do this (extra treewalk) if objects are in src directory
  498. if libdir != "" && handy.IsDir(dir) {
  499. walker.IncludeFile = func(s string) bool { return true }
  500. walker.IncludeDir = func(s string) bool { return true }
  501. if len(walker.PathWalk(dir)) == 0 {
  502. if global.GetBool("-dryrun") {
  503. fmt.Printf("[dryrun] rm: %s\n", dir)
  504. } else {
  505. say.Printf("rm: %s\n", dir)
  506. handy.RmRf(dir, true) // die on error
  507. }
  508. }
  509. }
  510. }