1// Copyright (C) 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package sdk 16 17import ( 18 "fmt" 19 "reflect" 20 "sort" 21 "strings" 22 23 "android/soong/apex" 24 "android/soong/cc" 25 26 "github.com/google/blueprint" 27 "github.com/google/blueprint/proptools" 28 29 "android/soong/android" 30) 31 32var pctx = android.NewPackageContext("android/soong/sdk") 33 34var ( 35 repackageZip = pctx.AndroidStaticRule("SnapshotRepackageZip", 36 blueprint.RuleParams{ 37 Command: `${config.Zip2ZipCmd} -i $in -o $out -x META-INF/**/* "**/*:$destdir"`, 38 CommandDeps: []string{ 39 "${config.Zip2ZipCmd}", 40 }, 41 }, 42 "destdir") 43 44 zipFiles = pctx.AndroidStaticRule("SnapshotZipFiles", 45 blueprint.RuleParams{ 46 Command: `${config.SoongZipCmd} -C $basedir -l $out.rsp -o $out`, 47 CommandDeps: []string{ 48 "${config.SoongZipCmd}", 49 }, 50 Rspfile: "$out.rsp", 51 RspfileContent: "$in", 52 }, 53 "basedir") 54 55 mergeZips = pctx.AndroidStaticRule("SnapshotMergeZips", 56 blueprint.RuleParams{ 57 Command: `${config.MergeZipsCmd} $out $in`, 58 CommandDeps: []string{ 59 "${config.MergeZipsCmd}", 60 }, 61 }) 62) 63 64type generatedContents struct { 65 content strings.Builder 66 indentLevel int 67} 68 69// generatedFile abstracts operations for writing contents into a file and emit a build rule 70// for the file. 71type generatedFile struct { 72 generatedContents 73 path android.OutputPath 74} 75 76func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile { 77 return &generatedFile{ 78 path: android.PathForModuleOut(ctx, path...).OutputPath, 79 } 80} 81 82func (gc *generatedContents) Indent() { 83 gc.indentLevel++ 84} 85 86func (gc *generatedContents) Dedent() { 87 gc.indentLevel-- 88} 89 90func (gc *generatedContents) Printfln(format string, args ...interface{}) { 91 fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format+"\n", args...) 92} 93 94func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) { 95 rb := android.NewRuleBuilder() 96 97 content := gf.content.String() 98 99 // ninja consumes newline characters in rspfile_content. Prevent it by 100 // escaping the backslash in the newline character. The extra backslash 101 // is removed when the rspfile is written to the actual script file 102 content = strings.ReplaceAll(content, "\n", "\\n") 103 104 rb.Command(). 105 Implicits(implicits). 106 Text("echo").Text(proptools.ShellEscape(content)). 107 // convert \\n to \n 108 Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path) 109 rb.Command(). 110 Text("chmod a+x").Output(gf.path) 111 rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base()) 112} 113 114// Collect all the members. 115// 116// Returns a list containing type (extracted from the dependency tag) and the variant 117// plus the multilib usages. 118func (s *sdk) collectMembers(ctx android.ModuleContext) { 119 s.multilibUsages = multilibNone 120 ctx.WalkDeps(func(child android.Module, parent android.Module) bool { 121 tag := ctx.OtherModuleDependencyTag(child) 122 if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok { 123 memberType := memberTag.SdkMemberType() 124 125 // Make sure that the resolved module is allowed in the member list property. 126 if !memberType.IsInstance(child) { 127 ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName()) 128 } 129 130 // Keep track of which multilib variants are used by the sdk. 131 s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType) 132 133 s.memberRefs = append(s.memberRefs, sdkMemberRef{memberType, child.(android.SdkAware)}) 134 135 // If the member type supports transitive sdk members then recurse down into 136 // its dependencies, otherwise exit traversal. 137 return memberType.HasTransitiveSdkMembers() 138 } 139 140 return false 141 }) 142} 143 144// Organize the members. 145// 146// The members are first grouped by type and then grouped by name. The order of 147// the types is the order they are referenced in android.SdkMemberTypesRegistry. 148// The names are in the order in which the dependencies were added. 149// 150// Returns the members as well as the multilib setting to use. 151func (s *sdk) organizeMembers(ctx android.ModuleContext, memberRefs []sdkMemberRef) []*sdkMember { 152 byType := make(map[android.SdkMemberType][]*sdkMember) 153 byName := make(map[string]*sdkMember) 154 155 for _, memberRef := range memberRefs { 156 memberType := memberRef.memberType 157 variant := memberRef.variant 158 159 name := ctx.OtherModuleName(variant) 160 member := byName[name] 161 if member == nil { 162 member = &sdkMember{memberType: memberType, name: name} 163 byName[name] = member 164 byType[memberType] = append(byType[memberType], member) 165 } 166 167 // Only append new variants to the list. This is needed because a member can be both 168 // exported by the sdk and also be a transitive sdk member. 169 member.variants = appendUniqueVariants(member.variants, variant) 170 } 171 172 var members []*sdkMember 173 for _, memberListProperty := range s.memberListProperties() { 174 membersOfType := byType[memberListProperty.memberType] 175 members = append(members, membersOfType...) 176 } 177 178 return members 179} 180 181func appendUniqueVariants(variants []android.SdkAware, newVariant android.SdkAware) []android.SdkAware { 182 for _, v := range variants { 183 if v == newVariant { 184 return variants 185 } 186 } 187 return append(variants, newVariant) 188} 189 190// SDK directory structure 191// <sdk_root>/ 192// Android.bp : definition of a 'sdk' module is here. This is a hand-made one. 193// <api_ver>/ : below this directory are all auto-generated 194// Android.bp : definition of 'sdk_snapshot' module is here 195// aidl/ 196// frameworks/base/core/..../IFoo.aidl : an exported AIDL file 197// java/ 198// <module_name>.jar : the stub jar for a java library 'module_name' 199// include/ 200// bionic/libc/include/stdlib.h : an exported header file 201// include_gen/ 202// <module_name>/com/android/.../IFoo.h : a generated header file 203// <arch>/include/ : arch-specific exported headers 204// <arch>/include_gen/ : arch-specific generated headers 205// <arch>/lib/ 206// libFoo.so : a stub library 207 208// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot 209// This isn't visible to users, so could be changed in future. 210func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string { 211 return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version 212} 213 214// buildSnapshot is the main function in this source file. It creates rules to copy 215// the contents (header files, stub libraries, etc) into the zip file. 216func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath { 217 218 allMembersByName := make(map[string]struct{}) 219 exportedMembersByName := make(map[string]struct{}) 220 var memberRefs []sdkMemberRef 221 for _, sdkVariant := range sdkVariants { 222 memberRefs = append(memberRefs, sdkVariant.memberRefs...) 223 224 // Record the names of all the members, both explicitly specified and implicitly 225 // included. 226 for _, memberRef := range sdkVariant.memberRefs { 227 allMembersByName[memberRef.variant.Name()] = struct{}{} 228 } 229 230 // Merge the exported member sets from all sdk variants. 231 for key, _ := range sdkVariant.getExportedMembers() { 232 exportedMembersByName[key] = struct{}{} 233 } 234 } 235 236 snapshotDir := android.PathForModuleOut(ctx, "snapshot") 237 238 bp := newGeneratedFile(ctx, "snapshot", "Android.bp") 239 240 bpFile := &bpFile{ 241 modules: make(map[string]*bpModule), 242 } 243 244 builder := &snapshotBuilder{ 245 ctx: ctx, 246 sdk: s, 247 version: "current", 248 snapshotDir: snapshotDir.OutputPath, 249 copies: make(map[string]string), 250 filesToZip: []android.Path{bp.path}, 251 bpFile: bpFile, 252 prebuiltModules: make(map[string]*bpModule), 253 allMembersByName: allMembersByName, 254 exportedMembersByName: exportedMembersByName, 255 } 256 s.builderForTests = builder 257 258 members := s.organizeMembers(ctx, memberRefs) 259 for _, member := range members { 260 memberType := member.memberType 261 262 memberCtx := &memberContext{ctx, builder, memberType, member.name} 263 264 prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member) 265 s.createMemberSnapshot(memberCtx, member, prebuiltModule) 266 } 267 268 // Create a transformer that will transform an unversioned module into a versioned module. 269 unversionedToVersionedTransformer := unversionedToVersionedTransformation{builder: builder} 270 271 // Create a transformer that will transform an unversioned module by replacing any references 272 // to internal members with a unique module name and setting prefer: false. 273 unversionedTransformer := unversionedTransformation{builder: builder} 274 275 for _, unversioned := range builder.prebuiltOrder { 276 // Prune any empty property sets. 277 unversioned = unversioned.transform(pruneEmptySetTransformer{}) 278 279 // Copy the unversioned module so it can be modified to make it versioned. 280 versioned := unversioned.deepCopy() 281 282 // Transform the unversioned module into a versioned one. 283 versioned.transform(unversionedToVersionedTransformer) 284 bpFile.AddModule(versioned) 285 286 // Transform the unversioned module to make it suitable for use in the snapshot. 287 unversioned.transform(unversionedTransformer) 288 bpFile.AddModule(unversioned) 289 } 290 291 // Create the snapshot module. 292 snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version 293 var snapshotModuleType string 294 if s.properties.Module_exports { 295 snapshotModuleType = "module_exports_snapshot" 296 } else { 297 snapshotModuleType = "sdk_snapshot" 298 } 299 snapshotModule := bpFile.newModule(snapshotModuleType) 300 snapshotModule.AddProperty("name", snapshotName) 301 302 // Make sure that the snapshot has the same visibility as the sdk. 303 visibility := android.EffectiveVisibilityRules(ctx, s) 304 if len(visibility) != 0 { 305 snapshotModule.AddProperty("visibility", visibility) 306 } 307 308 addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule) 309 310 var dynamicMemberPropertiesContainers []propertiesContainer 311 osTypeToMemberProperties := make(map[android.OsType]*sdk) 312 for _, sdkVariant := range sdkVariants { 313 properties := sdkVariant.dynamicMemberTypeListProperties 314 osTypeToMemberProperties[sdkVariant.Target().Os] = sdkVariant 315 dynamicMemberPropertiesContainers = append(dynamicMemberPropertiesContainers, &dynamicMemberPropertiesContainer{sdkVariant, properties}) 316 } 317 318 // Extract the common lists of members into a separate struct. 319 commonDynamicMemberProperties := s.dynamicSdkMemberTypes.createMemberListProperties() 320 extractor := newCommonValueExtractor(commonDynamicMemberProperties) 321 extractCommonProperties(ctx, extractor, commonDynamicMemberProperties, dynamicMemberPropertiesContainers) 322 323 // Add properties common to all os types. 324 s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties) 325 326 // Iterate over the os types in a fixed order. 327 targetPropertySet := snapshotModule.AddPropertySet("target") 328 for _, osType := range s.getPossibleOsTypes() { 329 if sdkVariant, ok := osTypeToMemberProperties[osType]; ok { 330 osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name) 331 332 // Compile_multilib defaults to both and must always be set to both on the 333 // device and so only needs to be set when targeted at the host and is neither 334 // unspecified or both. 335 multilib := sdkVariant.multilibUsages 336 if (osType.Class == android.Host || osType.Class == android.HostCross) && 337 multilib != multilibNone && multilib != multilibBoth { 338 osPropertySet.AddProperty("compile_multilib", multilib.String()) 339 } 340 341 s.addMemberPropertiesToPropertySet(builder, osPropertySet, sdkVariant.dynamicMemberTypeListProperties) 342 } 343 } 344 345 // Prune any empty property sets. 346 snapshotModule.transform(pruneEmptySetTransformer{}) 347 348 bpFile.AddModule(snapshotModule) 349 350 // generate Android.bp 351 bp = newGeneratedFile(ctx, "snapshot", "Android.bp") 352 generateBpContents(&bp.generatedContents, bpFile) 353 354 contents := bp.content.String() 355 syntaxCheckSnapshotBpFile(ctx, contents) 356 357 bp.build(pctx, ctx, nil) 358 359 filesToZip := builder.filesToZip 360 361 // zip them all 362 outputZipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath 363 outputDesc := "Building snapshot for " + ctx.ModuleName() 364 365 // If there are no zips to merge then generate the output zip directly. 366 // Otherwise, generate an intermediate zip file into which other zips can be 367 // merged. 368 var zipFile android.OutputPath 369 var desc string 370 if len(builder.zipsToMerge) == 0 { 371 zipFile = outputZipFile 372 desc = outputDesc 373 } else { 374 zipFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.unmerged.zip").OutputPath 375 desc = "Building intermediate snapshot for " + ctx.ModuleName() 376 } 377 378 ctx.Build(pctx, android.BuildParams{ 379 Description: desc, 380 Rule: zipFiles, 381 Inputs: filesToZip, 382 Output: zipFile, 383 Args: map[string]string{ 384 "basedir": builder.snapshotDir.String(), 385 }, 386 }) 387 388 if len(builder.zipsToMerge) != 0 { 389 ctx.Build(pctx, android.BuildParams{ 390 Description: outputDesc, 391 Rule: mergeZips, 392 Input: zipFile, 393 Inputs: builder.zipsToMerge, 394 Output: outputZipFile, 395 }) 396 } 397 398 return outputZipFile 399} 400 401// Check the syntax of the generated Android.bp file contents and if they are 402// invalid then log an error with the contents (tagged with line numbers) and the 403// errors that were found so that it is easy to see where the problem lies. 404func syntaxCheckSnapshotBpFile(ctx android.ModuleContext, contents string) { 405 errs := android.CheckBlueprintSyntax(ctx, "Android.bp", contents) 406 if len(errs) != 0 { 407 message := &strings.Builder{} 408 _, _ = fmt.Fprint(message, `errors in generated Android.bp snapshot: 409 410Generated Android.bp contents 411======================================================================== 412`) 413 for i, line := range strings.Split(contents, "\n") { 414 _, _ = fmt.Fprintf(message, "%6d: %s\n", i+1, line) 415 } 416 417 _, _ = fmt.Fprint(message, ` 418======================================================================== 419 420Errors found: 421`) 422 423 for _, err := range errs { 424 _, _ = fmt.Fprintf(message, "%s\n", err.Error()) 425 } 426 427 ctx.ModuleErrorf("%s", message.String()) 428 } 429} 430 431func extractCommonProperties(ctx android.ModuleContext, extractor *commonValueExtractor, commonProperties interface{}, inputPropertiesSlice interface{}) { 432 err := extractor.extractCommonProperties(commonProperties, inputPropertiesSlice) 433 if err != nil { 434 ctx.ModuleErrorf("error extracting common properties: %s", err) 435 } 436} 437 438func (s *sdk) addMemberPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, dynamicMemberTypeListProperties interface{}) { 439 for _, memberListProperty := range s.memberListProperties() { 440 names := memberListProperty.getter(dynamicMemberTypeListProperties) 441 if len(names) > 0 { 442 propertySet.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names, false)) 443 } 444 } 445} 446 447type propertyTag struct { 448 name string 449} 450 451// A BpPropertyTag to add to a property that contains references to other sdk members. 452// 453// This will cause the references to be rewritten to a versioned reference in the version 454// specific instance of a snapshot module. 455var requiredSdkMemberReferencePropertyTag = propertyTag{"requiredSdkMemberReferencePropertyTag"} 456var optionalSdkMemberReferencePropertyTag = propertyTag{"optionalSdkMemberReferencePropertyTag"} 457 458// A BpPropertyTag that indicates the property should only be present in the versioned 459// module. 460// 461// This will cause the property to be removed from the unversioned instance of a 462// snapshot module. 463var sdkVersionedOnlyPropertyTag = propertyTag{"sdkVersionedOnlyPropertyTag"} 464 465type unversionedToVersionedTransformation struct { 466 identityTransformation 467 builder *snapshotBuilder 468} 469 470func (t unversionedToVersionedTransformation) transformModule(module *bpModule) *bpModule { 471 // Use a versioned name for the module but remember the original name for the 472 // snapshot. 473 name := module.getValue("name").(string) 474 module.setProperty("name", t.builder.versionedSdkMemberName(name, true)) 475 module.insertAfter("name", "sdk_member_name", name) 476 return module 477} 478 479func (t unversionedToVersionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) { 480 if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag { 481 required := tag == requiredSdkMemberReferencePropertyTag 482 return t.builder.versionedSdkMemberNames(value.([]string), required), tag 483 } else { 484 return value, tag 485 } 486} 487 488type unversionedTransformation struct { 489 identityTransformation 490 builder *snapshotBuilder 491} 492 493func (t unversionedTransformation) transformModule(module *bpModule) *bpModule { 494 // If the module is an internal member then use a unique name for it. 495 name := module.getValue("name").(string) 496 module.setProperty("name", t.builder.unversionedSdkMemberName(name, true)) 497 498 // Set prefer: false - this is not strictly required as that is the default. 499 module.insertAfter("name", "prefer", false) 500 501 return module 502} 503 504func (t unversionedTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) { 505 if tag == requiredSdkMemberReferencePropertyTag || tag == optionalSdkMemberReferencePropertyTag { 506 required := tag == requiredSdkMemberReferencePropertyTag 507 return t.builder.unversionedSdkMemberNames(value.([]string), required), tag 508 } else if tag == sdkVersionedOnlyPropertyTag { 509 // The property is not allowed in the unversioned module so remove it. 510 return nil, nil 511 } else { 512 return value, tag 513 } 514} 515 516type pruneEmptySetTransformer struct { 517 identityTransformation 518} 519 520var _ bpTransformer = (*pruneEmptySetTransformer)(nil) 521 522func (t pruneEmptySetTransformer) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) { 523 if len(propertySet.properties) == 0 { 524 return nil, nil 525 } else { 526 return propertySet, tag 527 } 528} 529 530func generateBpContents(contents *generatedContents, bpFile *bpFile) { 531 contents.Printfln("// This is auto-generated. DO NOT EDIT.") 532 for _, bpModule := range bpFile.order { 533 contents.Printfln("") 534 contents.Printfln("%s {", bpModule.moduleType) 535 outputPropertySet(contents, bpModule.bpPropertySet) 536 contents.Printfln("}") 537 } 538} 539 540func outputPropertySet(contents *generatedContents, set *bpPropertySet) { 541 contents.Indent() 542 543 // Output the properties first, followed by the nested sets. This ensures a 544 // consistent output irrespective of whether property sets are created before 545 // or after the properties. This simplifies the creation of the module. 546 for _, name := range set.order { 547 value := set.getValue(name) 548 549 switch v := value.(type) { 550 case []string: 551 length := len(v) 552 if length > 1 { 553 contents.Printfln("%s: [", name) 554 contents.Indent() 555 for i := 0; i < length; i = i + 1 { 556 contents.Printfln("%q,", v[i]) 557 } 558 contents.Dedent() 559 contents.Printfln("],") 560 } else if length == 0 { 561 contents.Printfln("%s: [],", name) 562 } else { 563 contents.Printfln("%s: [%q],", name, v[0]) 564 } 565 566 case bool: 567 contents.Printfln("%s: %t,", name, v) 568 569 case *bpPropertySet: 570 // Do not write property sets in the properties phase. 571 572 default: 573 contents.Printfln("%s: %q,", name, value) 574 } 575 } 576 577 for _, name := range set.order { 578 value := set.getValue(name) 579 580 // Only write property sets in the sets phase. 581 switch v := value.(type) { 582 case *bpPropertySet: 583 contents.Printfln("%s: {", name) 584 outputPropertySet(contents, v) 585 contents.Printfln("},") 586 } 587 } 588 589 contents.Dedent() 590} 591 592func (s *sdk) GetAndroidBpContentsForTests() string { 593 contents := &generatedContents{} 594 generateBpContents(contents, s.builderForTests.bpFile) 595 return contents.content.String() 596} 597 598type snapshotBuilder struct { 599 ctx android.ModuleContext 600 sdk *sdk 601 version string 602 snapshotDir android.OutputPath 603 bpFile *bpFile 604 605 // Map from destination to source of each copy - used to eliminate duplicates and 606 // detect conflicts. 607 copies map[string]string 608 609 filesToZip android.Paths 610 zipsToMerge android.Paths 611 612 prebuiltModules map[string]*bpModule 613 prebuiltOrder []*bpModule 614 615 // The set of all members by name. 616 allMembersByName map[string]struct{} 617 618 // The set of exported members by name. 619 exportedMembersByName map[string]struct{} 620} 621 622func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) { 623 if existing, ok := s.copies[dest]; ok { 624 if existing != src.String() { 625 s.ctx.ModuleErrorf("conflicting copy, %s copied from both %s and %s", dest, existing, src) 626 return 627 } 628 } else { 629 path := s.snapshotDir.Join(s.ctx, dest) 630 s.ctx.Build(pctx, android.BuildParams{ 631 Rule: android.Cp, 632 Input: src, 633 Output: path, 634 }) 635 s.filesToZip = append(s.filesToZip, path) 636 637 s.copies[dest] = src.String() 638 } 639} 640 641func (s *snapshotBuilder) UnzipToSnapshot(zipPath android.Path, destDir string) { 642 ctx := s.ctx 643 644 // Repackage the zip file so that the entries are in the destDir directory. 645 // This will allow the zip file to be merged into the snapshot. 646 tmpZipPath := android.PathForModuleOut(ctx, "tmp", destDir+".zip").OutputPath 647 648 ctx.Build(pctx, android.BuildParams{ 649 Description: "Repackaging zip file " + destDir + " for snapshot " + ctx.ModuleName(), 650 Rule: repackageZip, 651 Input: zipPath, 652 Output: tmpZipPath, 653 Args: map[string]string{ 654 "destdir": destDir, 655 }, 656 }) 657 658 // Add the repackaged zip file to the files to merge. 659 s.zipsToMerge = append(s.zipsToMerge, tmpZipPath) 660} 661 662func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule { 663 name := member.Name() 664 if s.prebuiltModules[name] != nil { 665 panic(fmt.Sprintf("Duplicate module detected, module %s has already been added", name)) 666 } 667 668 m := s.bpFile.newModule(moduleType) 669 m.AddProperty("name", name) 670 671 variant := member.Variants()[0] 672 673 if s.isInternalMember(name) { 674 // An internal member is only referenced from the sdk snapshot which is in the 675 // same package so can be marked as private. 676 m.AddProperty("visibility", []string{"//visibility:private"}) 677 } else { 678 // Extract visibility information from a member variant. All variants have the same 679 // visibility so it doesn't matter which one is used. 680 visibility := android.EffectiveVisibilityRules(s.ctx, variant) 681 if len(visibility) != 0 { 682 m.AddProperty("visibility", visibility) 683 } 684 } 685 686 deviceSupported := false 687 hostSupported := false 688 689 for _, variant := range member.Variants() { 690 osClass := variant.Target().Os.Class 691 if osClass == android.Host || osClass == android.HostCross { 692 hostSupported = true 693 } else if osClass == android.Device { 694 deviceSupported = true 695 } 696 } 697 698 addHostDeviceSupportedProperties(deviceSupported, hostSupported, m) 699 700 // Where available copy apex_available properties from the member. 701 if apexAware, ok := variant.(interface{ ApexAvailable() []string }); ok { 702 apexAvailable := apexAware.ApexAvailable() 703 704 // Add in any baseline apex available settings. 705 apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...) 706 707 if len(apexAvailable) > 0 { 708 // Remove duplicates and sort. 709 apexAvailable = android.FirstUniqueStrings(apexAvailable) 710 sort.Strings(apexAvailable) 711 712 m.AddProperty("apex_available", apexAvailable) 713 } 714 } 715 716 // Disable installation in the versioned module of those modules that are ever installable. 717 if installable, ok := variant.(interface{ EverInstallable() bool }); ok { 718 if installable.EverInstallable() { 719 m.AddPropertyWithTag("installable", false, sdkVersionedOnlyPropertyTag) 720 } 721 } 722 723 s.prebuiltModules[name] = m 724 s.prebuiltOrder = append(s.prebuiltOrder, m) 725 return m 726} 727 728func addHostDeviceSupportedProperties(deviceSupported bool, hostSupported bool, bpModule *bpModule) { 729 if !deviceSupported { 730 bpModule.AddProperty("device_supported", false) 731 } 732 if hostSupported { 733 bpModule.AddProperty("host_supported", true) 734 } 735} 736 737func (s *snapshotBuilder) SdkMemberReferencePropertyTag(required bool) android.BpPropertyTag { 738 if required { 739 return requiredSdkMemberReferencePropertyTag 740 } else { 741 return optionalSdkMemberReferencePropertyTag 742 } 743} 744 745func (s *snapshotBuilder) OptionalSdkMemberReferencePropertyTag() android.BpPropertyTag { 746 return optionalSdkMemberReferencePropertyTag 747} 748 749// Get a versioned name appropriate for the SDK snapshot version being taken. 750func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string, required bool) string { 751 if _, ok := s.allMembersByName[unversionedName]; !ok { 752 if required { 753 s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName) 754 } 755 return unversionedName 756 } 757 return versionedSdkMemberName(s.ctx, unversionedName, s.version) 758} 759 760func (s *snapshotBuilder) versionedSdkMemberNames(members []string, required bool) []string { 761 var references []string = nil 762 for _, m := range members { 763 references = append(references, s.versionedSdkMemberName(m, required)) 764 } 765 return references 766} 767 768// Get an internal name unique to the sdk. 769func (s *snapshotBuilder) unversionedSdkMemberName(unversionedName string, required bool) string { 770 if _, ok := s.allMembersByName[unversionedName]; !ok { 771 if required { 772 s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", unversionedName) 773 } 774 return unversionedName 775 } 776 777 if s.isInternalMember(unversionedName) { 778 return s.ctx.ModuleName() + "_" + unversionedName 779 } else { 780 return unversionedName 781 } 782} 783 784func (s *snapshotBuilder) unversionedSdkMemberNames(members []string, required bool) []string { 785 var references []string = nil 786 for _, m := range members { 787 references = append(references, s.unversionedSdkMemberName(m, required)) 788 } 789 return references 790} 791 792func (s *snapshotBuilder) isInternalMember(memberName string) bool { 793 _, ok := s.exportedMembersByName[memberName] 794 return !ok 795} 796 797// Add the properties from the given SdkMemberProperties to the blueprint 798// property set. This handles common properties in SdkMemberPropertiesBase and 799// calls the member-specific AddToPropertySet for the rest. 800func addSdkMemberPropertiesToSet(ctx *memberContext, memberProperties android.SdkMemberProperties, targetPropertySet android.BpPropertySet) { 801 if memberProperties.Base().Compile_multilib != "" { 802 targetPropertySet.AddProperty("compile_multilib", memberProperties.Base().Compile_multilib) 803 } 804 805 memberProperties.AddToPropertySet(ctx, targetPropertySet) 806} 807 808type sdkMemberRef struct { 809 memberType android.SdkMemberType 810 variant android.SdkAware 811} 812 813var _ android.SdkMember = (*sdkMember)(nil) 814 815type sdkMember struct { 816 memberType android.SdkMemberType 817 name string 818 variants []android.SdkAware 819} 820 821func (m *sdkMember) Name() string { 822 return m.name 823} 824 825func (m *sdkMember) Variants() []android.SdkAware { 826 return m.variants 827} 828 829// Track usages of multilib variants. 830type multilibUsage int 831 832const ( 833 multilibNone multilibUsage = 0 834 multilib32 multilibUsage = 1 835 multilib64 multilibUsage = 2 836 multilibBoth = multilib32 | multilib64 837) 838 839// Add the multilib that is used in the arch type. 840func (m multilibUsage) addArchType(archType android.ArchType) multilibUsage { 841 multilib := archType.Multilib 842 switch multilib { 843 case "": 844 return m 845 case "lib32": 846 return m | multilib32 847 case "lib64": 848 return m | multilib64 849 default: 850 panic(fmt.Errorf("Unknown Multilib field in ArchType, expected 'lib32' or 'lib64', found %q", multilib)) 851 } 852} 853 854func (m multilibUsage) String() string { 855 switch m { 856 case multilibNone: 857 return "" 858 case multilib32: 859 return "32" 860 case multilib64: 861 return "64" 862 case multilibBoth: 863 return "both" 864 default: 865 panic(fmt.Errorf("Unknown multilib value, found %b, expected one of %b, %b, %b or %b", 866 m, multilibNone, multilib32, multilib64, multilibBoth)) 867 } 868} 869 870type baseInfo struct { 871 Properties android.SdkMemberProperties 872} 873 874func (b *baseInfo) optimizableProperties() interface{} { 875 return b.Properties 876} 877 878type osTypeSpecificInfo struct { 879 baseInfo 880 881 osType android.OsType 882 883 // The list of arch type specific info for this os type. 884 // 885 // Nil if there is one variant whose arch type is common 886 archInfos []*archTypeSpecificInfo 887} 888 889var _ propertiesContainer = (*osTypeSpecificInfo)(nil) 890 891type variantPropertiesFactoryFunc func() android.SdkMemberProperties 892 893// Create a new osTypeSpecificInfo for the specified os type and its properties 894// structures populated with information from the variants. 895func newOsTypeSpecificInfo(ctx android.SdkMemberContext, osType android.OsType, variantPropertiesFactory variantPropertiesFactoryFunc, osTypeVariants []android.Module) *osTypeSpecificInfo { 896 osInfo := &osTypeSpecificInfo{ 897 osType: osType, 898 } 899 900 osSpecificVariantPropertiesFactory := func() android.SdkMemberProperties { 901 properties := variantPropertiesFactory() 902 properties.Base().Os = osType 903 return properties 904 } 905 906 // Create a structure into which properties common across the architectures in 907 // this os type will be stored. 908 osInfo.Properties = osSpecificVariantPropertiesFactory() 909 910 // Group the variants by arch type. 911 var variantsByArchName = make(map[string][]android.Module) 912 var archTypes []android.ArchType 913 for _, variant := range osTypeVariants { 914 archType := variant.Target().Arch.ArchType 915 archTypeName := archType.Name 916 if _, ok := variantsByArchName[archTypeName]; !ok { 917 archTypes = append(archTypes, archType) 918 } 919 920 variantsByArchName[archTypeName] = append(variantsByArchName[archTypeName], variant) 921 } 922 923 if commonVariants, ok := variantsByArchName["common"]; ok { 924 if len(osTypeVariants) != 1 { 925 panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants))) 926 } 927 928 // A common arch type only has one variant and its properties should be treated 929 // as common to the os type. 930 osInfo.Properties.PopulateFromVariant(ctx, commonVariants[0]) 931 } else { 932 // Create an arch specific info for each supported architecture type. 933 for _, archType := range archTypes { 934 archTypeName := archType.Name 935 936 archVariants := variantsByArchName[archTypeName] 937 archInfo := newArchSpecificInfo(ctx, archType, osSpecificVariantPropertiesFactory, archVariants) 938 939 osInfo.archInfos = append(osInfo.archInfos, archInfo) 940 } 941 } 942 943 return osInfo 944} 945 946// Optimize the properties by extracting common properties from arch type specific 947// properties into os type specific properties. 948func (osInfo *osTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonValueExtractor *commonValueExtractor) { 949 // Nothing to do if there is only a single common architecture. 950 if len(osInfo.archInfos) == 0 { 951 return 952 } 953 954 multilib := multilibNone 955 for _, archInfo := range osInfo.archInfos { 956 multilib = multilib.addArchType(archInfo.archType) 957 958 // Optimize the arch properties first. 959 archInfo.optimizeProperties(ctx, commonValueExtractor) 960 } 961 962 extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, osInfo.Properties, osInfo.archInfos) 963 964 // Choose setting for compile_multilib that is appropriate for the arch variants supplied. 965 osInfo.Properties.Base().Compile_multilib = multilib.String() 966} 967 968// Add the properties for an os to a property set. 969// 970// Maps the properties related to the os variants through to an appropriate 971// module structure that will produce equivalent set of variants when it is 972// processed in a build. 973func (osInfo *osTypeSpecificInfo) addToPropertySet(ctx *memberContext, bpModule android.BpModule, targetPropertySet android.BpPropertySet) { 974 975 var osPropertySet android.BpPropertySet 976 var archPropertySet android.BpPropertySet 977 var archOsPrefix string 978 if osInfo.Properties.Base().Os_count == 1 { 979 // There is only one os type present in the variants so don't bother 980 // with adding target specific properties. 981 982 // Create a structure that looks like: 983 // module_type { 984 // name: "...", 985 // ... 986 // <common properties> 987 // ... 988 // <single os type specific properties> 989 // 990 // arch: { 991 // <arch specific sections> 992 // } 993 // 994 osPropertySet = bpModule 995 archPropertySet = osPropertySet.AddPropertySet("arch") 996 997 // Arch specific properties need to be added to an arch specific section 998 // within arch. 999 archOsPrefix = "" 1000 } else { 1001 // Create a structure that looks like: 1002 // module_type { 1003 // name: "...", 1004 // ... 1005 // <common properties> 1006 // ... 1007 // target: { 1008 // <arch independent os specific sections, e.g. android> 1009 // ... 1010 // <arch and os specific sections, e.g. android_x86> 1011 // } 1012 // 1013 osType := osInfo.osType 1014 osPropertySet = targetPropertySet.AddPropertySet(osType.Name) 1015 archPropertySet = targetPropertySet 1016 1017 // Arch specific properties need to be added to an os and arch specific 1018 // section prefixed with <os>_. 1019 archOsPrefix = osType.Name + "_" 1020 } 1021 1022 // Add the os specific but arch independent properties to the module. 1023 addSdkMemberPropertiesToSet(ctx, osInfo.Properties, osPropertySet) 1024 1025 // Add arch (and possibly os) specific sections for each set of arch (and possibly 1026 // os) specific properties. 1027 // 1028 // The archInfos list will be empty if the os contains variants for the common 1029 // architecture. 1030 for _, archInfo := range osInfo.archInfos { 1031 archInfo.addToPropertySet(ctx, archPropertySet, archOsPrefix) 1032 } 1033} 1034 1035func (osInfo *osTypeSpecificInfo) isHostVariant() bool { 1036 osClass := osInfo.osType.Class 1037 return osClass == android.Host || osClass == android.HostCross 1038} 1039 1040var _ isHostVariant = (*osTypeSpecificInfo)(nil) 1041 1042func (osInfo *osTypeSpecificInfo) String() string { 1043 return fmt.Sprintf("OsType{%s}", osInfo.osType) 1044} 1045 1046type archTypeSpecificInfo struct { 1047 baseInfo 1048 1049 archType android.ArchType 1050 1051 linkInfos []*linkTypeSpecificInfo 1052} 1053 1054var _ propertiesContainer = (*archTypeSpecificInfo)(nil) 1055 1056// Create a new archTypeSpecificInfo for the specified arch type and its properties 1057// structures populated with information from the variants. 1058func newArchSpecificInfo(ctx android.SdkMemberContext, archType android.ArchType, variantPropertiesFactory variantPropertiesFactoryFunc, archVariants []android.Module) *archTypeSpecificInfo { 1059 1060 // Create an arch specific info into which the variant properties can be copied. 1061 archInfo := &archTypeSpecificInfo{archType: archType} 1062 1063 // Create the properties into which the arch type specific properties will be 1064 // added. 1065 archInfo.Properties = variantPropertiesFactory() 1066 1067 if len(archVariants) == 1 { 1068 archInfo.Properties.PopulateFromVariant(ctx, archVariants[0]) 1069 } else { 1070 // There is more than one variant for this arch type which must be differentiated 1071 // by link type. 1072 for _, linkVariant := range archVariants { 1073 linkType := getLinkType(linkVariant) 1074 if linkType == "" { 1075 panic(fmt.Errorf("expected one arch specific variant as it is not identified by link type but found %d", len(archVariants))) 1076 } else { 1077 linkInfo := newLinkSpecificInfo(ctx, linkType, variantPropertiesFactory, linkVariant) 1078 1079 archInfo.linkInfos = append(archInfo.linkInfos, linkInfo) 1080 } 1081 } 1082 } 1083 1084 return archInfo 1085} 1086 1087func (archInfo *archTypeSpecificInfo) optimizableProperties() interface{} { 1088 return archInfo.Properties 1089} 1090 1091// Get the link type of the variant 1092// 1093// If the variant is not differentiated by link type then it returns "", 1094// otherwise it returns one of "static" or "shared". 1095func getLinkType(variant android.Module) string { 1096 linkType := "" 1097 if linkable, ok := variant.(cc.LinkableInterface); ok { 1098 if linkable.Shared() && linkable.Static() { 1099 panic(fmt.Errorf("expected variant %q to be either static or shared but was both", variant.String())) 1100 } else if linkable.Shared() { 1101 linkType = "shared" 1102 } else if linkable.Static() { 1103 linkType = "static" 1104 } else { 1105 panic(fmt.Errorf("expected variant %q to be either static or shared but was neither", variant.String())) 1106 } 1107 } 1108 return linkType 1109} 1110 1111// Optimize the properties by extracting common properties from link type specific 1112// properties into arch type specific properties. 1113func (archInfo *archTypeSpecificInfo) optimizeProperties(ctx *memberContext, commonValueExtractor *commonValueExtractor) { 1114 if len(archInfo.linkInfos) == 0 { 1115 return 1116 } 1117 1118 extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, archInfo.Properties, archInfo.linkInfos) 1119} 1120 1121// Add the properties for an arch type to a property set. 1122func (archInfo *archTypeSpecificInfo) addToPropertySet(ctx *memberContext, archPropertySet android.BpPropertySet, archOsPrefix string) { 1123 archTypeName := archInfo.archType.Name 1124 archTypePropertySet := archPropertySet.AddPropertySet(archOsPrefix + archTypeName) 1125 addSdkMemberPropertiesToSet(ctx, archInfo.Properties, archTypePropertySet) 1126 1127 for _, linkInfo := range archInfo.linkInfos { 1128 linkPropertySet := archTypePropertySet.AddPropertySet(linkInfo.linkType) 1129 addSdkMemberPropertiesToSet(ctx, linkInfo.Properties, linkPropertySet) 1130 } 1131} 1132 1133func (archInfo *archTypeSpecificInfo) String() string { 1134 return fmt.Sprintf("ArchType{%s}", archInfo.archType) 1135} 1136 1137type linkTypeSpecificInfo struct { 1138 baseInfo 1139 1140 linkType string 1141} 1142 1143var _ propertiesContainer = (*linkTypeSpecificInfo)(nil) 1144 1145// Create a new linkTypeSpecificInfo for the specified link type and its properties 1146// structures populated with information from the variant. 1147func newLinkSpecificInfo(ctx android.SdkMemberContext, linkType string, variantPropertiesFactory variantPropertiesFactoryFunc, linkVariant android.Module) *linkTypeSpecificInfo { 1148 linkInfo := &linkTypeSpecificInfo{ 1149 baseInfo: baseInfo{ 1150 // Create the properties into which the link type specific properties will be 1151 // added. 1152 Properties: variantPropertiesFactory(), 1153 }, 1154 linkType: linkType, 1155 } 1156 linkInfo.Properties.PopulateFromVariant(ctx, linkVariant) 1157 return linkInfo 1158} 1159 1160func (l *linkTypeSpecificInfo) String() string { 1161 return fmt.Sprintf("LinkType{%s}", l.linkType) 1162} 1163 1164type memberContext struct { 1165 sdkMemberContext android.ModuleContext 1166 builder *snapshotBuilder 1167 memberType android.SdkMemberType 1168 name string 1169} 1170 1171func (m *memberContext) SdkModuleContext() android.ModuleContext { 1172 return m.sdkMemberContext 1173} 1174 1175func (m *memberContext) SnapshotBuilder() android.SnapshotBuilder { 1176 return m.builder 1177} 1178 1179func (m *memberContext) MemberType() android.SdkMemberType { 1180 return m.memberType 1181} 1182 1183func (m *memberContext) Name() string { 1184 return m.name 1185} 1186 1187func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule android.BpModule) { 1188 1189 memberType := member.memberType 1190 1191 // Group the variants by os type. 1192 variantsByOsType := make(map[android.OsType][]android.Module) 1193 variants := member.Variants() 1194 for _, variant := range variants { 1195 osType := variant.Target().Os 1196 variantsByOsType[osType] = append(variantsByOsType[osType], variant) 1197 } 1198 1199 osCount := len(variantsByOsType) 1200 variantPropertiesFactory := func() android.SdkMemberProperties { 1201 properties := memberType.CreateVariantPropertiesStruct() 1202 base := properties.Base() 1203 base.Os_count = osCount 1204 return properties 1205 } 1206 1207 osTypeToInfo := make(map[android.OsType]*osTypeSpecificInfo) 1208 1209 // The set of properties that are common across all architectures and os types. 1210 commonProperties := variantPropertiesFactory() 1211 commonProperties.Base().Os = android.CommonOS 1212 1213 // Create common value extractor that can be used to optimize the properties. 1214 commonValueExtractor := newCommonValueExtractor(commonProperties) 1215 1216 // The list of property structures which are os type specific but common across 1217 // architectures within that os type. 1218 var osSpecificPropertiesContainers []*osTypeSpecificInfo 1219 1220 for osType, osTypeVariants := range variantsByOsType { 1221 osInfo := newOsTypeSpecificInfo(ctx, osType, variantPropertiesFactory, osTypeVariants) 1222 osTypeToInfo[osType] = osInfo 1223 // Add the os specific properties to a list of os type specific yet architecture 1224 // independent properties structs. 1225 osSpecificPropertiesContainers = append(osSpecificPropertiesContainers, osInfo) 1226 1227 // Optimize the properties across all the variants for a specific os type. 1228 osInfo.optimizeProperties(ctx, commonValueExtractor) 1229 } 1230 1231 // Extract properties which are common across all architectures and os types. 1232 extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers) 1233 1234 // Add the common properties to the module. 1235 addSdkMemberPropertiesToSet(ctx, commonProperties, bpModule) 1236 1237 // Create a target property set into which target specific properties can be 1238 // added. 1239 targetPropertySet := bpModule.AddPropertySet("target") 1240 1241 // Iterate over the os types in a fixed order. 1242 for _, osType := range s.getPossibleOsTypes() { 1243 osInfo := osTypeToInfo[osType] 1244 if osInfo == nil { 1245 continue 1246 } 1247 1248 osInfo.addToPropertySet(ctx, bpModule, targetPropertySet) 1249 } 1250} 1251 1252// Compute the list of possible os types that this sdk could support. 1253func (s *sdk) getPossibleOsTypes() []android.OsType { 1254 var osTypes []android.OsType 1255 for _, osType := range android.OsTypeList { 1256 if s.DeviceSupported() { 1257 if osType.Class == android.Device && osType != android.Fuchsia { 1258 osTypes = append(osTypes, osType) 1259 } 1260 } 1261 if s.HostSupported() { 1262 if osType.Class == android.Host || osType.Class == android.HostCross { 1263 osTypes = append(osTypes, osType) 1264 } 1265 } 1266 } 1267 sort.SliceStable(osTypes, func(i, j int) bool { return osTypes[i].Name < osTypes[j].Name }) 1268 return osTypes 1269} 1270 1271// Given a set of properties (struct value), return the value of the field within that 1272// struct (or one of its embedded structs). 1273type fieldAccessorFunc func(structValue reflect.Value) reflect.Value 1274 1275// Checks the metadata to determine whether the property should be ignored for the 1276// purposes of common value extraction or not. 1277type extractorMetadataPredicate func(metadata propertiesContainer) bool 1278 1279// Indicates whether optimizable properties are provided by a host variant or 1280// not. 1281type isHostVariant interface { 1282 isHostVariant() bool 1283} 1284 1285// A property that can be optimized by the commonValueExtractor. 1286type extractorProperty struct { 1287 // The name of the field for this property. 1288 name string 1289 1290 // Filter that can use metadata associated with the properties being optimized 1291 // to determine whether the field should be ignored during common value 1292 // optimization. 1293 filter extractorMetadataPredicate 1294 1295 // Retrieves the value on which common value optimization will be performed. 1296 getter fieldAccessorFunc 1297 1298 // The empty value for the field. 1299 emptyValue reflect.Value 1300 1301 // True if the property can support arch variants false otherwise. 1302 archVariant bool 1303} 1304 1305func (p extractorProperty) String() string { 1306 return p.name 1307} 1308 1309// Supports extracting common values from a number of instances of a properties 1310// structure into a separate common set of properties. 1311type commonValueExtractor struct { 1312 // The properties that the extractor can optimize. 1313 properties []extractorProperty 1314} 1315 1316// Create a new common value extractor for the structure type for the supplied 1317// properties struct. 1318// 1319// The returned extractor can be used on any properties structure of the same type 1320// as the supplied set of properties. 1321func newCommonValueExtractor(propertiesStruct interface{}) *commonValueExtractor { 1322 structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type() 1323 extractor := &commonValueExtractor{} 1324 extractor.gatherFields(structType, nil) 1325 return extractor 1326} 1327 1328// Gather the fields from the supplied structure type from which common values will 1329// be extracted. 1330// 1331// This is recursive function. If it encounters an embedded field (no field name) 1332// that is a struct then it will recurse into that struct passing in the accessor 1333// for the field. That will then be used in the accessors for the fields in the 1334// embedded struct. 1335func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingStructAccessor fieldAccessorFunc) { 1336 for f := 0; f < structType.NumField(); f++ { 1337 field := structType.Field(f) 1338 if field.PkgPath != "" { 1339 // Ignore unexported fields. 1340 continue 1341 } 1342 1343 // Ignore fields whose value should be kept. 1344 if proptools.HasTag(field, "sdk", "keep") { 1345 continue 1346 } 1347 1348 var filter extractorMetadataPredicate 1349 1350 // Add a filter 1351 if proptools.HasTag(field, "sdk", "ignored-on-host") { 1352 filter = func(metadata propertiesContainer) bool { 1353 if m, ok := metadata.(isHostVariant); ok { 1354 if m.isHostVariant() { 1355 return false 1356 } 1357 } 1358 return true 1359 } 1360 } 1361 1362 // Save a copy of the field index for use in the function. 1363 fieldIndex := f 1364 1365 name := field.Name 1366 1367 fieldGetter := func(value reflect.Value) reflect.Value { 1368 if containingStructAccessor != nil { 1369 // This is an embedded structure so first access the field for the embedded 1370 // structure. 1371 value = containingStructAccessor(value) 1372 } 1373 1374 // Skip through interface and pointer values to find the structure. 1375 value = getStructValue(value) 1376 1377 defer func() { 1378 if r := recover(); r != nil { 1379 panic(fmt.Errorf("%s for fieldIndex %d of field %s of value %#v", r, fieldIndex, name, value.Interface())) 1380 } 1381 }() 1382 1383 // Return the field. 1384 return value.Field(fieldIndex) 1385 } 1386 1387 if field.Type.Kind() == reflect.Struct && field.Anonymous { 1388 // Gather fields from the embedded structure. 1389 e.gatherFields(field.Type, fieldGetter) 1390 } else { 1391 property := extractorProperty{ 1392 name, 1393 filter, 1394 fieldGetter, 1395 reflect.Zero(field.Type), 1396 proptools.HasTag(field, "android", "arch_variant"), 1397 } 1398 e.properties = append(e.properties, property) 1399 } 1400 } 1401} 1402 1403func getStructValue(value reflect.Value) reflect.Value { 1404foundStruct: 1405 for { 1406 kind := value.Kind() 1407 switch kind { 1408 case reflect.Interface, reflect.Ptr: 1409 value = value.Elem() 1410 case reflect.Struct: 1411 break foundStruct 1412 default: 1413 panic(fmt.Errorf("expecting struct, interface or pointer, found %v of kind %s", value, kind)) 1414 } 1415 } 1416 return value 1417} 1418 1419// A container of properties to be optimized. 1420// 1421// Allows additional information to be associated with the properties, e.g. for 1422// filtering. 1423type propertiesContainer interface { 1424 fmt.Stringer 1425 1426 // Get the properties that need optimizing. 1427 optimizableProperties() interface{} 1428} 1429 1430// A wrapper for dynamic member properties to allow them to be optimized. 1431type dynamicMemberPropertiesContainer struct { 1432 sdkVariant *sdk 1433 dynamicMemberProperties interface{} 1434} 1435 1436func (c dynamicMemberPropertiesContainer) optimizableProperties() interface{} { 1437 return c.dynamicMemberProperties 1438} 1439 1440func (c dynamicMemberPropertiesContainer) String() string { 1441 return c.sdkVariant.String() 1442} 1443 1444// Extract common properties from a slice of property structures of the same type. 1445// 1446// All the property structures must be of the same type. 1447// commonProperties - must be a pointer to the structure into which common properties will be added. 1448// inputPropertiesSlice - must be a slice of propertiesContainer interfaces. 1449// 1450// Iterates over each exported field (capitalized name) and checks to see whether they 1451// have the same value (using DeepEquals) across all the input properties. If it does not then no 1452// change is made. Otherwise, the common value is stored in the field in the commonProperties 1453// and the field in each of the input properties structure is set to its default value. 1454func (e *commonValueExtractor) extractCommonProperties(commonProperties interface{}, inputPropertiesSlice interface{}) error { 1455 commonPropertiesValue := reflect.ValueOf(commonProperties) 1456 commonStructValue := commonPropertiesValue.Elem() 1457 1458 sliceValue := reflect.ValueOf(inputPropertiesSlice) 1459 1460 for _, property := range e.properties { 1461 fieldGetter := property.getter 1462 filter := property.filter 1463 if filter == nil { 1464 filter = func(metadata propertiesContainer) bool { 1465 return true 1466 } 1467 } 1468 1469 // Check to see if all the structures have the same value for the field. The commonValue 1470 // is nil on entry to the loop and if it is nil on exit then there is no common value or 1471 // all the values have been filtered out, otherwise it points to the common value. 1472 var commonValue *reflect.Value 1473 1474 // Assume that all the values will be the same. 1475 // 1476 // While similar to this is not quite the same as commonValue == nil. If all the values 1477 // have been filtered out then this will be false but commonValue == nil will be true. 1478 valuesDiffer := false 1479 1480 for i := 0; i < sliceValue.Len(); i++ { 1481 container := sliceValue.Index(i).Interface().(propertiesContainer) 1482 itemValue := reflect.ValueOf(container.optimizableProperties()) 1483 fieldValue := fieldGetter(itemValue) 1484 1485 if !filter(container) { 1486 expectedValue := property.emptyValue.Interface() 1487 actualValue := fieldValue.Interface() 1488 if !reflect.DeepEqual(expectedValue, actualValue) { 1489 return fmt.Errorf("field %q is supposed to be ignored for %q but is set to %#v instead of %#v", property, container, actualValue, expectedValue) 1490 } 1491 continue 1492 } 1493 1494 if commonValue == nil { 1495 // Use the first value as the commonProperties value. 1496 commonValue = &fieldValue 1497 } else { 1498 // If the value does not match the current common value then there is 1499 // no value in common so break out. 1500 if !reflect.DeepEqual(fieldValue.Interface(), commonValue.Interface()) { 1501 commonValue = nil 1502 valuesDiffer = true 1503 break 1504 } 1505 } 1506 } 1507 1508 // If the fields all have common value then store it in the common struct field 1509 // and set the input struct's field to the empty value. 1510 if commonValue != nil { 1511 emptyValue := property.emptyValue 1512 fieldGetter(commonStructValue).Set(*commonValue) 1513 for i := 0; i < sliceValue.Len(); i++ { 1514 container := sliceValue.Index(i).Interface().(propertiesContainer) 1515 itemValue := reflect.ValueOf(container.optimizableProperties()) 1516 fieldValue := fieldGetter(itemValue) 1517 fieldValue.Set(emptyValue) 1518 } 1519 } 1520 1521 if valuesDiffer && !property.archVariant { 1522 // The values differ but the property does not support arch variants so it 1523 // is an error. 1524 var details strings.Builder 1525 for i := 0; i < sliceValue.Len(); i++ { 1526 container := sliceValue.Index(i).Interface().(propertiesContainer) 1527 itemValue := reflect.ValueOf(container.optimizableProperties()) 1528 fieldValue := fieldGetter(itemValue) 1529 1530 _, _ = fmt.Fprintf(&details, "\n %q has value %q", container.String(), fieldValue.Interface()) 1531 } 1532 1533 return fmt.Errorf("field %q is not tagged as \"arch_variant\" but has arch specific properties:%s", property.String(), details.String()) 1534 } 1535 } 1536 1537 return nil 1538} 1539