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 "sort" 25 "strings" 26 27 rc_proto "android/soong/cmd/release_config/release_config_proto" 28 29 "google.golang.org/protobuf/proto" 30) 31 32// One directory's contribution to the a release config. 33type ReleaseConfigContribution struct { 34 // Path of the file providing this config contribution. 35 path string 36 37 // The index of the config directory where this release config 38 // contribution was declared. 39 // Flag values cannot be set in a location with a lower index. 40 DeclarationIndex int 41 42 // Protobufs relevant to the config. 43 proto rc_proto.ReleaseConfig 44 45 FlagValues []*FlagValue 46} 47 48// A generated release config. 49type ReleaseConfig struct { 50 // the Name of the release config 51 Name string 52 53 // The index of the config directory where this release config was 54 // first declared. 55 // Flag values cannot be set in a location with a lower index. 56 DeclarationIndex int 57 58 // What contributes to this config. 59 Contributions []*ReleaseConfigContribution 60 61 // Aliases for this release 62 OtherNames []string 63 64 // The names of release configs that we inherit 65 InheritNames []string 66 67 // True if this release config only allows inheritance and aconfig flag 68 // overrides. Build flag value overrides are an error. 69 AconfigFlagsOnly bool 70 71 // Unmarshalled flag artifacts 72 FlagArtifacts FlagArtifacts 73 74 // The files used by this release config 75 FilesUsedMap map[string]bool 76 77 // Generated release config 78 ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact 79 80 // We have begun compiling this release config. 81 compileInProgress bool 82 83 // Partitioned artifacts for {partition}/etc/build_flags.json 84 PartitionBuildFlags map[string]*rc_proto.FlagArtifacts 85 86 // Prior stage(s) for flag advancement (during development). 87 // Once a flag has met criteria in a prior stage, it can advance to this one. 88 PriorStagesMap map[string]bool 89} 90 91func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) { 92 return &ReleaseConfig{ 93 Name: name, 94 DeclarationIndex: index, 95 FilesUsedMap: make(map[string]bool), 96 PriorStagesMap: make(map[string]bool), 97 } 98} 99 100func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { 101 for f := range iConfig.FilesUsedMap { 102 config.FilesUsedMap[f] = true 103 } 104 for _, fa := range iConfig.FlagArtifacts { 105 name := *fa.FlagDeclaration.Name 106 myFa, ok := config.FlagArtifacts[name] 107 if !ok { 108 return fmt.Errorf("Could not inherit flag %s from %s", name, iConfig.Name) 109 } 110 if name == "RELEASE_ACONFIG_VALUE_SETS" { 111 // If there is a value assigned, add the trace. 112 if len(fa.Value.GetStringValue()) > 0 { 113 myFa.Traces = append(myFa.Traces, fa.Traces...) 114 myFa.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{ 115 myFa.Value.GetStringValue() + " " + fa.Value.GetStringValue()}} 116 } 117 } else if len(fa.Traces) > 1 { 118 // A value was assigned. Set our value. 119 myFa.Traces = append(myFa.Traces, fa.Traces[1:]...) 120 myFa.Value = fa.Value 121 } 122 } 123 return nil 124} 125 126func (config *ReleaseConfig) GetSortedFileList() []string { 127 return SortedMapKeys(config.FilesUsedMap) 128} 129 130func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error { 131 if config.ReleaseConfigArtifact != nil { 132 return nil 133 } 134 if config.compileInProgress { 135 return fmt.Errorf("Loop detected for release config %s", config.Name) 136 } 137 config.compileInProgress = true 138 isRoot := config.Name == "root" 139 140 // Is this a build-prefix release config, such as 'ap3a'? 141 isBuildPrefix, err := regexp.MatchString("^[a-z][a-z][0-9][0-9a-z]$", config.Name) 142 if err != nil { 143 return err 144 } 145 // Start with only the flag declarations. 146 config.FlagArtifacts = configs.FlagArtifacts.Clone() 147 releaseAconfigValueSets := config.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"] 148 releasePlatformVersion := config.FlagArtifacts["RELEASE_PLATFORM_VERSION"] 149 150 // Generate any configs we need to inherit. This will detect loops in 151 // the config. 152 contributionsToApply := []*ReleaseConfigContribution{} 153 myInherits := []string{} 154 myInheritsSet := make(map[string]bool) 155 // If there is a "root" release config, it is the start of every inheritance chain. 156 _, err = configs.GetReleaseConfig("root") 157 if err == nil && !isRoot { 158 config.InheritNames = append([]string{"root"}, config.InheritNames...) 159 } 160 for _, inherit := range config.InheritNames { 161 if _, ok := myInheritsSet[inherit]; ok { 162 continue 163 } 164 if isBuildPrefix && configs.Aliases[inherit] != nil { 165 return fmt.Errorf("%s cannot inherit from alias %s", config.Name, inherit) 166 } 167 myInherits = append(myInherits, inherit) 168 myInheritsSet[inherit] = true 169 iConfig, err := configs.GetReleaseConfig(inherit) 170 if err != nil { 171 return err 172 } 173 err = iConfig.GenerateReleaseConfig(configs) 174 if err != nil { 175 return err 176 } 177 err = config.InheritConfig(iConfig) 178 if err != nil { 179 return err 180 } 181 } 182 183 // If we inherited nothing, then we need to mark the global files as used for this 184 // config. If we inherited, then we already marked them as part of inheritance. 185 if len(config.InheritNames) == 0 { 186 for f := range configs.FilesUsedMap { 187 config.FilesUsedMap[f] = true 188 } 189 } 190 191 contributionsToApply = append(contributionsToApply, config.Contributions...) 192 193 workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL) 194 myDirsMap := make(map[int]bool) 195 if isBuildPrefix && releasePlatformVersion != nil { 196 if MarshalValue(releasePlatformVersion.Value) != strings.ToUpper(config.Name) { 197 value := FlagValue{ 198 path: config.Contributions[0].path, 199 proto: rc_proto.FlagValue{ 200 Name: releasePlatformVersion.FlagDeclaration.Name, 201 Value: UnmarshalValue(strings.ToUpper(config.Name)), 202 }, 203 } 204 if err := releasePlatformVersion.UpdateValue(value); err != nil { 205 return err 206 } 207 } 208 } 209 for _, contrib := range contributionsToApply { 210 contribAconfigValueSets := []string{} 211 // Gather the aconfig_value_sets from this contribution, allowing duplicates for simplicity. 212 for _, v := range contrib.proto.AconfigValueSets { 213 contribAconfigValueSets = append(contribAconfigValueSets, v) 214 } 215 contribAconfigValueSetsString := strings.Join(contribAconfigValueSets, " ") 216 releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{ 217 releaseAconfigValueSets.Value.GetStringValue() + " " + contribAconfigValueSetsString}} 218 releaseAconfigValueSets.Traces = append( 219 releaseAconfigValueSets.Traces, 220 &rc_proto.Tracepoint{ 221 Source: proto.String(contrib.path), 222 Value: &rc_proto.Value{Val: &rc_proto.Value_StringValue{contribAconfigValueSetsString}}, 223 }) 224 225 for _, priorStage := range contrib.proto.PriorStages { 226 config.PriorStagesMap[priorStage] = true 227 } 228 myDirsMap[contrib.DeclarationIndex] = true 229 if config.AconfigFlagsOnly && len(contrib.FlagValues) > 0 { 230 return fmt.Errorf("%s does not allow build flag overrides", config.Name) 231 } 232 for _, value := range contrib.FlagValues { 233 name := *value.proto.Name 234 fa, ok := config.FlagArtifacts[name] 235 if !ok { 236 return fmt.Errorf("Setting value for undefined flag %s in %s\n", name, value.path) 237 } 238 myDirsMap[fa.DeclarationIndex] = true 239 if fa.DeclarationIndex > contrib.DeclarationIndex { 240 // Setting location is to the left of declaration. 241 return fmt.Errorf("Setting value for flag %s not allowed in %s\n", name, value.path) 242 } 243 if isRoot && *fa.FlagDeclaration.Workflow != workflowManual { 244 // The "root" release config can only contain workflow: MANUAL flags. 245 return fmt.Errorf("Setting value for non-MANUAL flag %s is not allowed in %s", name, value.path) 246 } 247 if err := fa.UpdateValue(*value); err != nil { 248 return err 249 } 250 if fa.Redacted { 251 delete(config.FlagArtifacts, name) 252 } 253 } 254 } 255 // Now remove any duplicates from the actual value of RELEASE_ACONFIG_VALUE_SETS 256 myAconfigValueSets := []string{} 257 myAconfigValueSetsMap := map[string]bool{} 258 for _, v := range strings.Split(releaseAconfigValueSets.Value.GetStringValue(), " ") { 259 if myAconfigValueSetsMap[v] { 260 continue 261 } 262 myAconfigValueSetsMap[v] = true 263 myAconfigValueSets = append(myAconfigValueSets, v) 264 } 265 releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.TrimSpace(strings.Join(myAconfigValueSets, " "))}} 266 267 directories := []string{} 268 for idx, confDir := range configs.configDirs { 269 if _, ok := myDirsMap[idx]; ok { 270 directories = append(directories, confDir) 271 } 272 } 273 274 // Now build the per-partition artifacts 275 config.PartitionBuildFlags = make(map[string]*rc_proto.FlagArtifacts) 276 for _, v := range config.FlagArtifacts { 277 artifact, err := v.MarshalWithoutTraces() 278 if err != nil { 279 return err 280 } 281 for _, container := range v.FlagDeclaration.Containers { 282 if _, ok := config.PartitionBuildFlags[container]; !ok { 283 config.PartitionBuildFlags[container] = &rc_proto.FlagArtifacts{} 284 } 285 config.PartitionBuildFlags[container].FlagArtifacts = append(config.PartitionBuildFlags[container].FlagArtifacts, artifact) 286 } 287 } 288 config.ReleaseConfigArtifact = &rc_proto.ReleaseConfigArtifact{ 289 Name: proto.String(config.Name), 290 OtherNames: config.OtherNames, 291 FlagArtifacts: func() []*rc_proto.FlagArtifact { 292 ret := []*rc_proto.FlagArtifact{} 293 flagNames := []string{} 294 for k := range config.FlagArtifacts { 295 flagNames = append(flagNames, k) 296 } 297 sort.Strings(flagNames) 298 for _, flagName := range flagNames { 299 flag := config.FlagArtifacts[flagName] 300 ret = append(ret, &rc_proto.FlagArtifact{ 301 FlagDeclaration: flag.FlagDeclaration, 302 Traces: flag.Traces, 303 Value: flag.Value, 304 }) 305 } 306 return ret 307 }(), 308 AconfigValueSets: myAconfigValueSets, 309 Inherits: myInherits, 310 Directories: directories, 311 PriorStages: SortedMapKeys(config.PriorStagesMap), 312 } 313 314 config.compileInProgress = false 315 return nil 316} 317 318// Write the makefile for this targetRelease. 319func (config *ReleaseConfig) WriteMakefile(outFile, targetRelease string, configs *ReleaseConfigs) error { 320 makeVars := make(map[string]string) 321 322 myFlagArtifacts := config.FlagArtifacts.Clone() 323 // Sort the flags by name first. 324 names := myFlagArtifacts.SortedFlagNames() 325 partitions := make(map[string][]string) 326 327 vNames := []string{} 328 addVar := func(name, suffix, value string) { 329 fullName := fmt.Sprintf("_ALL_RELEASE_FLAGS.%s.%s", name, suffix) 330 vNames = append(vNames, fullName) 331 makeVars[fullName] = value 332 } 333 334 for _, name := range names { 335 flag := myFlagArtifacts[name] 336 decl := flag.FlagDeclaration 337 338 for _, container := range decl.Containers { 339 partitions[container] = append(partitions[container], name) 340 } 341 value := MarshalValue(flag.Value) 342 makeVars[name] = value 343 addVar(name, "TYPE", ValueType(flag.Value)) 344 addVar(name, "PARTITIONS", strings.Join(decl.Containers, " ")) 345 addVar(name, "DEFAULT", MarshalValue(decl.Value)) 346 addVar(name, "VALUE", value) 347 addVar(name, "DECLARED_IN", *flag.Traces[0].Source) 348 addVar(name, "SET_IN", *flag.Traces[len(flag.Traces)-1].Source) 349 addVar(name, "NAMESPACE", *decl.Namespace) 350 } 351 pNames := []string{} 352 for k := range partitions { 353 pNames = append(pNames, k) 354 } 355 slices.Sort(pNames) 356 357 // Now sort the make variables, and output them. 358 slices.Sort(vNames) 359 360 // Write the flags as: 361 // _ALL_RELELASE_FLAGS 362 // _ALL_RELEASE_FLAGS.PARTITIONS.* 363 // all _ALL_RELEASE_FLAGS.*, sorted by name 364 // Final flag values, sorted by name. 365 data := fmt.Sprintf("# TARGET_RELEASE=%s\n", config.Name) 366 if targetRelease != config.Name { 367 data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease) 368 } 369 // As it stands this list is not per-product, but conceptually it is, and will be. 370 data += fmt.Sprintf("ALL_RELEASE_CONFIGS_FOR_PRODUCT :=$= %s\n", strings.Join(configs.GetAllReleaseNames(), " ")) 371 data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " ")) 372 data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " ")) 373 for _, pName := range pNames { 374 data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " ")) 375 } 376 for _, vName := range vNames { 377 data += fmt.Sprintf("%s :=$= %s\n", vName, makeVars[vName]) 378 } 379 data += "\n\n# Values for all build flags\n" 380 for _, name := range names { 381 data += fmt.Sprintf("%s :=$= %s\n", name, makeVars[name]) 382 } 383 return os.WriteFile(outFile, []byte(data), 0644) 384} 385 386func (config *ReleaseConfig) WritePartitionBuildFlags(outDir string) error { 387 var err error 388 for partition, flags := range config.PartitionBuildFlags { 389 slices.SortFunc(flags.FlagArtifacts, func(a, b *rc_proto.FlagArtifact) int { 390 return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name) 391 }) 392 // The json file name must not be modified as this is read from 393 // build_flags_json module 394 if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s.json", partition)), flags); err != nil { 395 return err 396 } 397 } 398 return nil 399} 400