github.com/tiagovtristao/plz@v13.4.0+incompatible/src/query/graph.go (about) 1 package query 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "path" 8 "sync" 9 10 "github.com/thought-machine/please/src/build" 11 "github.com/thought-machine/please/src/core" 12 ) 13 14 // Graph prints a representation of the build graph as JSON. 15 func Graph(state *core.BuildState, targets []core.BuildLabel) { 16 log.Notice("Generating graph...") 17 g := makeJSONGraph(state, targets) 18 log.Notice("Marshalling...") 19 b, err := json.MarshalIndent(g, "", " ") 20 if err != nil { 21 log.Fatalf("Failed to serialise JSON: %s\n", err) 22 } 23 log.Notice("Writing...") 24 fmt.Println(string(b)) 25 log.Notice("Done") 26 } 27 28 // JSONGraph is an alternate representation of our build graph; will contain different information 29 // to the structures in core (also those ones can't be printed as JSON directly). 30 type JSONGraph struct { 31 Packages map[string]JSONPackage `json:"packages"` 32 } 33 34 // JSONPackage is an alternate representation of a build package 35 type JSONPackage struct { 36 name string 37 Targets map[string]JSONTarget `json:"targets"` 38 } 39 40 // JSONTarget is an alternate representation of a build target 41 type JSONTarget struct { 42 Inputs []string `json:"inputs,omitempty" note:"declared inputs of target"` 43 Outputs []string `json:"outs,omitempty" note:"corresponds to outs in rule declaration"` 44 Sources []string `json:"srcs,omitempty" note:"corresponds to srcs in rule declaration"` 45 Deps []string `json:"deps,omitempty" note:"corresponds to deps in rule declaration"` 46 Data []string `json:"data,omitempty" note:"corresponds to data in rule declaration"` 47 Labels []string `json:"labels,omitempty" note:"corresponds to labels in rule declaration"` 48 Requires []string `json:"requires,omitempty" note:"corresponds to requires in rule declaration"` 49 Hash string `json:"hash" note:"partial hash of target, does not include source hash"` 50 Test bool `json:"test,omitempty" note:"true if target is a test"` 51 Binary bool `json:"binary,omitempty" note:"true if target is a binary"` 52 TestOnly bool `json:"test_only,omitempty" note:"true if target should be restricted to test code"` 53 } 54 55 func makeJSONGraph(state *core.BuildState, targets []core.BuildLabel) *JSONGraph { 56 ret := JSONGraph{Packages: map[string]JSONPackage{}} 57 if len(targets) == 0 { 58 for pkg := range makeAllPackages(state) { 59 ret.Packages[pkg.name] = pkg 60 } 61 } else { 62 done := map[core.BuildLabel]struct{}{} 63 for _, target := range targets { 64 addJSONTarget(state, &ret, target, done) 65 } 66 } 67 return &ret 68 } 69 70 // makeAllPackages constructs all the JSONPackage objects for this graph in parallel. 71 func makeAllPackages(state *core.BuildState) <-chan JSONPackage { 72 ch := make(chan JSONPackage, 100) 73 go func() { 74 packages := state.Graph.PackageMap() 75 var wg sync.WaitGroup 76 wg.Add(len(packages)) 77 for _, pkg := range packages { 78 go func(pkg *core.Package) { 79 ch <- makeJSONPackage(state, pkg) 80 wg.Done() 81 }(pkg) 82 } 83 wg.Wait() 84 close(ch) 85 }() 86 return ch 87 } 88 89 func addJSONTarget(state *core.BuildState, ret *JSONGraph, label core.BuildLabel, done map[core.BuildLabel]struct{}) { 90 if _, present := done[label]; present { 91 return 92 } 93 done[label] = struct{}{} 94 if label.IsAllTargets() { 95 pkg := state.Graph.PackageOrDie(label) 96 for _, target := range pkg.AllTargets() { 97 addJSONTarget(state, ret, target.Label, done) 98 } 99 return 100 } 101 target := state.Graph.TargetOrDie(label) 102 if _, present := ret.Packages[label.PackageName]; present { 103 ret.Packages[label.PackageName].Targets[label.Name] = makeJSONTarget(state, target) 104 } else { 105 ret.Packages[label.PackageName] = JSONPackage{ 106 Targets: map[string]JSONTarget{ 107 label.Name: makeJSONTarget(state, target), 108 }, 109 } 110 } 111 for _, dep := range target.Dependencies() { 112 addJSONTarget(state, ret, dep.Label, done) 113 } 114 } 115 116 func makeJSONPackage(state *core.BuildState, pkg *core.Package) JSONPackage { 117 targets := map[string]JSONTarget{} 118 for _, target := range pkg.AllTargets() { 119 targets[target.Label.Name] = makeJSONTarget(state, target) 120 } 121 return JSONPackage{name: pkg.Name, Targets: targets} 122 } 123 124 func makeJSONTarget(state *core.BuildState, target *core.BuildTarget) JSONTarget { 125 t := JSONTarget{ 126 Sources: target.AllSourcePaths(state.Graph), 127 } 128 for in := range core.IterSources(state.Graph, target) { 129 t.Inputs = append(t.Inputs, in.Src) 130 } 131 for _, out := range target.Outputs() { 132 t.Outputs = append(t.Outputs, path.Join(target.Label.PackageName, out)) 133 } 134 for _, dep := range target.Dependencies() { 135 t.Deps = append(t.Deps, dep.Label.String()) 136 } 137 for data := range core.IterRuntimeFiles(state.Graph, target, false) { 138 t.Data = append(t.Data, data.Src) 139 } 140 t.Labels = target.Labels 141 t.Requires = target.Requires 142 rawHash := append(build.RuleHash(state, target, true, false), state.Hashes.Config...) 143 t.Hash = base64.RawStdEncoding.EncodeToString(rawHash) 144 t.Test = target.IsTest 145 t.Binary = target.IsBinary 146 t.TestOnly = target.TestOnly 147 return t 148 }