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 }