PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/cmplr/dag.go

https://code.google.com/p/godag/
Go | 929 lines | 701 code | 182 blank | 46 comment | 178 complexity | 0df5cc9cb77c7d241fd3ec2b1117e2fe 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 dag
  16. import (
  17. "fmt"
  18. "go/ast"
  19. "go/parser"
  20. "go/token"
  21. "log"
  22. "os"
  23. "path/filepath"
  24. "regexp"
  25. "strings"
  26. "sync"
  27. "time"
  28. "utilz/global"
  29. "utilz/handy"
  30. "utilz/say"
  31. "utilz/stringbuffer"
  32. "utilz/stringset"
  33. )
  34. var locker = new(sync.Mutex)
  35. var oldPkgFound bool // false
  36. type Dag map[string]*Package // package-name -> Package object
  37. type Package struct {
  38. Indegree int
  39. Name, ShortName string // absolute path, basename
  40. Argv []string // command needed to compile package
  41. Files []string // relative path of files
  42. dependencies *stringset.StringSet
  43. children []*Package // packages that depend on this
  44. waiter *sync.WaitGroup
  45. needsCompile bool
  46. lock *sync.Mutex
  47. }
  48. type TestCollector struct {
  49. TestFuncs []string
  50. BenchFuncs []string
  51. ExampleFuncs []string
  52. }
  53. type initCollector struct {
  54. hasInit bool
  55. }
  56. func New() Dag {
  57. return make(map[string]*Package)
  58. }
  59. func newPackage() *Package {
  60. p := new(Package)
  61. p.Indegree = 0
  62. p.Files = make([]string, 0)
  63. p.dependencies = stringset.New()
  64. p.children = make([]*Package, 0)
  65. p.waiter = nil
  66. p.needsCompile = false // yeah yeah..
  67. p.lock = new(sync.Mutex)
  68. return p
  69. }
  70. func newTestCollector() *TestCollector {
  71. t := new(TestCollector)
  72. t.TestFuncs = make([]string, 0)
  73. t.BenchFuncs = make([]string, 0)
  74. t.ExampleFuncs = make([]string, 0)
  75. return t
  76. }
  77. func (d Dag) Parse(root string, files []string) {
  78. root = addSeparatorPath(root)
  79. var e, pkgname string
  80. for i := 0; i < len(files); i++ {
  81. e = files[i]
  82. tree := getSyntaxTreeOrDie(e, parser.ImportsOnly)
  83. dir, _ := filepath.Split(e)
  84. unroot := dir[len(root):len(dir)]
  85. shortname := tree.Name.String()
  86. // if package name == directory name -> assume stdlib organizing
  87. if len(unroot) > 1 && filepath.Base(dir) == shortname {
  88. pkgname = unroot[:len(unroot)-1]
  89. } else {
  90. pkgname = filepath.Join(unroot, shortname)
  91. }
  92. pkgname = filepath.ToSlash(pkgname)
  93. _, ok := d[pkgname]
  94. if !ok {
  95. d[pkgname] = newPackage()
  96. d[pkgname].Name = pkgname
  97. d[pkgname].ShortName = shortname
  98. }
  99. ast.Walk(d[pkgname], tree)
  100. d[pkgname].Files = append(d[pkgname].Files, e)
  101. }
  102. }
  103. func (d Dag) addEdge(from, to string) {
  104. fromNode := d[from]
  105. toNode := d[to]
  106. fromNode.children = append(fromNode.children, toNode)
  107. toNode.Indegree++
  108. }
  109. // note that nothing is done in order to check if dependencies
  110. // are valid if they are not part of the actual source-tree.
  111. func (d Dag) GraphBuilder() {
  112. for k, v := range d {
  113. for dep := range v.dependencies.Iter() {
  114. if d.localDependency(dep) {
  115. d.addEdge(dep, k)
  116. ///fmt.Printf("local: %s \n", dep);
  117. }
  118. }
  119. }
  120. }
  121. func (d Dag) Alien() (set *stringset.StringSet) {
  122. set = stringset.New()
  123. for _, v := range d {
  124. for dep := range v.dependencies.Iter() {
  125. if !d.localDependency(dep) {
  126. set.Add(dep)
  127. }
  128. }
  129. }
  130. for u := range set.Iter() {
  131. if !seemsExternal(u) {
  132. set.Remove(u)
  133. }
  134. }
  135. return set
  136. }
  137. func (d Dag) External() {
  138. var argv []string
  139. var set *stringset.StringSet
  140. var i int = 0
  141. set = d.Alien()
  142. argv = make([]string, 0)
  143. argv = append(argv, "go")
  144. argv = append(argv, "get")
  145. if global.GetBool("-verbose") {
  146. argv = append(argv, "-v")
  147. }
  148. argv = append(argv, "-u")
  149. argv = append(argv, "-a")
  150. i = len(argv)
  151. argv = append(argv, "dummy")
  152. for u := range set.Iter() {
  153. argv[i] = u
  154. if global.GetBool("-dryrun") {
  155. fmt.Printf("%s || exit 1\n", strings.Join(argv, " "))
  156. } else {
  157. say.Printf("go get: %s\n", u)
  158. handy.StdExecve(argv, true)
  159. }
  160. }
  161. }
  162. // If import starts with one of these, it seems legal...
  163. //
  164. // bitbucket.org/
  165. // github.com/
  166. // [^.]+\.googlecode\.com/
  167. // launchpad.net/
  168. func seemsExternal(imprt string) bool {
  169. if strings.HasPrefix(imprt, "bitbucket.org/") {
  170. return true
  171. } else if strings.HasPrefix(imprt, "github.com/") {
  172. return true
  173. } else if strings.HasPrefix(imprt, "launchpad.net/") {
  174. return true
  175. } else if strings.HasPrefix(imprt, "code.google.com/") {
  176. return true
  177. }
  178. ok, _ := regexp.MatchString("[^.]\\.googlecode\\.com\\/.*", imprt)
  179. return ok
  180. }
  181. func (d Dag) MakeDotGraph(filename string) {
  182. var file *os.File
  183. var fileinfo os.FileInfo
  184. var e error
  185. var sb *stringbuffer.StringBuffer
  186. fileinfo, e = os.Stat(filename)
  187. if e == nil {
  188. if !fileinfo.IsDir() {
  189. e = os.Remove(fileinfo.Name())
  190. if e != nil {
  191. log.Fatalf("[ERROR] failed to remove: %s\n", filename)
  192. }
  193. }
  194. }
  195. sb = stringbuffer.NewSize(500)
  196. file, e = os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0644)
  197. if e != nil {
  198. log.Fatalf("[ERROR] %s\n", e)
  199. }
  200. sb.Add("digraph depgraph {\n\trankdir=LR;\n")
  201. for _, v := range d {
  202. v.DotGraph(sb)
  203. }
  204. sb.Add("}\n")
  205. file.WriteString(sb.String())
  206. file.Close()
  207. }
  208. func (d Dag) MakeMainTest(root string) ([]*Package, string, string) {
  209. var (
  210. i int
  211. lname string
  212. sname string
  213. tmplib string
  214. tmpdir string
  215. tmpstub string
  216. tmpfile string
  217. collector *TestCollector
  218. )
  219. sbImports := stringbuffer.NewSize(300)
  220. imprtSet := stringset.New()
  221. sbTests := stringbuffer.NewSize(1000)
  222. sbBench := stringbuffer.NewSize(1000)
  223. sbExample := stringbuffer.NewSize(1000)
  224. sbImports.Add("\n// autogenerated code\n\n")
  225. sbImports.Add("package main\n\n")
  226. imprtSet.Add("import \"regexp\"\n")
  227. imprtSet.Add("import \"testing\"\n")
  228. sbTests.Add("\n\nvar tests = []testing.InternalTest{\n")
  229. sbBench.Add("\n\nvar benchmarks = []testing.InternalBenchmark{\n")
  230. sbExample.Add("\n\nvar examples = []testing.InternalExample{\n")
  231. for _, v := range d {
  232. sname = v.ShortName
  233. lname = v.ShortName
  234. collector = newTestCollector()
  235. if strings.HasSuffix(v.ShortName, "_test") {
  236. for i = 0; i < len(v.Files); i++ {
  237. tree := getSyntaxTreeOrDie(v.Files[i], parser.ParseComments)
  238. ast.Walk(collector, tree)
  239. }
  240. } else {
  241. for i = 0; i < len(v.Files); i++ {
  242. if strings.HasSuffix(v.Files[i], "_test.go") {
  243. tree := getSyntaxTreeOrDie(v.Files[i], parser.ParseComments)
  244. ast.Walk(collector, tree)
  245. }
  246. }
  247. }
  248. if collector.FoundAnything() {
  249. if hasSlash(v.Name) {
  250. lname = removeSlashAndDot(v.Name)
  251. imprtSet.Add(fmt.Sprintf("import %s \"%s\"\n", lname, v.Name))
  252. } else {
  253. imprtSet.Add(fmt.Sprintf("import \"%s\"\n", v.Name))
  254. }
  255. // add tests
  256. for i := 0; i < len(collector.TestFuncs); i++ {
  257. fn := collector.TestFuncs[i]
  258. sbTests.Add(fmt.Sprintf(
  259. "testing.InternalTest{\"%s.%s\", %s.%s },\n",
  260. sname, fn, lname, fn))
  261. }
  262. // add benchmarks
  263. for i := 0; i < len(collector.BenchFuncs); i++ {
  264. fn := collector.BenchFuncs[i]
  265. sbBench.Add(fmt.Sprintf(
  266. "testing.InternalBenchmark{\"%s.%s\", %s.%s },\n",
  267. sname, fn, lname, fn))
  268. }
  269. // add examples ( not really )
  270. for i := 0; i < len(collector.ExampleFuncs); i++ {
  271. // fn := collector.ExampleFuncs[i] //TODO add comment which seems to be what we compare against..
  272. // sbExample.Add(fmt.Sprintf("testing.InternalExample{\"%s.%s\", %s.%s },\n", sname, fn, lname, fn))
  273. }
  274. }
  275. }
  276. sbTests.Add("};\n")
  277. sbBench.Add("};\n")
  278. sbExample.Add("};\n")
  279. for im := range imprtSet.Iter() {
  280. sbImports.Add(im)
  281. }
  282. sbTotal := stringbuffer.NewSize(sbImports.Len() +
  283. sbTests.Len() +
  284. sbBench.Len() + 100)
  285. sbTotal.Add(sbImports.String())
  286. sbTotal.Add(sbTests.String())
  287. sbTotal.Add(sbBench.String())
  288. sbTotal.Add(sbExample.String())
  289. sbTotal.Add("func main(){\n")
  290. sbTotal.Add("testing.Main(regexp.MatchString, tests, benchmarks, examples);\n}\n\n")
  291. tmpstub = fmt.Sprintf("tmp%d", time.Now().Unix())
  292. tmpdir = filepath.Join(root, tmpstub)
  293. if global.GetString("-lib") != "" {
  294. tmplib = filepath.Join(global.GetString("-lib"), tmpstub)
  295. }
  296. dir, e1 := os.Stat(tmpdir)
  297. if e1 == nil && dir.IsDir() {
  298. log.Printf("[ERROR] directory: %s already exists\n", tmpdir)
  299. } else {
  300. e_mk := os.Mkdir(tmpdir, 0777)
  301. if e_mk != nil {
  302. log.Fatal("[ERROR] failed to create directory for testing")
  303. }
  304. }
  305. tmpfile = filepath.Join(tmpdir, "_main.go")
  306. fil, e2 := os.OpenFile(tmpfile, os.O_WRONLY|os.O_CREATE, 0777)
  307. if e2 != nil {
  308. log.Fatalf("[ERROR] %s\n", e2)
  309. }
  310. n, e3 := fil.WriteString(sbTotal.String())
  311. if e3 != nil {
  312. log.Fatalf("[ERROR] %s\n", e3)
  313. } else if n != sbTotal.Len() {
  314. log.Fatal("[ERROR] failed to write test")
  315. }
  316. fil.Close()
  317. p := newPackage()
  318. p.Name = filepath.Join(tmpstub, "main")
  319. p.ShortName = "main"
  320. p.Files = append(p.Files, tmpfile)
  321. vec := make([]*Package, 1)
  322. vec[0] = p
  323. return vec, tmpdir, tmplib
  324. }
  325. func (d Dag) Topsort() []*Package {
  326. var node, child *Package
  327. var cnt int = 0
  328. zero := make([]*Package, 0)
  329. done := make([]*Package, 0)
  330. for _, v := range d {
  331. if v.Indegree == 0 {
  332. zero = append(zero, v)
  333. }
  334. }
  335. for len(zero) > 0 {
  336. node = zero[0]
  337. zero = zero[1:] // Pop
  338. for i := 0; i < len(node.children); i++ {
  339. child = node.children[i]
  340. child.Indegree--
  341. if child.Indegree == 0 {
  342. zero = append(zero, child)
  343. }
  344. }
  345. cnt++
  346. done = append(done, node)
  347. }
  348. if cnt < len(d) {
  349. log.Fatal("[ERROR] loop in dependency graph\n")
  350. }
  351. return done
  352. }
  353. func (d Dag) localDependency(dep string) bool {
  354. _, ok := d[dep]
  355. return ok
  356. }
  357. func (d Dag) PrintInfo() {
  358. var i int
  359. fmt.Println("--------------------------------------")
  360. fmt.Println("Packages and Dependencies")
  361. fmt.Println("p = package, f = file, d = dependency ")
  362. fmt.Println("--------------------------------------\n")
  363. for k, v := range d {
  364. fmt.Println("p ", k)
  365. for i = 0; i < len(v.Files); i++ {
  366. fmt.Println("f ", v.Files[i])
  367. }
  368. for ds := range v.dependencies.Iter() {
  369. fmt.Println("d ", ds)
  370. }
  371. fmt.Println("")
  372. }
  373. }
  374. func (p *Package) DotGraph(sb *stringbuffer.StringBuffer) {
  375. if p.dependencies.Len() == 0 {
  376. sb.Add(fmt.Sprintf("\t\"%s\";\n", p.Name))
  377. } else {
  378. for dep := range p.dependencies.Iter() {
  379. sb.Add(fmt.Sprintf("\t\"%s\" -> \"%s\";\n", p.Name, dep))
  380. }
  381. }
  382. }
  383. // if a package contains test-files and regular files, and on top
  384. // of that contains an 'init' function inside the test-files; we
  385. // have to recompile that package and its recursive dependencies
  386. // to avoid dragging the test-code into the produced binaries and
  387. // libraries that depends on this package. thanks to seth.bunce@gm..
  388. // for reporting this issue.
  389. func (p *Package) HasTestAndInit() (recompile bool) {
  390. var (
  391. testFile bool = false
  392. plainFile bool = false
  393. plainArgv []string
  394. plainFiles []string
  395. )
  396. p.Indegree = 0
  397. for y := 0; y < len(p.Files); y++ {
  398. if strings.HasSuffix(p.Files[y], "_test.go") {
  399. testFile = true
  400. } else {
  401. plainFile = true
  402. }
  403. }
  404. if testFile && plainFile {
  405. for j := 0; j < len(p.Files); j++ {
  406. if strings.HasSuffix(p.Files[j], "_test.go") {
  407. collector := &initCollector{hasInit: false}
  408. tree := getSyntaxTreeOrDie(p.Files[j], 0)
  409. ast.Walk(collector, tree)
  410. if collector.hasInit {
  411. recompile = true
  412. }
  413. }
  414. }
  415. }
  416. // strip test-files from package during recompile, we
  417. // 'touch' a file inside the package in order to make godag
  418. // recompile the package (it won't be up2date any longer)
  419. if recompile {
  420. plainArgv = make([]string, 0)
  421. plainFiles = make([]string, 0)
  422. for j := 0; j < len(p.Argv); j++ {
  423. if !strings.HasSuffix(p.Argv[j], "_test.go") {
  424. plainArgv = append(plainArgv, p.Argv[j])
  425. }
  426. }
  427. for j := 0; j < len(p.Files); j++ {
  428. if !strings.HasSuffix(p.Files[j], "_test.go") {
  429. plainFiles = append(plainFiles, p.Files[j])
  430. handy.Touch(p.Files[j])
  431. }
  432. }
  433. p.Argv = plainArgv
  434. p.Files = plainFiles
  435. }
  436. return recompile
  437. }
  438. func (p *Package) Rep() string {
  439. sb := make([]string, 0)
  440. sb = append(sb, "&Package{")
  441. sb = append(sb, " name: \""+p.ShortName+"\",")
  442. sb = append(sb, " full: \""+p.Name+"\",")
  443. sb = append(sb, " output: \"_obj/"+p.Name+"\",")
  444. // special case: build from PWD (srcdir == .)
  445. files := make([]string, len(p.Files))
  446. for i := 0; i < len(p.Files); i++ {
  447. files[i] = p.Files[i]
  448. }
  449. pwd, e := os.Getwd()
  450. if e == nil {
  451. pwd = pwd + string(filepath.Separator)
  452. for i := 0; i < len(files); i++ {
  453. if strings.HasPrefix(files[i], pwd) {
  454. files[i] = files[i][len(pwd):]
  455. }
  456. }
  457. }
  458. fs := make([]string, 0)
  459. for i := 0; i < len(p.Files); i++ {
  460. fs = append(fs, "\""+filepath.ToSlash(files[i])+"\"")
  461. }
  462. sb = append(sb, " files: []string{"+strings.Join(fs, ",")+"},")
  463. sb = append(sb, "},\n")
  464. for i := 0; i < len(sb); i++ {
  465. sb[i] = " " + sb[i]
  466. }
  467. return strings.Join(sb, "\n")
  468. }
  469. func (p *Package) UpToDate() bool {
  470. if p.Argv == nil {
  471. log.Fatalf("[ERROR] missing dag.Package.Argv\n")
  472. }
  473. var e error
  474. var finfo os.FileInfo
  475. var compiledModifiedTime int64
  476. var last, stop, i int
  477. var resultingFile string
  478. last = len(p.Argv) - 1
  479. stop = last - len(p.Files)
  480. resultingFile = p.Argv[stop]
  481. finfo, e = os.Stat(resultingFile)
  482. if e != nil {
  483. return false
  484. } else {
  485. compiledModifiedTime = finfo.ModTime().UnixNano()
  486. }
  487. for i = last; i > stop; i-- {
  488. finfo, e = os.Stat(p.Argv[i])
  489. if e != nil {
  490. panic(fmt.Sprintf("Missing go file: %s\n", p.Argv[i]))
  491. } else {
  492. if finfo.ModTime().UnixNano() > compiledModifiedTime {
  493. return false
  494. }
  495. }
  496. }
  497. // package contains _test.go and -test => not UpToDate
  498. if global.GetBool("-test") {
  499. testpkgs := 0
  500. for i = 0; i < len(p.Files); i++ {
  501. if strings.HasSuffix(p.Files[i], "_test.go") {
  502. testpkgs++
  503. }
  504. }
  505. if testpkgs > 0 && testpkgs != len(p.Files) {
  506. return false
  507. }
  508. }
  509. return true
  510. }
  511. func (p *Package) Ready(local, compiled *stringset.StringSet) bool {
  512. for dep := range p.dependencies.Iter() {
  513. if local.Contains(dep) && !compiled.Contains(dep) {
  514. return false
  515. }
  516. }
  517. return true
  518. }
  519. func (p *Package) ResetIndegree() {
  520. for i := 0; i < len(p.children); i++ {
  521. p.children[i].Indegree++
  522. }
  523. }
  524. func (p *Package) InitWaitGroup() {
  525. p.waiter = new(sync.WaitGroup)
  526. p.waiter.Add(p.Indegree)
  527. }
  528. func (p *Package) Decrement(compile bool) {
  529. p.lock.Lock()
  530. p.needsCompile = compile
  531. p.waiter.Done()
  532. p.lock.Unlock()
  533. }
  534. func (p *Package) Compile(ch chan int) {
  535. var doCompile bool
  536. p.waiter.Wait()
  537. if p.needsCompile || !p.UpToDate() {
  538. oldPkgIsFound()
  539. doCompile = true
  540. } else {
  541. say.Printf("up 2 date: %s\n", p.Name)
  542. }
  543. if doCompile {
  544. say.Printf("compiling: %s\n", p.Name)
  545. handy.StdExecve(p.Argv, true)
  546. }
  547. for _, child := range p.children {
  548. child.Decrement(doCompile)
  549. }
  550. ch <- 1
  551. }
  552. func (p *Package) Visit(node ast.Node) (v ast.Visitor) {
  553. switch node.(type) {
  554. case *ast.BasicLit:
  555. bl, ok := node.(*ast.BasicLit)
  556. if ok {
  557. stripped := string(bl.Value[1 : len(bl.Value)-1])
  558. p.dependencies.Add(stripped)
  559. }
  560. default: // nothing to do if not BasicLit
  561. }
  562. return p
  563. }
  564. //TODO make this examples stuff work, if someone asks for it..
  565. //TODO check that types are ok as well..
  566. func (t *TestCollector) Visit(node ast.Node) (v ast.Visitor) {
  567. switch fn := node.(type) {
  568. case *ast.FuncDecl:
  569. if fn.Recv == nil { // node is a function
  570. if strings.HasPrefix(fn.Name.Name, "Test") {
  571. if fn.Type.Params != nil && fn.Type.Params.NumFields() == 1 {
  572. t.TestFuncs = append(t.TestFuncs, fn.Name.Name)
  573. }
  574. }
  575. if strings.HasPrefix(fn.Name.Name, "Benchmark") {
  576. if fn.Type.Params != nil && fn.Type.Params.NumFields() == 1 {
  577. t.BenchFuncs = append(t.BenchFuncs, fn.Name.Name)
  578. }
  579. }
  580. if strings.HasPrefix(fn.Name.Name, "Example") {
  581. if fn.Type.Params != nil && fn.Type.Params.NumFields() == 0 {
  582. t.ExampleFuncs = append(t.ExampleFuncs, fn.Name.Name)
  583. }
  584. }
  585. }
  586. /// case *ast.Comment: fmt.Printf("Comment: %s\n", fn.Text)
  587. default: // nothing to do if not FuncDecl,Comment
  588. }
  589. return t
  590. }
  591. func (t *TestCollector) String() string {
  592. return fmt.Sprintf("&TestCollector{\n\tt: %v\n\tb: %v\n\te: %v\n}\n",
  593. t.TestFuncs, t.BenchFuncs, t.ExampleFuncs)
  594. }
  595. func (t *TestCollector) FoundAnything() bool {
  596. tot := len(t.TestFuncs) + len(t.BenchFuncs) + len(t.ExampleFuncs)
  597. return tot > 0
  598. }
  599. func (i *initCollector) Visit(node ast.Node) (v ast.Visitor) {
  600. switch t := node.(type) {
  601. case *ast.FuncDecl:
  602. if t.Name.Name == "init" {
  603. if (t.Type.Params == nil || t.Type.Params.NumFields() == 0) &&
  604. (t.Type.Results == nil || t.Type.Results.NumFields() == 0) {
  605. i.hasInit = true
  606. }
  607. }
  608. default: // nothing to do if not FuncDecl
  609. }
  610. return i
  611. }
  612. func addSeparatorPath(root string) string {
  613. if root[len(root)-1:] != "/" {
  614. root = root + "/"
  615. }
  616. return root
  617. }
  618. func hasSlash(s string) bool {
  619. return strings.Index(s, "/") != -1
  620. }
  621. func removeSlashAndDot(s string) string {
  622. noslash := strings.Replace(s, "/", "", -1)
  623. return strings.Replace(noslash, ".", "", -1)
  624. }
  625. func getSyntaxTreeOrDie(file string, mode parser.Mode) *ast.File {
  626. absSynTree, err := parser.ParseFile(token.NewFileSet(), file, nil, mode)
  627. if err != nil {
  628. log.Fatalf("%s\n", err)
  629. }
  630. return absSynTree
  631. }
  632. func OldPkgYet() (res bool) {
  633. locker.Lock()
  634. res = oldPkgFound
  635. locker.Unlock()
  636. return res
  637. }
  638. func oldPkgIsFound() {
  639. locker.Lock()
  640. oldPkgFound = true
  641. locker.Unlock()
  642. }
  643. // gorun like stuff
  644. func ParseSingle(pathname string) (pkgs []*Package, name string) {
  645. tree := getSyntaxTreeOrDie(pathname, parser.ImportsOnly)
  646. shortname := tree.Name.String()
  647. if shortname != "main" {
  648. log.Fatalf("[ERROR] running a single file requires 'main' package\n")
  649. }
  650. p := newPackage()
  651. p.ShortName = shortname
  652. absPath, e := filepath.Abs(pathname)
  653. handy.Check(e)
  654. stub := filepath.Join(os.TempDir(), "godag")
  655. handy.DirOrMkdir(stub)
  656. name = filepath.Join(stub, handy.Sha1(absPath))
  657. p.Name = name
  658. p.Files = append(p.Files, pathname)
  659. pkgs = append(pkgs, p)
  660. return
  661. }
  662. // parse mk.go
  663. func GetMakeTargets(pathname string) []string {
  664. tree := getSyntaxTreeOrDie(pathname, 0)
  665. collect := &targetCollector{MAP_NOT_FOUND,"", make([]string, 0)}
  666. ast.Walk(collect, tree)
  667. return collect.targets
  668. }
  669. // states targetCollector can be in while looking for targets in makefile
  670. const(
  671. MAP_NOT_FOUND = iota
  672. MAP_FOUND
  673. KEY_VALUE_FOUND
  674. KEY_FOUND
  675. KEY_NIL_FOUND
  676. KEY_NIL_UNARY_FOUND
  677. KEY_NIL_UNARY_COMPOSITE_FOUND
  678. )
  679. type targetCollector struct {
  680. state int
  681. prev string
  682. targets []string
  683. }
  684. func (t *targetCollector) Visit(node ast.Node) (v ast.Visitor) {
  685. switch t.state{
  686. case MAP_NOT_FOUND: {
  687. map_t, ok := node.(*ast.MapType)
  688. if ok {
  689. iden, ok := map_t.Key.(*ast.Ident)
  690. if ok && iden.Name == "string" {
  691. star, ok := map_t.Value.(*ast.StarExpr)
  692. if ok {
  693. starType, ok := star.X.(*ast.Ident)
  694. if ok && starType.Name == "Target" {
  695. t.state = MAP_FOUND
  696. }
  697. }
  698. }
  699. }
  700. }
  701. case MAP_FOUND: {
  702. _, ok := node.(*ast.KeyValueExpr)
  703. if ok {
  704. t.state = KEY_VALUE_FOUND
  705. }
  706. }
  707. case KEY_VALUE_FOUND: {
  708. bl, ok := node.(*ast.BasicLit)
  709. if ok && bl.Kind == token.STRING {
  710. t.state = KEY_FOUND
  711. t.prev = bl.Value
  712. }
  713. }
  714. case KEY_FOUND: {
  715. if node == nil {
  716. t.state = KEY_NIL_FOUND
  717. }else{
  718. t.state = MAP_FOUND
  719. }
  720. }
  721. case KEY_NIL_FOUND: {
  722. _, ok := node.(*ast.UnaryExpr)
  723. if ok {
  724. t.state = KEY_NIL_UNARY_FOUND
  725. }else{
  726. t.state = MAP_FOUND
  727. }
  728. }
  729. case KEY_NIL_UNARY_FOUND: {
  730. _, ok := node.(*ast.CompositeLit)
  731. if ok {
  732. t.state = KEY_NIL_UNARY_COMPOSITE_FOUND
  733. }else{
  734. t.state = MAP_FOUND
  735. }
  736. }
  737. case KEY_NIL_UNARY_COMPOSITE_FOUND: {
  738. ident, ok := node.(*ast.Ident)
  739. if ok && ident.Name == "Target" {
  740. stripped := t.prev[1:len(t.prev)-1]
  741. t.targets = append(t.targets, stripped)
  742. }
  743. t.state = MAP_FOUND
  744. }
  745. }
  746. return t
  747. }