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