github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/query/print.go (about) 1 package query 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "reflect" 8 "sort" 9 "strings" 10 "time" 11 12 "core" 13 ) 14 15 // Print produces a Python call which would (hopefully) regenerate the same build rule if run. 16 // This is of course not ideal since they were almost certainly created as a java_library 17 // or some similar wrapper rule, but we've lost that information by now. 18 func Print(graph *core.BuildGraph, labels []core.BuildLabel, fields []string) { 19 for _, label := range labels { 20 if len(fields) == 0 { 21 fmt.Fprintf(os.Stdout, "# %s:\n", label) 22 } 23 if len(fields) > 0 { 24 newPrinter(os.Stdout, graph.TargetOrDie(label), 0).PrintFields(fields) 25 } else { 26 newPrinter(os.Stdout, graph.TargetOrDie(label), 0).PrintTarget() 27 } 28 } 29 } 30 31 // specialFields is a mapping of field name -> any special casing relating to how to print it. 32 var specialFields = map[string]func(*printer) (string, bool){ 33 "name": func(p *printer) (string, bool) { 34 return "'" + p.target.Label.Name + "'", true 35 }, 36 "building_description": func(p *printer) (string, bool) { 37 s, ok := p.genericPrint(reflect.ValueOf(p.target.BuildingDescription)) 38 return s, ok && p.target.BuildingDescription != core.DefaultBuildingDescription 39 }, 40 "deps": func(p *printer) (string, bool) { 41 return p.genericPrint(reflect.ValueOf(p.target.DeclaredDependenciesStrict())) 42 }, 43 "exported_deps": func(p *printer) (string, bool) { 44 return p.genericPrint(reflect.ValueOf(p.target.ExportedDependencies())) 45 }, 46 "visibility": func(p *printer) (string, bool) { 47 if len(p.target.Visibility) == 1 && p.target.Visibility[0] == core.WholeGraph[0] { 48 return "['PUBLIC']", true 49 } 50 return p.genericPrint(reflect.ValueOf(p.target.Visibility)) 51 }, 52 "container": func(p *printer) (string, bool) { 53 if p.target.ContainerSettings == nil { 54 return "True", p.target.Containerise 55 } 56 return p.genericPrint(reflect.ValueOf(p.target.ContainerSettings.ToMap())) 57 }, 58 "tools": func(p *printer) (string, bool) { 59 return p.genericPrint(reflect.ValueOf(p.target.AllTools())) 60 }, 61 } 62 63 // fieldPrecedence defines a specific ordering for fields. 64 var fieldPrecedence = map[string]int{ 65 "name": -100, 66 "srcs": -90, 67 "visibility": 90, 68 "deps": 100, 69 } 70 71 // A printer is responsible for creating the output of 'plz query print'. 72 type printer struct { 73 w io.Writer 74 target *core.BuildTarget 75 indent int 76 doneFields map[string]bool 77 error bool // true if something went wrong 78 surroundSyntax bool // true if we are quoting strings or surrounding slices with [] etc. 79 } 80 81 // newPrinter creates a new printer instance. 82 func newPrinter(w io.Writer, target *core.BuildTarget, indent int) *printer { 83 return &printer{ 84 w: w, 85 target: target, 86 indent: indent, 87 doneFields: make(map[string]bool, 50), // Leave enough space for all of BuildTarget's fields. 88 } 89 } 90 91 // printf is an internal function which prints to the internal writer with an indent. 92 func (p *printer) printf(msg string, args ...interface{}) { 93 fmt.Fprint(p.w, strings.Repeat(" ", p.indent)) 94 fmt.Fprintf(p.w, msg, args...) 95 } 96 97 // PrintTarget prints an entire build target. 98 func (p *printer) PrintTarget() { 99 if p.target.IsHashFilegroup { 100 p.printf("hash_filegroup(\n") 101 } else if p.target.IsFilegroup { 102 p.printf("filegroup(\n") 103 } else if p.target.IsRemoteFile { 104 p.printf("remote_file(\n") 105 } else { 106 p.printf("build_rule(\n") 107 } 108 p.surroundSyntax = true 109 p.indent += 4 110 v := reflect.ValueOf(p.target).Elem() 111 t := v.Type() 112 f := make(orderedFields, t.NumField()) 113 for i := 0; i < t.NumField(); i++ { 114 f[i].structIndex = i 115 f[i].printIndex = i 116 if index, present := fieldPrecedence[p.fieldName(t.Field(i))]; present { 117 f[i].printIndex = index 118 } 119 } 120 sort.Sort(f) 121 for _, orderedField := range f { 122 p.printField(t.Field(orderedField.structIndex), v.Field(orderedField.structIndex)) 123 } 124 p.indent -= 4 125 p.printf(")\n\n") 126 } 127 128 // PrintFields prints a subset of fields of a build target. 129 func (p *printer) PrintFields(fields []string) bool { 130 v := reflect.ValueOf(p.target).Elem() 131 for _, field := range fields { 132 f := p.findField(field) 133 if contents, shouldPrint := p.shouldPrintField(f, v.FieldByIndex(f.Index)); shouldPrint { 134 if !strings.HasSuffix(contents, "\n") { 135 contents += "\n" 136 } 137 p.printf("%s", contents) 138 } 139 } 140 return p.error 141 } 142 143 // findField returns the field which would print with the given name. 144 // This isn't as simple as using reflect.Value.FieldByName since the print names 145 // are different to the actual struct names. 146 func (p *printer) findField(field string) reflect.StructField { 147 t := reflect.ValueOf(p.target).Elem().Type() 148 for i := 0; i < t.NumField(); i++ { 149 if f := t.Field(i); p.fieldName(f) == field { 150 return f 151 } 152 } 153 log.Fatalf("Unknown field %s", field) 154 return reflect.StructField{} 155 } 156 157 // fieldName returns the name we'll use to print a field. 158 func (p *printer) fieldName(f reflect.StructField) string { 159 if name := f.Tag.Get("name"); name != "" { 160 return name 161 } 162 // We don't bother specifying on some fields when it's equivalent other than case. 163 return strings.ToLower(f.Name) 164 } 165 166 // printField prints a single field of a build target. 167 func (p *printer) printField(f reflect.StructField, v reflect.Value) { 168 if contents, shouldPrint := p.shouldPrintField(f, v); shouldPrint { 169 name := p.fieldName(f) 170 p.printf("%s = %s,\n", name, contents) 171 p.doneFields[name] = true 172 } 173 } 174 175 // shouldPrintField returns whether we should print a field and what we'd print if we did. 176 func (p *printer) shouldPrintField(f reflect.StructField, v reflect.Value) (string, bool) { 177 if f.Tag.Get("print") == "false" { // Indicates not to print the field. 178 return "", false 179 } 180 name := p.fieldName(f) 181 if p.doneFields[name] { 182 return "", false 183 } 184 if customFunc, present := specialFields[name]; present { 185 return customFunc(p) 186 } 187 return p.genericPrint(v) 188 } 189 190 // genericPrint is the generic print function for a field. 191 func (p *printer) genericPrint(v reflect.Value) (string, bool) { 192 switch v.Kind() { 193 case reflect.Slice: 194 return p.printSlice(v), v.Len() > 0 195 case reflect.Map: 196 return p.printMap(v), v.Len() > 0 197 case reflect.String: 198 return p.quote(v.String()), v.Len() > 0 199 case reflect.Bool: 200 return "True", v.Bool() 201 case reflect.Int, reflect.Int32: 202 return fmt.Sprintf("%d", v.Int()), v.Int() > 0 203 case reflect.Struct, reflect.Interface: 204 if stringer, ok := v.Interface().(fmt.Stringer); ok { 205 return p.quote(stringer.String()), true 206 } 207 return "", false 208 case reflect.Int64: 209 if v.Type().Name() == "Duration" { 210 secs := v.Interface().(time.Duration).Seconds() 211 return fmt.Sprintf("%0.0f", secs), secs > 0.0 212 } 213 } 214 log.Error("Unknown field type %s: %s", v.Kind(), v.Type().Name()) 215 p.error = true 216 return "", false 217 } 218 219 // printSlice prints the representation of a slice field. 220 func (p *printer) printSlice(v reflect.Value) string { 221 if v.Len() == 1 { 222 // Single-element slices are printed on one line 223 elem, _ := p.genericPrint(v.Index(0)) 224 return p.surround("[", elem, "]", "") 225 } 226 s := make([]string, v.Len()) 227 indent := strings.Repeat(" ", p.indent+4) 228 for i := 0; i < v.Len(); i++ { 229 elem, _ := p.genericPrint(v.Index(i)) 230 s[i] = p.surround(indent, elem, ",", "\n") 231 } 232 return p.surround("[\n", strings.Join(s, ""), strings.Repeat(" ", p.indent)+"]", "") 233 } 234 235 // printMap prints the representation of a map field. 236 func (p *printer) printMap(v reflect.Value) string { 237 keys := v.MapKeys() 238 sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() }) 239 s := make([]string, len(keys)) 240 indent := strings.Repeat(" ", p.indent+4) 241 for i, key := range keys { 242 keyElem, _ := p.genericPrint(key) 243 valElem, _ := p.genericPrint(v.MapIndex(key)) 244 s[i] = p.surround(indent, keyElem+": "+valElem, ",", "\n") 245 } 246 return p.surround("{\n", strings.Join(s, ""), strings.Repeat(" ", p.indent)+"}", "") 247 } 248 249 // quote quotes the given string appropriately for the current printing method. 250 func (p *printer) quote(s string) string { 251 if p.surroundSyntax { 252 return "'" + s + "'" 253 } 254 return s 255 } 256 257 // surround surrounds the given string with a prefix and suffix, if appropriate for the current printing method. 258 func (p *printer) surround(prefix, s, suffix, always string) string { 259 if p.surroundSyntax { 260 return prefix + s + suffix + always 261 } 262 return s + always 263 } 264 265 // An orderedField is used to sort the fields into the order we print them in. 266 // This isn't necessarily the same as the order on the struct. 267 type orderedField struct { 268 structIndex, printIndex int 269 } 270 271 type orderedFields []orderedField 272 273 func (f orderedFields) Len() int { return len(f) } 274 func (f orderedFields) Swap(a, b int) { f[a], f[b] = f[b], f[a] } 275 func (f orderedFields) Less(a, b int) bool { return f[a].printIndex < f[b].printIndex }