1// Copyright 2024 Google Inc. All rights reserved. 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 release_config_lib 16 17import ( 18 "cmp" 19 "fmt" 20 "os" 21 "path/filepath" 22 "regexp" 23 "slices" 24 "strings" 25 26 rc_proto "android/soong/cmd/release_config/release_config_proto" 27 28 "google.golang.org/protobuf/proto" 29) 30 31// One directory's contribution to the a release config. 32type ReleaseConfigContribution struct { 33 // Path of the file providing this config contribution. 34 path string 35 36 // The index of the config directory where this release config 37 // contribution was declared. 38 // Flag values cannot be set in a location with a lower index. 39 DeclarationIndex int 40 41 // Protobufs relevant to the config. 42 proto rc_proto.ReleaseConfig 43 44 FlagValues []*FlagValue 45} 46 47// A generated release config. 48type ReleaseConfig struct { 49 // the Name of the release config 50 Name string 51 52 // The index of the config directory where this release config was 53 // first declared. 54 // Flag values cannot be set in a location with a lower index. 55 DeclarationIndex int 56 57 // What contributes to this config. 58 Contributions []*ReleaseConfigContribution 59 60 // Aliases for this release 61 OtherNames []string 62 63 // The names of release configs that we inherit 64 InheritNames []string 65 66 // True if this release config only allows inheritance and aconfig flag 67 // overrides. Build flag value overrides are an error. 68 AconfigFlagsOnly bool 69 70 // Unmarshalled flag artifacts 71 FlagArtifacts FlagArtifacts 72 73 // The files used by this release config 74 FilesUsedMap map[string]bool 75 76 // Generated release config 77 ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact 78 79 // We have begun compiling this release config. 80 compileInProgress bool 81 82 // Partitioned artifacts for {partition}/etc/build_flags.json 83 PartitionBuildFlags map[string]*rc_proto.FlagArtifacts 84 85 // Prior stage(s) for flag advancement (during development). 86 // Once a flag has met criteria in a prior stage, it can advance to this one. 87 PriorStagesMap map[string]bool 88 89 // What type of release config is this? This should never be 90 // ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED. 91 ReleaseConfigType rc_proto.ReleaseConfigType 92} 93 94// If true, this is a proper release config that can be used in "lunch". 95func (config *ReleaseConfig) isConfigListable() bool { 96 switch config.ReleaseConfigType { 97 case rc_proto.ReleaseConfigType_RELEASE_CONFIG: 98 return true 99 } 100 101 return false 102} 103 104// If true, this ReleaseConfigType may only inherit from a ReleaseConfig of the 105// same ReleaseConfigType. 106var ReleaseConfigInheritanceDenyMap = map[rc_proto.ReleaseConfigType]bool{ 107 rc_proto.ReleaseConfigType_BUILD_VARIANT: true, 108} 109 110func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) { 111 return &ReleaseConfig{ 112 Name: name, 113 DeclarationIndex: index, 114 FilesUsedMap: make(map[string]bool), 115 PriorStagesMap: make(map[string]bool), 116 } 117} 118 119func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { 120 if config.ReleaseConfigType != iConfig.ReleaseConfigType && ReleaseConfigInheritanceDenyMap[config.ReleaseConfigType] { 121 return fmt.Errorf("Release config %s (type '%s') cannot inherit from %s (type '%s')", 122 config.Name, config.ReleaseConfigType, iConfig.Name, iConfig.ReleaseConfigType) 123 } 124 for f := range iConfig.FilesUsedMap { 125 config.FilesUsedMap[f] = true 126 } 127 for _, fa := range iConfig.FlagArtifacts { 128 name := *fa.FlagDeclaration.Name 129 myFa, ok := config.FlagArtifacts[name] 130 if !ok { 131 return fmt.Errorf("Could not inherit flag %s from %s", name, iConfig.Name) 132 } 133 if fa.Redacted { 134 myFa.Redact() 135 } 136 if name == "RELEASE_ACONFIG_VALUE_SETS" { 137 // If there is a value assigned, add the trace. 138 if len(fa.Value.GetStringValue()) > 0 { 139 myFa.Traces = append(myFa.Traces, fa.Traces...) 140 myFa.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{ 141 myFa.Value.GetStringValue() + " " + fa.Value.GetStringValue()}} 142 } 143 } else if len(fa.Traces) > 1 { 144 // A value was assigned. Set our value. 145 myFa.Traces = append(myFa.Traces, fa.Traces[1:]...) 146 myFa.Value = fa.Value 147 } 148 } 149 return nil 150} 151 152func (config *ReleaseConfig) GetSortedFileList() []string { 153 return SortedMapKeys(config.FilesUsedMap) 154} 155 156func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error { 157 if config.ReleaseConfigArtifact != nil { 158 return nil 159 } 160 if config.compileInProgress { 161 return fmt.Errorf("Loop detected for release config %s", config.Name) 162 } 163 config.compileInProgress = true 164 isRoot := config.Name == "root" 165 166 // Is this a build-prefix release config, such as 'ap3a'? 167 isBuildPrefix, err := regexp.MatchString("^[a-z][a-z][0-9][0-9a-z]$", config.Name) 168 if err != nil { 169 return err 170 } 171 // Start with only the flag declarations. 172 config.FlagArtifacts = configs.FlagArtifacts.Clone() 173 releaseAconfigValueSets := config.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"] 174 releasePlatformVersion := config.FlagArtifacts["RELEASE_PLATFORM_VERSION"] 175 176 // Generate any configs we need to inherit. This will detect loops in 177 // the config. 178 contributionsToApply := []*ReleaseConfigContribution{} 179 myInherits := []string{} 180 myInheritsSet := make(map[string]bool) 181 if config.ReleaseConfigType == rc_proto.ReleaseConfigType_RELEASE_CONFIG { 182 if _, err = configs.GetReleaseConfigStrict("root"); err == nil { 183 config.InheritNames = append([]string{"root"}, config.InheritNames...) 184 } 185 } 186 for _, inherit := range config.InheritNames { 187 if _, ok := myInheritsSet[inherit]; ok { 188 continue 189 } 190 if isBuildPrefix && configs.Aliases[inherit] != nil { 191 return fmt.Errorf("%s cannot inherit from alias %s", config.Name, inherit) 192 } 193 myInherits = append(myInherits, inherit) 194 myInheritsSet[inherit] = true 195 // TODO: there are some configs that rely on vgsbr being 196 // present on branches where it isn't. Once the broken configs 197 // are fixed, we can be more strict. In the meantime, they 198 // will wind up inheriting `trunk_stable` instead of the 199 // non-existent (alias) that they reference today. Once fixed, 200 // this becomes: 201 // iConfig, err := configs.GetReleaseConfigStrict(inherit) 202 iConfig, err := configs.GetReleaseConfig(inherit) 203 if err != nil { 204 return err 205 } 206 err = iConfig.GenerateReleaseConfig(configs) 207 if err != nil { 208 return err 209 } 210 err = config.InheritConfig(iConfig) 211 if err != nil { 212 return err 213 } 214 } 215 216 // If we inherited nothing, then we need to mark the global files as used for this 217 // config. If we inherited, then we already marked them as part of inheritance. 218 if len(config.InheritNames) == 0 { 219 for f := range configs.FilesUsedMap { 220 config.FilesUsedMap[f] = true 221 } 222 } 223 224 contributionsToApply = append(contributionsToApply, config.Contributions...) 225 226 workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL) 227 myDirsMap := make(map[int]bool) 228 myValueDirsMap := make(map[int]bool) 229 if isBuildPrefix && releasePlatformVersion != nil { 230 if MarshalValue(releasePlatformVersion.Value) != strings.ToUpper(config.Name) { 231 value := FlagValue{ 232 path: config.Contributions[0].path, 233 proto: rc_proto.FlagValue{ 234 Name: releasePlatformVersion.FlagDeclaration.Name, 235 Value: UnmarshalValue(strings.ToUpper(config.Name)), 236 }, 237 } 238 if err := releasePlatformVersion.UpdateValue(value); err != nil { 239 return err 240 } 241 } 242 } 243 for _, contrib := range contributionsToApply { 244 contribAconfigValueSets := []string{} 245 // Gather the aconfig_value_sets from this contribution, allowing duplicates for simplicity. 246 for _, v := range contrib.proto.AconfigValueSets { 247 contribAconfigValueSets = append(contribAconfigValueSets, v) 248 } 249 contribAconfigValueSetsString := strings.Join(contribAconfigValueSets, " ") 250 releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{ 251 releaseAconfigValueSets.Value.GetStringValue() + " " + contribAconfigValueSetsString}} 252 releaseAconfigValueSets.Traces = append( 253 releaseAconfigValueSets.Traces, 254 &rc_proto.Tracepoint{ 255 Source: proto.String(contrib.path), 256 Value: &rc_proto.Value{Val: &rc_proto.Value_StringValue{contribAconfigValueSetsString}}, 257 }) 258 259 for _, priorStage := range contrib.proto.PriorStages { 260 config.PriorStagesMap[priorStage] = true 261 } 262 myDirsMap[contrib.DeclarationIndex] = true 263 // This path *could* provide a value for this release config. 264 myValueDirsMap[contrib.DeclarationIndex] = true 265 if config.AconfigFlagsOnly { 266 // AconfigFlagsOnly allows very very few build flag values, all of them are part of aconfig flags. 267 allowedFlags := map[string]bool{ 268 "RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": true, 269 } 270 for _, fv := range contrib.FlagValues { 271 if !allowedFlags[*fv.proto.Name] { 272 return fmt.Errorf("%s does not allow build flag overrides", config.Name) 273 } 274 } 275 } 276 for _, value := range contrib.FlagValues { 277 name := *value.proto.Name 278 fa, ok := config.FlagArtifacts[name] 279 if !ok { 280 return fmt.Errorf("Setting value for undefined flag %s in %s\n", name, value.path) 281 } 282 // Record that flag declarations from fa.DeclarationIndex were included in this release config. 283 myDirsMap[fa.DeclarationIndex] = true 284 // Do not set myValueDirsMap, since it just records that we *could* provide values here. 285 if fa.DeclarationIndex > contrib.DeclarationIndex { 286 // Setting location is to the left of declaration. 287 return fmt.Errorf("Setting value for flag %s (declared in %s) not allowed in %s\n", 288 name, filepath.Dir(configs.ReleaseConfigMaps[fa.DeclarationIndex].path), value.path) 289 } 290 if isRoot && *fa.FlagDeclaration.Workflow != workflowManual { 291 // The "root" release config can only contain workflow: MANUAL flags. 292 return fmt.Errorf("Setting value for non-MANUAL flag %s is not allowed in %s", name, value.path) 293 } 294 if err := fa.UpdateValue(*value); err != nil { 295 return err 296 } 297 } 298 } 299 300 if config.ReleaseConfigType == rc_proto.ReleaseConfigType_RELEASE_CONFIG { 301 inheritBuildVariant := func() error { 302 build_variant := os.Getenv("TARGET_BUILD_VARIANT") 303 if build_variant == "" || config.Name == build_variant { 304 return nil 305 } 306 variant, err := configs.GetReleaseConfigStrict(build_variant) 307 if err != nil { 308 // Failure to find the build-variant release config is 309 // not an error. 310 return nil 311 } 312 if variant.ReleaseConfigType != rc_proto.ReleaseConfigType_BUILD_VARIANT { 313 return nil 314 } 315 if err = variant.GenerateReleaseConfig(configs); err != nil { 316 return err 317 } 318 return config.InheritConfig(variant) 319 } 320 321 useVariant, ok := config.FlagArtifacts["RELEASE_BUILD_USE_VARIANT_FLAGS"] 322 if ok && MarshalValue(useVariant.Value) != "" { 323 if err = inheritBuildVariant(); err != nil { 324 return err 325 } 326 } 327 } 328 329 // Now remove any duplicates from the actual value of RELEASE_ACONFIG_VALUE_SETS 330 myAconfigValueSets := []string{} 331 myAconfigValueSetsMap := map[string]bool{} 332 for _, v := range strings.Split(releaseAconfigValueSets.Value.GetStringValue(), " ") { 333 if v == "" || myAconfigValueSetsMap[v] { 334 continue 335 } 336 myAconfigValueSetsMap[v] = true 337 myAconfigValueSets = append(myAconfigValueSets, v) 338 } 339 releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.TrimSpace(strings.Join(myAconfigValueSets, " "))}} 340 341 directories := []string{} 342 valueDirectories := []string{} 343 // These path prefixes are exclusive for a release config. 344 // "A release config shall exist in at most one of these." 345 // If we find a benefit to generalizing this, we can do so at that time. 346 exclusiveDirPrefixes := []string{ 347 "build/release", 348 "vendor/google_shared/build/release", 349 } 350 var exclusiveDir string 351 for idx, confDir := range configs.configDirs { 352 if _, ok := myDirsMap[idx]; ok { 353 directories = append(directories, confDir) 354 } 355 if _, ok := myValueDirsMap[idx]; ok { 356 for _, dir := range exclusiveDirPrefixes { 357 if strings.HasPrefix(confDir, dir) { 358 if exclusiveDir != "" && !strings.HasPrefix(exclusiveDir, dir) { 359 return fmt.Errorf("%s is declared in both %s and %s", 360 config.Name, exclusiveDir, confDir) 361 } 362 exclusiveDir = confDir 363 } 364 } 365 valueDirectories = append(valueDirectories, confDir) 366 } 367 } 368 369 // Now build the per-partition artifacts 370 config.PartitionBuildFlags = make(map[string]*rc_proto.FlagArtifacts) 371 for _, v := range config.FlagArtifacts { 372 artifact, err := v.MarshalWithoutTraces() 373 if err != nil { 374 return err 375 } 376 // Redacted flags return nil when rendered. 377 if artifact == nil { 378 continue 379 } 380 for _, container := range v.FlagDeclaration.Containers { 381 if _, ok := config.PartitionBuildFlags[container]; !ok { 382 config.PartitionBuildFlags[container] = &rc_proto.FlagArtifacts{} 383 } 384 config.PartitionBuildFlags[container].Flags = append(config.PartitionBuildFlags[container].Flags, artifact) 385 } 386 } 387 config.ReleaseConfigArtifact = &rc_proto.ReleaseConfigArtifact{ 388 Name: proto.String(config.Name), 389 OtherNames: config.OtherNames, 390 Flags: func() []*rc_proto.FlagArtifact { 391 ret := []*rc_proto.FlagArtifact{} 392 for _, flagName := range config.FlagArtifacts.SortedFlagNames() { 393 flag := config.FlagArtifacts[flagName] 394 ret = append(ret, &rc_proto.FlagArtifact{ 395 FlagDeclaration: flag.FlagDeclaration, 396 Traces: flag.Traces, 397 Value: flag.Value, 398 }) 399 } 400 return ret 401 }(), 402 AconfigValueSets: myAconfigValueSets, 403 Inherits: myInherits, 404 Directories: directories, 405 ValueDirectories: valueDirectories, 406 PriorStages: SortedMapKeys(config.PriorStagesMap), 407 ReleaseConfigType: config.ReleaseConfigType.Enum(), 408 } 409 410 config.compileInProgress = false 411 return nil 412} 413 414// Write the makefile for this targetRelease. 415func (config *ReleaseConfig) WriteMakefile(outFile, targetRelease string, configs *ReleaseConfigs) error { 416 makeVars := make(map[string]string) 417 418 myFlagArtifacts := config.FlagArtifacts.Clone() 419 420 // Add any RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS variables. 421 var extraAconfigReleaseConfigs []string 422 if extraAconfigValueSetsValue, ok := config.FlagArtifacts["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok { 423 if val := MarshalValue(extraAconfigValueSetsValue.Value); len(val) > 0 { 424 extraAconfigReleaseConfigs = strings.Split(val, " ") 425 } 426 } 427 for _, rcName := range extraAconfigReleaseConfigs { 428 rc, err := configs.GetReleaseConfigStrict(rcName) 429 if err != nil { 430 return err 431 } 432 myFlagArtifacts["RELEASE_ACONFIG_VALUE_SETS_"+rcName] = rc.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"] 433 myFlagArtifacts["RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_"+rcName] = rc.FlagArtifacts["RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION"] 434 } 435 436 // Sort the flags by name first. 437 names := myFlagArtifacts.SortedFlagNames() 438 partitions := make(map[string][]string) 439 440 vNames := []string{} 441 addVar := func(name, suffix, value string) { 442 fullName := fmt.Sprintf("_ALL_RELEASE_FLAGS.%s.%s", name, suffix) 443 vNames = append(vNames, fullName) 444 makeVars[fullName] = value 445 } 446 447 for _, name := range names { 448 flag := myFlagArtifacts[name] 449 decl := flag.FlagDeclaration 450 451 for _, container := range decl.Containers { 452 partitions[container] = append(partitions[container], name) 453 } 454 value := MarshalValue(flag.Value) 455 makeVars[name] = value 456 addVar(name, "TYPE", ValueType(flag.Value)) 457 addVar(name, "PARTITIONS", strings.Join(decl.Containers, " ")) 458 addVar(name, "DEFAULT", MarshalValue(decl.Value)) 459 addVar(name, "VALUE", value) 460 addVar(name, "DECLARED_IN", *flag.Traces[0].Source) 461 addVar(name, "SET_IN", *flag.Traces[len(flag.Traces)-1].Source) 462 addVar(name, "NAMESPACE", *decl.Namespace) 463 } 464 pNames := []string{} 465 for k := range partitions { 466 pNames = append(pNames, k) 467 } 468 slices.Sort(pNames) 469 470 // Now sort the make variables, and output them. 471 slices.Sort(vNames) 472 473 // Write the flags as: 474 // _ALL_RELELASE_FLAGS 475 // _ALL_RELEASE_FLAGS.PARTITIONS.* 476 // all _ALL_RELEASE_FLAGS.*, sorted by name 477 // Final flag values, sorted by name. 478 data := fmt.Sprintf("# TARGET_RELEASE=%s\n", config.Name) 479 if targetRelease != config.Name { 480 data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease) 481 } 482 // As it stands this list is not per-product, but conceptually it is, and will be. 483 data += fmt.Sprintf("ALL_RELEASE_CONFIGS_FOR_PRODUCT :=$= %s\n", strings.Join(configs.GetAllReleaseNames(), " ")) 484 data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " ")) 485 data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " ")) 486 for _, pName := range pNames { 487 data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " ")) 488 } 489 for _, vName := range vNames { 490 data += fmt.Sprintf("%s :=$= %s\n", vName, makeVars[vName]) 491 } 492 data += "\n\n# Values for all build flags\n" 493 for _, name := range names { 494 data += fmt.Sprintf("%s :=$= %s\n", name, makeVars[name]) 495 } 496 return os.WriteFile(outFile, []byte(data), 0644) 497} 498 499func (config *ReleaseConfig) WritePartitionBuildFlags(outDir string) error { 500 var err error 501 for partition, flags := range config.PartitionBuildFlags { 502 slices.SortFunc(flags.Flags, func(a, b *rc_proto.FlagArtifact) int { 503 return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name) 504 }) 505 // The json file name must not be modified as this is read from 506 // build_flags_json module 507 if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s.json", partition)), flags); err != nil { 508 return err 509 } 510 } 511 return nil 512} 513