Home | History | Annotate | Download | only in bpdoc
      1 package bpdoc
      2 
      3 import (
      4 	"fmt"
      5 	"html/template"
      6 	"reflect"
      7 	"sort"
      8 
      9 	"github.com/google/blueprint"
     10 	"github.com/google/blueprint/proptools"
     11 )
     12 
     13 // Package contains the information about a package relevant to generating documentation.
     14 type Package struct {
     15 	// Name is the name of the package.
     16 	Name string
     17 
     18 	// Path is the full package path of the package as used in the primary builder.
     19 	Path string
     20 
     21 	// Text is the contents of the package comment documenting the module types in the package.
     22 	Text string
     23 
     24 	// ModuleTypes is a list of ModuleType objects that contain information about each module type that is
     25 	// defined by the package.
     26 	ModuleTypes []*ModuleType
     27 }
     28 
     29 // ModuleType contains the information about a module type that is relevant to generating documentation.
     30 type ModuleType struct {
     31 	// Name is the string that will appear in Blueprints files when defining a new module of
     32 	// this type.
     33 	Name string
     34 
     35 	// PkgPath is the full package path of the package that contains the module type factory.
     36 	PkgPath string
     37 
     38 	// Text is the contents of the comment documenting the module type.
     39 	Text template.HTML
     40 
     41 	// PropertyStructs is a list of PropertyStruct objects that contain information about each
     42 	// property struct that is used by the module type, containing all properties that are valid
     43 	// for the module type.
     44 	PropertyStructs []*PropertyStruct
     45 }
     46 
     47 type PropertyStruct struct {
     48 	Name       string
     49 	Text       string
     50 	Properties []Property
     51 }
     52 
     53 type Property struct {
     54 	Name       string
     55 	OtherNames []string
     56 	Type       string
     57 	Tag        reflect.StructTag
     58 	Text       template.HTML
     59 	OtherTexts []template.HTML
     60 	Properties []Property
     61 	Default    string
     62 }
     63 
     64 func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value,
     65 	moduleTypeNamePropertyStructs map[string][]interface{}) ([]*Package, error) {
     66 	// Read basic info from the files to construct a Reader instance.
     67 	r := NewReader(pkgFiles)
     68 
     69 	pkgMap := map[string]*Package{}
     70 	var pkgs []*Package
     71 	// Scan through per-module-type property structs map.
     72 	for mtName, propertyStructs := range moduleTypeNamePropertyStructs {
     73 		// Construct ModuleType with the given info.
     74 		mtInfo, err := assembleModuleTypeInfo(r, mtName, moduleTypeNameFactories[mtName], propertyStructs)
     75 		if err != nil {
     76 			return nil, err
     77 		}
     78 		// Some pruning work
     79 		removeEmptyPropertyStructs(mtInfo)
     80 		collapseDuplicatePropertyStructs(mtInfo)
     81 		collapseNestedPropertyStructs(mtInfo)
     82 		combineDuplicateProperties(mtInfo)
     83 
     84 		// Add the ModuleInfo to the corresponding Package map/slice entries.
     85 		pkg := pkgMap[mtInfo.PkgPath]
     86 		if pkg == nil {
     87 			var err error
     88 			pkg, err = r.Package(mtInfo.PkgPath)
     89 			if err != nil {
     90 				return nil, err
     91 			}
     92 			pkgMap[mtInfo.PkgPath] = pkg
     93 			pkgs = append(pkgs, pkg)
     94 		}
     95 		pkg.ModuleTypes = append(pkg.ModuleTypes, mtInfo)
     96 	}
     97 
     98 	// Sort ModuleTypes within each package.
     99 	for _, pkg := range pkgs {
    100 		sort.Slice(pkg.ModuleTypes, func(i, j int) bool { return pkg.ModuleTypes[i].Name < pkg.ModuleTypes[j].Name })
    101 	}
    102 	// Sort packages.
    103 	sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Path < pkgs[j].Path })
    104 
    105 	return pkgs, nil
    106 }
    107 
    108 func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
    109 	propertyStructs []interface{}) (*ModuleType, error) {
    110 
    111 	mt, err := r.ModuleType(name, factory)
    112 	if err != nil {
    113 		return nil, err
    114 	}
    115 
    116 	// Reader.ModuleType only fills basic information such as name and package path. Collect more info
    117 	// from property struct data.
    118 	for _, s := range propertyStructs {
    119 		v := reflect.ValueOf(s).Elem()
    120 		t := v.Type()
    121 
    122 		// Ignore property structs with unexported or unnamed types
    123 		if t.PkgPath() == "" {
    124 			continue
    125 		}
    126 		ps, err := r.PropertyStruct(t.PkgPath(), t.Name(), v)
    127 		if err != nil {
    128 			return nil, err
    129 		}
    130 		ps.ExcludeByTag("blueprint", "mutated")
    131 
    132 		for nestedName, nestedValue := range nestedPropertyStructs(v) {
    133 			nestedType := nestedValue.Type()
    134 
    135 			// Ignore property structs with unexported or unnamed types
    136 			if nestedType.PkgPath() == "" {
    137 				continue
    138 			}
    139 			nested, err := r.PropertyStruct(nestedType.PkgPath(), nestedType.Name(), nestedValue)
    140 			if err != nil {
    141 				return nil, err
    142 			}
    143 			nested.ExcludeByTag("blueprint", "mutated")
    144 			nestPoint := ps.GetByName(nestedName)
    145 			if nestPoint == nil {
    146 				return nil, fmt.Errorf("nesting point %q not found", nestedName)
    147 			}
    148 
    149 			key, value, err := blueprint.HasFilter(nestPoint.Tag)
    150 			if err != nil {
    151 				return nil, err
    152 			}
    153 			if key != "" {
    154 				nested.IncludeByTag(key, value)
    155 			}
    156 
    157 			nestPoint.Nest(nested)
    158 		}
    159 		mt.PropertyStructs = append(mt.PropertyStructs, ps)
    160 	}
    161 
    162 	return mt, nil
    163 }
    164 
    165 func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
    166 	ret := make(map[string]reflect.Value)
    167 	var walk func(structValue reflect.Value, prefix string)
    168 	walk = func(structValue reflect.Value, prefix string) {
    169 		typ := structValue.Type()
    170 		for i := 0; i < structValue.NumField(); i++ {
    171 			field := typ.Field(i)
    172 			if field.PkgPath != "" {
    173 				// The field is not exported so just skip it.
    174 				continue
    175 			}
    176 
    177 			fieldValue := structValue.Field(i)
    178 
    179 			switch fieldValue.Kind() {
    180 			case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint:
    181 				// Nothing
    182 			case reflect.Struct:
    183 				walk(fieldValue, prefix+proptools.PropertyNameForField(field.Name)+".")
    184 			case reflect.Ptr, reflect.Interface:
    185 				if !fieldValue.IsNil() {
    186 					// We leave the pointer intact and zero out the struct that's
    187 					// pointed to.
    188 					elem := fieldValue.Elem()
    189 					if fieldValue.Kind() == reflect.Interface {
    190 						if elem.Kind() != reflect.Ptr {
    191 							panic(fmt.Errorf("can't get type of field %q: interface "+
    192 								"refers to a non-pointer", field.Name))
    193 						}
    194 						elem = elem.Elem()
    195 					}
    196 					if elem.Kind() == reflect.Struct {
    197 						nestPoint := prefix + proptools.PropertyNameForField(field.Name)
    198 						ret[nestPoint] = elem
    199 						walk(elem, nestPoint+".")
    200 					}
    201 				}
    202 			default:
    203 				panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
    204 					field.Name, fieldValue.Kind()))
    205 			}
    206 		}
    207 	}
    208 
    209 	walk(s, "")
    210 	return ret
    211 }
    212 
    213 // Remove any property structs that have no exported fields
    214 func removeEmptyPropertyStructs(mt *ModuleType) {
    215 	for i := 0; i < len(mt.PropertyStructs); i++ {
    216 		if len(mt.PropertyStructs[i].Properties) == 0 {
    217 			mt.PropertyStructs = append(mt.PropertyStructs[:i], mt.PropertyStructs[i+1:]...)
    218 			i--
    219 		}
    220 	}
    221 }
    222 
    223 // Squashes duplicates of the same property struct into single entries
    224 func collapseDuplicatePropertyStructs(mt *ModuleType) {
    225 	var collapsed []*PropertyStruct
    226 
    227 propertyStructLoop:
    228 	for _, from := range mt.PropertyStructs {
    229 		for _, to := range collapsed {
    230 			if from.Name == to.Name {
    231 				CollapseDuplicateProperties(&to.Properties, &from.Properties)
    232 				continue propertyStructLoop
    233 			}
    234 		}
    235 		collapsed = append(collapsed, from)
    236 	}
    237 	mt.PropertyStructs = collapsed
    238 }
    239 
    240 func CollapseDuplicateProperties(to, from *[]Property) {
    241 propertyLoop:
    242 	for _, f := range *from {
    243 		for i := range *to {
    244 			t := &(*to)[i]
    245 			if f.Name == t.Name {
    246 				CollapseDuplicateProperties(&t.Properties, &f.Properties)
    247 				continue propertyLoop
    248 			}
    249 		}
    250 		*to = append(*to, f)
    251 	}
    252 }
    253 
    254 // Find all property structs that only contain structs, and move their children up one with
    255 // a prefixed name
    256 func collapseNestedPropertyStructs(mt *ModuleType) {
    257 	for _, ps := range mt.PropertyStructs {
    258 		collapseNestedProperties(&ps.Properties)
    259 	}
    260 }
    261 
    262 func collapseNestedProperties(p *[]Property) {
    263 	var n []Property
    264 
    265 	for _, parent := range *p {
    266 		var containsProperty bool
    267 		for j := range parent.Properties {
    268 			child := &parent.Properties[j]
    269 			if len(child.Properties) > 0 {
    270 				collapseNestedProperties(&child.Properties)
    271 			} else {
    272 				containsProperty = true
    273 			}
    274 		}
    275 		if containsProperty || len(parent.Properties) == 0 {
    276 			n = append(n, parent)
    277 		} else {
    278 			for j := range parent.Properties {
    279 				child := parent.Properties[j]
    280 				child.Name = parent.Name + "." + child.Name
    281 				n = append(n, child)
    282 			}
    283 		}
    284 	}
    285 	*p = n
    286 }
    287 
    288 func combineDuplicateProperties(mt *ModuleType) {
    289 	for _, ps := range mt.PropertyStructs {
    290 		combineDuplicateSubProperties(&ps.Properties)
    291 	}
    292 }
    293 
    294 func combineDuplicateSubProperties(p *[]Property) {
    295 	var n []Property
    296 propertyLoop:
    297 	for _, child := range *p {
    298 		if len(child.Properties) > 0 {
    299 			combineDuplicateSubProperties(&child.Properties)
    300 			for i := range n {
    301 				s := &n[i]
    302 				if s.SameSubProperties(child) {
    303 					s.OtherNames = append(s.OtherNames, child.Name)
    304 					s.OtherTexts = append(s.OtherTexts, child.Text)
    305 					continue propertyLoop
    306 				}
    307 			}
    308 		}
    309 		n = append(n, child)
    310 	}
    311 	*p = n
    312 }
    313