release_config: make the code more sharable
This will allow the `build-flag` command to use release_config_lib code. Bug: 328495189 Test: manual Change-Id: If068597b1d68c52c941788931997b7f77c73d2b3
This commit is contained in:
parent
e6f5468ef2
commit
ac79679f77
12 changed files with 622 additions and 395 deletions
|
@ -1,30 +0,0 @@
|
|||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
bootstrap_go_package {
|
||||
name: "release-config",
|
||||
pkgPath: "android/soong/cmd/release_config",
|
||||
deps: [
|
||||
"golang-protobuf-encoding-prototext",
|
||||
"golang-protobuf-reflect-protoreflect",
|
||||
"golang-protobuf-runtime-protoimpl",
|
||||
"soong-cmd-release-config-proto",
|
||||
],
|
||||
srcs: [
|
||||
"main.go",
|
||||
],
|
||||
}
|
||||
|
||||
bootstrap_go_package {
|
||||
name: "soong-cmd-release-config-proto",
|
||||
pkgPath: "android/soong/cmd/release_config/release_config_proto",
|
||||
deps: [
|
||||
"golang-protobuf-reflect-protoreflect",
|
||||
"golang-protobuf-runtime-protoimpl",
|
||||
],
|
||||
srcs: [
|
||||
"release_config_proto/build_flags_out.pb.go",
|
||||
"release_config_proto/build_flags_src.pb.go",
|
||||
],
|
||||
}
|
18
cmd/release_config/release_config/Android.bp
Normal file
18
cmd/release_config/release_config/Android.bp
Normal file
|
@ -0,0 +1,18 @@
|
|||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
bootstrap_go_package {
|
||||
name: "soong-cmd-release_config-release_config",
|
||||
pkgPath: "android/soong/cmd/release_config/release_config",
|
||||
deps: [
|
||||
"golang-protobuf-encoding-prototext",
|
||||
"golang-protobuf-reflect-protoreflect",
|
||||
"golang-protobuf-runtime-protoimpl",
|
||||
"soong-cmd-release_config-proto",
|
||||
"soong-cmd-release_config-lib",
|
||||
],
|
||||
srcs: [
|
||||
"main.go",
|
||||
],
|
||||
}
|
57
cmd/release_config/release_config/main.go
Normal file
57
cmd/release_config/release_config/main.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
rc_lib "android/soong/cmd/release_config/release_config_lib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var top string
|
||||
var releaseConfigMapPaths rc_lib.StringList
|
||||
var targetRelease string
|
||||
var outputDir string
|
||||
var err error
|
||||
var configs *rc_lib.ReleaseConfigs
|
||||
|
||||
flag.StringVar(&top, "top", ".", "path to top of workspace")
|
||||
flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
|
||||
flag.StringVar(&targetRelease, "release", "trunk_staging", "TARGET_RELEASE for this build")
|
||||
flag.StringVar(&outputDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
|
||||
flag.Parse()
|
||||
|
||||
if err = os.Chdir(top); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = os.MkdirAll(outputDir, 0775)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = configs.DumpMakefile(outputDir, targetRelease)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = configs.DumpArtifact(outputDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
36
cmd/release_config/release_config_lib/Android.bp
Normal file
36
cmd/release_config/release_config_lib/Android.bp
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
bootstrap_go_package {
|
||||
name: "soong-cmd-release_config-lib",
|
||||
pkgPath: "android/soong/release_config/release_config_lib",
|
||||
deps: [
|
||||
"golang-protobuf-encoding-prototext",
|
||||
"golang-protobuf-reflect-protoreflect",
|
||||
"golang-protobuf-runtime-protoimpl",
|
||||
"soong-cmd-release_config-proto",
|
||||
],
|
||||
srcs: [
|
||||
"flag_artifact.go",
|
||||
"flag_declaration.go",
|
||||
"flag_value.go",
|
||||
"release_config.go",
|
||||
"release_configs.go",
|
||||
"util.go",
|
||||
],
|
||||
}
|
89
cmd/release_config/release_config_lib/flag_artifact.go
Normal file
89
cmd/release_config/release_config_lib/flag_artifact.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"android/soong/cmd/release_config/release_config_proto"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type FlagArtifact struct {
|
||||
FlagDeclaration *release_config_proto.FlagDeclaration
|
||||
|
||||
// The index of the config directory where this flag was declared.
|
||||
// Flag values cannot be set in a location with a lower index.
|
||||
DeclarationIndex int
|
||||
|
||||
Traces []*release_config_proto.Tracepoint
|
||||
|
||||
// Assigned value
|
||||
Value *release_config_proto.Value
|
||||
}
|
||||
|
||||
// Key is flag name.
|
||||
type FlagArtifacts map[string]*FlagArtifact
|
||||
|
||||
func (src *FlagArtifact) Clone() *FlagArtifact {
|
||||
value := &release_config_proto.Value{}
|
||||
proto.Merge(value, src.Value)
|
||||
return &FlagArtifact{
|
||||
FlagDeclaration: src.FlagDeclaration,
|
||||
Traces: src.Traces,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
|
||||
if dst == nil {
|
||||
dst = make(FlagArtifacts)
|
||||
}
|
||||
for k, v := range src {
|
||||
dst[k] = v.Clone()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
|
||||
name := *flagValue.proto.Name
|
||||
fa.Traces = append(fa.Traces, &release_config_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
|
||||
if fa.Value.GetObsolete() {
|
||||
return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
|
||||
}
|
||||
switch val := flagValue.proto.Value.Val.(type) {
|
||||
case *release_config_proto.Value_StringValue:
|
||||
fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_StringValue{val.StringValue}}
|
||||
case *release_config_proto.Value_BoolValue:
|
||||
fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_BoolValue{val.BoolValue}}
|
||||
case *release_config_proto.Value_Obsolete:
|
||||
if !val.Obsolete {
|
||||
return fmt.Errorf("%s: Cannot set obsolete=false. Trace=%v", name, fa.Traces)
|
||||
}
|
||||
fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_Obsolete{true}}
|
||||
default:
|
||||
return fmt.Errorf("Invalid type for flag_value: %T. Trace=%v", val, fa.Traces)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fa *FlagArtifact) Marshal() (*release_config_proto.FlagArtifact, error) {
|
||||
return &release_config_proto.FlagArtifact{
|
||||
FlagDeclaration: fa.FlagDeclaration,
|
||||
Value: fa.Value,
|
||||
Traces: fa.Traces,
|
||||
}, nil
|
||||
}
|
27
cmd/release_config/release_config_lib/flag_declaration.go
Normal file
27
cmd/release_config/release_config_lib/flag_declaration.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"android/soong/cmd/release_config/release_config_proto"
|
||||
)
|
||||
|
||||
func FlagDeclarationFactory(protoPath string) (fd *release_config_proto.FlagDeclaration) {
|
||||
fd = &release_config_proto.FlagDeclaration{}
|
||||
if protoPath != "" {
|
||||
LoadTextproto(protoPath, fd)
|
||||
}
|
||||
return fd
|
||||
}
|
56
cmd/release_config/release_config_lib/flag_value.go
Normal file
56
cmd/release_config/release_config_lib/flag_value.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"android/soong/cmd/release_config/release_config_proto"
|
||||
)
|
||||
|
||||
type FlagValue struct {
|
||||
// The path providing this value.
|
||||
path string
|
||||
|
||||
// Protobuf
|
||||
proto release_config_proto.FlagValue
|
||||
}
|
||||
|
||||
func FlagValueFactory(protoPath string) (fv *FlagValue) {
|
||||
fv = &FlagValue{path: protoPath}
|
||||
if protoPath != "" {
|
||||
LoadTextproto(protoPath, &fv.proto)
|
||||
}
|
||||
return fv
|
||||
}
|
||||
|
||||
func MarshalValue(value *release_config_proto.Value) string {
|
||||
switch val := value.Val.(type) {
|
||||
case *release_config_proto.Value_UnspecifiedValue:
|
||||
// Value was never set.
|
||||
return ""
|
||||
case *release_config_proto.Value_StringValue:
|
||||
return val.StringValue
|
||||
case *release_config_proto.Value_BoolValue:
|
||||
if val.BoolValue {
|
||||
return "true"
|
||||
}
|
||||
// False ==> empty string
|
||||
return ""
|
||||
case *release_config_proto.Value_Obsolete:
|
||||
return " #OBSOLETE"
|
||||
default:
|
||||
// Flagged as error elsewhere, so return empty string here.
|
||||
return ""
|
||||
}
|
||||
}
|
67
cmd/release_config/release_config_lib/flag_value_test.go
Normal file
67
cmd/release_config/release_config_lib/flag_value_test.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
rc_proto "android/soong/cmd/release_config/release_config_proto"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type testCaseFlagValue struct {
|
||||
protoPath string
|
||||
name string
|
||||
data []byte
|
||||
expected rc_proto.FlagValue
|
||||
err error
|
||||
}
|
||||
|
||||
func (tc testCaseFlagValue) assertProtoEqual(t *testing.T, expected, actual proto.Message) {
|
||||
if !proto.Equal(expected, actual) {
|
||||
t.Errorf("Expected %q found %q", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagValue(t *testing.T) {
|
||||
testCases := []testCaseFlagValue{
|
||||
{
|
||||
name: "stringVal",
|
||||
protoPath: "build/release/flag_values/test/RELEASE_FOO.textproto",
|
||||
data: []byte(`name: "RELEASE_FOO" value {string_value: "BAR"}`),
|
||||
expected: rc_proto.FlagValue{
|
||||
Name: proto.String("RELEASE_FOO"),
|
||||
Value: &rc_proto.Value{Val: &rc_proto.Value_StringValue{"BAR"}},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
var err error
|
||||
tempdir := t.TempDir()
|
||||
path := filepath.Join(tempdir, tc.protoPath)
|
||||
if err = os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = os.WriteFile(path, tc.data, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := FlagValueFactory(path)
|
||||
tc.assertProtoEqual(t, &tc.expected, &actual.proto)
|
||||
}
|
||||
}
|
154
cmd/release_config/release_config_lib/release_config.go
Normal file
154
cmd/release_config/release_config_lib/release_config.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"android/soong/cmd/release_config/release_config_proto"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// One directory's contribution to the a release config.
|
||||
type ReleaseConfigContribution struct {
|
||||
// Paths to files providing this config.
|
||||
path string
|
||||
|
||||
// The index of the config directory where this release config
|
||||
// contribution was declared.
|
||||
// Flag values cannot be set in a location with a lower index.
|
||||
DeclarationIndex int
|
||||
|
||||
// Protobufs relevant to the config.
|
||||
proto release_config_proto.ReleaseConfig
|
||||
|
||||
FlagValues []*FlagValue
|
||||
}
|
||||
|
||||
// A generated release config.
|
||||
type ReleaseConfig struct {
|
||||
// the Name of the release config
|
||||
Name string
|
||||
|
||||
// The index of the config directory where this release config was
|
||||
// first declared.
|
||||
// Flag values cannot be set in a location with a lower index.
|
||||
DeclarationIndex int
|
||||
|
||||
// What contributes to this config.
|
||||
Contributions []*ReleaseConfigContribution
|
||||
|
||||
// Aliases for this release
|
||||
OtherNames []string
|
||||
|
||||
// The names of release configs that we inherit
|
||||
InheritNames []string
|
||||
|
||||
// Unmarshalled flag artifacts
|
||||
FlagArtifacts FlagArtifacts
|
||||
|
||||
// Generated release config
|
||||
ReleaseConfigArtifact *release_config_proto.ReleaseConfigArtifact
|
||||
|
||||
// We have begun compiling this release config.
|
||||
compileInProgress bool
|
||||
}
|
||||
|
||||
func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
|
||||
return &ReleaseConfig{Name: name, DeclarationIndex: index}
|
||||
}
|
||||
|
||||
func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
|
||||
if config.ReleaseConfigArtifact != nil {
|
||||
return nil
|
||||
}
|
||||
if config.compileInProgress {
|
||||
return fmt.Errorf("Loop detected for release config %s", config.Name)
|
||||
}
|
||||
config.compileInProgress = true
|
||||
|
||||
// Generate any configs we need to inherit. This will detect loops in
|
||||
// the config.
|
||||
contributionsToApply := []*ReleaseConfigContribution{}
|
||||
myInherits := []string{}
|
||||
myInheritsSet := make(map[string]bool)
|
||||
for _, inherit := range config.InheritNames {
|
||||
if _, ok := myInheritsSet[inherit]; ok {
|
||||
continue
|
||||
}
|
||||
myInherits = append(myInherits, inherit)
|
||||
myInheritsSet[inherit] = true
|
||||
iConfig, err := configs.GetReleaseConfig(inherit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iConfig.GenerateReleaseConfig(configs)
|
||||
contributionsToApply = append(contributionsToApply, iConfig.Contributions...)
|
||||
}
|
||||
contributionsToApply = append(contributionsToApply, config.Contributions...)
|
||||
|
||||
myAconfigValueSets := []string{}
|
||||
myFlags := configs.FlagArtifacts.Clone()
|
||||
myDirsMap := make(map[int]bool)
|
||||
for _, contrib := range contributionsToApply {
|
||||
myAconfigValueSets = append(myAconfigValueSets, contrib.proto.AconfigValueSets...)
|
||||
myDirsMap[contrib.DeclarationIndex] = true
|
||||
for _, value := range contrib.FlagValues {
|
||||
fa, ok := myFlags[*value.proto.Name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Setting value for undefined flag %s in %s\n", *value.proto.Name, value.path)
|
||||
}
|
||||
myDirsMap[fa.DeclarationIndex] = true
|
||||
if fa.DeclarationIndex > contrib.DeclarationIndex {
|
||||
// Setting location is to the left of declaration.
|
||||
return fmt.Errorf("Setting value for flag %s not allowed in %s\n", *value.proto.Name, value.path)
|
||||
}
|
||||
if err := fa.UpdateValue(*value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
directories := []string{}
|
||||
for idx, confDir := range configs.ConfigDirs {
|
||||
if _, ok := myDirsMap[idx]; ok {
|
||||
directories = append(directories, confDir)
|
||||
}
|
||||
}
|
||||
|
||||
config.FlagArtifacts = myFlags
|
||||
config.ReleaseConfigArtifact = &release_config_proto.ReleaseConfigArtifact{
|
||||
Name: proto.String(config.Name),
|
||||
OtherNames: config.OtherNames,
|
||||
FlagArtifacts: func() []*release_config_proto.FlagArtifact {
|
||||
ret := []*release_config_proto.FlagArtifact{}
|
||||
for _, flag := range myFlags {
|
||||
ret = append(ret, &release_config_proto.FlagArtifact{
|
||||
FlagDeclaration: flag.FlagDeclaration,
|
||||
Traces: flag.Traces,
|
||||
Value: flag.Value,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}(),
|
||||
AconfigValueSets: myAconfigValueSets,
|
||||
Inherits: myInherits,
|
||||
Directories: directories,
|
||||
}
|
||||
|
||||
config.compileInProgress = false
|
||||
return nil
|
||||
}
|
|
@ -12,12 +12,11 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
@ -31,105 +30,6 @@ import (
|
|||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var verboseFlag bool
|
||||
|
||||
type StringList []string
|
||||
|
||||
func (l *StringList) Set(v string) error {
|
||||
*l = append(*l, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *StringList) String() string {
|
||||
return fmt.Sprintf("%v", *l)
|
||||
}
|
||||
|
||||
var releaseConfigMapPaths StringList
|
||||
|
||||
func DumpProtos(outDir string, message proto.Message) error {
|
||||
basePath := filepath.Join(outDir, "all_release_configs")
|
||||
writer := func(suffix string, marshal func() ([]byte, error)) error {
|
||||
data, err := marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(fmt.Sprintf("%s.%s", basePath, suffix), data, 0644)
|
||||
}
|
||||
err := writer("textproto", func() ([]byte, error) { return prototext.MarshalOptions{Multiline: true}.Marshal(message) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writer("pb", func() ([]byte, error) { return proto.Marshal(message) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writer("json", func() ([]byte, error) { return json.MarshalIndent(message, "", " ") })
|
||||
}
|
||||
|
||||
func LoadTextproto(path string, message proto.Message) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret := prototext.Unmarshal(data, message)
|
||||
if verboseFlag {
|
||||
debug, _ := prototext.Marshal(message)
|
||||
fmt.Printf("%s: %s\n", path, debug)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func WalkTextprotoFiles(root string, subdir string, Func fs.WalkDirFunc) error {
|
||||
path := filepath.Join(root, subdir)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
// Missing subdirs are not an error.
|
||||
return nil
|
||||
}
|
||||
return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(d.Name(), ".textproto") && d.Type().IsRegular() {
|
||||
return Func(path, d, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type FlagValue struct {
|
||||
// The path providing this value.
|
||||
path string
|
||||
|
||||
// Protobuf
|
||||
proto release_config_proto.FlagValue
|
||||
}
|
||||
|
||||
func FlagValueFactory(protoPath string) (fv *FlagValue) {
|
||||
fv = &FlagValue{path: protoPath}
|
||||
if protoPath != "" {
|
||||
LoadTextproto(protoPath, &fv.proto)
|
||||
}
|
||||
return fv
|
||||
}
|
||||
|
||||
// One directory's contribution to the a release config.
|
||||
type ReleaseConfigContribution struct {
|
||||
// Paths to files providing this config.
|
||||
path string
|
||||
|
||||
// The index of the config directory where this release config
|
||||
// contribution was declared.
|
||||
// Flag values cannot be set in a location with a lower index.
|
||||
DeclarationIndex int
|
||||
|
||||
// Protobufs relevant to the config.
|
||||
proto release_config_proto.ReleaseConfig
|
||||
|
||||
FlagValues []*FlagValue
|
||||
}
|
||||
|
||||
// A single release_config_map.textproto and its associated data.
|
||||
// Used primarily for debugging.
|
||||
type ReleaseConfigMap struct {
|
||||
|
@ -143,51 +43,6 @@ type ReleaseConfigMap struct {
|
|||
FlagDeclarations []release_config_proto.FlagDeclaration
|
||||
}
|
||||
|
||||
// A generated release config.
|
||||
type ReleaseConfig struct {
|
||||
// the Name of the release config
|
||||
Name string
|
||||
|
||||
// The index of the config directory where this release config was
|
||||
// first declared.
|
||||
// Flag values cannot be set in a location with a lower index.
|
||||
DeclarationIndex int
|
||||
|
||||
// What contributes to this config.
|
||||
Contributions []*ReleaseConfigContribution
|
||||
|
||||
// Aliases for this release
|
||||
OtherNames []string
|
||||
|
||||
// The names of release configs that we inherit
|
||||
InheritNames []string
|
||||
|
||||
// Unmarshalled flag artifacts
|
||||
FlagArtifacts FlagArtifacts
|
||||
|
||||
// Generated release config
|
||||
ReleaseConfigArtifact *release_config_proto.ReleaseConfigArtifact
|
||||
|
||||
// We have begun compiling this release config.
|
||||
compileInProgress bool
|
||||
}
|
||||
|
||||
type FlagArtifact struct {
|
||||
FlagDeclaration *release_config_proto.FlagDeclaration
|
||||
|
||||
// The index of the config directory where this flag was declared.
|
||||
// Flag values cannot be set in a location with a lower index.
|
||||
DeclarationIndex int
|
||||
|
||||
Traces []*release_config_proto.Tracepoint
|
||||
|
||||
// Assigned value
|
||||
Value *release_config_proto.Value
|
||||
}
|
||||
|
||||
// Key is flag name.
|
||||
type FlagArtifacts map[string]*FlagArtifact
|
||||
|
||||
type ReleaseConfigDirMap map[string]int
|
||||
|
||||
// The generated release configs.
|
||||
|
@ -215,28 +70,27 @@ type ReleaseConfigs struct {
|
|||
ConfigDirIndexes ReleaseConfigDirMap
|
||||
}
|
||||
|
||||
func (src *FlagArtifact) Clone() *FlagArtifact {
|
||||
value := &release_config_proto.Value{}
|
||||
proto.Merge(value, src.Value)
|
||||
return &FlagArtifact{
|
||||
FlagDeclaration: src.FlagDeclaration,
|
||||
Traces: src.Traces,
|
||||
Value: value,
|
||||
func (configs *ReleaseConfigs) DumpArtifact(outDir string) error {
|
||||
message := &configs.Artifact
|
||||
basePath := filepath.Join(outDir, "all_release_configs")
|
||||
writer := func(suffix string, marshal func() ([]byte, error)) error {
|
||||
data, err := marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(fmt.Sprintf("%s.%s", basePath, suffix), data, 0644)
|
||||
}
|
||||
err := writer("textproto", func() ([]byte, error) { return prototext.MarshalOptions{Multiline: true}.Marshal(message) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
|
||||
if dst == nil {
|
||||
dst = make(FlagArtifacts)
|
||||
err = writer("pb", func() ([]byte, error) { return proto.Marshal(message) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range src {
|
||||
dst[k] = v.Clone()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
|
||||
return &ReleaseConfig{Name: name, DeclarationIndex: index}
|
||||
return writer("json", func() ([]byte, error) { return json.MarshalIndent(message, "", " ") })
|
||||
}
|
||||
|
||||
func ReleaseConfigsFactory() (c *ReleaseConfigs) {
|
||||
|
@ -260,14 +114,6 @@ func ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) {
|
|||
return m
|
||||
}
|
||||
|
||||
func FlagDeclarationFactory(protoPath string) (fd *release_config_proto.FlagDeclaration) {
|
||||
fd = &release_config_proto.FlagDeclaration{}
|
||||
if protoPath != "" {
|
||||
LoadTextproto(protoPath, fd)
|
||||
}
|
||||
return fd
|
||||
}
|
||||
|
||||
func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
|
||||
m := ReleaseConfigMapFactory(path)
|
||||
if m.proto.DefaultContainer == nil {
|
||||
|
@ -490,167 +336,17 @@ func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func MarshalValue(value *release_config_proto.Value) string {
|
||||
switch val := value.Val.(type) {
|
||||
case *release_config_proto.Value_UnspecifiedValue:
|
||||
// Value was never set.
|
||||
return ""
|
||||
case *release_config_proto.Value_StringValue:
|
||||
return val.StringValue
|
||||
case *release_config_proto.Value_BoolValue:
|
||||
if val.BoolValue {
|
||||
return "true"
|
||||
}
|
||||
// False ==> empty string
|
||||
return ""
|
||||
case *release_config_proto.Value_Obsolete:
|
||||
return " #OBSOLETE"
|
||||
default:
|
||||
// Flagged as error elsewhere, so return empty string here.
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
|
||||
name := *flagValue.proto.Name
|
||||
fa.Traces = append(fa.Traces, &release_config_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
|
||||
if fa.Value.GetObsolete() {
|
||||
return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
|
||||
}
|
||||
switch val := flagValue.proto.Value.Val.(type) {
|
||||
case *release_config_proto.Value_StringValue:
|
||||
fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_StringValue{val.StringValue}}
|
||||
case *release_config_proto.Value_BoolValue:
|
||||
fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_BoolValue{val.BoolValue}}
|
||||
case *release_config_proto.Value_Obsolete:
|
||||
if !val.Obsolete {
|
||||
return fmt.Errorf("%s: Cannot set obsolete=false. Trace=%v", name, fa.Traces)
|
||||
}
|
||||
fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_Obsolete{true}}
|
||||
default:
|
||||
return fmt.Errorf("Invalid type for flag_value: %T. Trace=%v", val, fa.Traces)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fa *FlagArtifact) Marshal() (*release_config_proto.FlagArtifact, error) {
|
||||
return &release_config_proto.FlagArtifact{
|
||||
FlagDeclaration: fa.FlagDeclaration,
|
||||
Value: fa.Value,
|
||||
Traces: fa.Traces,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
|
||||
if config.ReleaseConfigArtifact != nil {
|
||||
return nil
|
||||
}
|
||||
if config.compileInProgress {
|
||||
return fmt.Errorf("Loop detected for release config %s", config.Name)
|
||||
}
|
||||
config.compileInProgress = true
|
||||
|
||||
// Generate any configs we need to inherit. This will detect loops in
|
||||
// the config.
|
||||
contributionsToApply := []*ReleaseConfigContribution{}
|
||||
myInherits := []string{}
|
||||
myInheritsSet := make(map[string]bool)
|
||||
for _, inherit := range config.InheritNames {
|
||||
if _, ok := myInheritsSet[inherit]; ok {
|
||||
continue
|
||||
}
|
||||
myInherits = append(myInherits, inherit)
|
||||
myInheritsSet[inherit] = true
|
||||
iConfig, err := configs.GetReleaseConfig(inherit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iConfig.GenerateReleaseConfig(configs)
|
||||
contributionsToApply = append(contributionsToApply, iConfig.Contributions...)
|
||||
}
|
||||
contributionsToApply = append(contributionsToApply, config.Contributions...)
|
||||
|
||||
myAconfigValueSets := []string{}
|
||||
myFlags := configs.FlagArtifacts.Clone()
|
||||
myDirsMap := make(map[int]bool)
|
||||
for _, contrib := range contributionsToApply {
|
||||
myAconfigValueSets = append(myAconfigValueSets, contrib.proto.AconfigValueSets...)
|
||||
myDirsMap[contrib.DeclarationIndex] = true
|
||||
for _, value := range contrib.FlagValues {
|
||||
fa, ok := myFlags[*value.proto.Name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Setting value for undefined flag %s in %s\n", *value.proto.Name, value.path)
|
||||
}
|
||||
myDirsMap[fa.DeclarationIndex] = true
|
||||
if fa.DeclarationIndex > contrib.DeclarationIndex {
|
||||
// Setting location is to the left of declaration.
|
||||
return fmt.Errorf("Setting value for flag %s not allowed in %s\n", *value.proto.Name, value.path)
|
||||
}
|
||||
if err := fa.UpdateValue(*value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
directories := []string{}
|
||||
for idx, confDir := range configs.ConfigDirs {
|
||||
if _, ok := myDirsMap[idx]; ok {
|
||||
directories = append(directories, confDir)
|
||||
}
|
||||
}
|
||||
|
||||
config.FlagArtifacts = myFlags
|
||||
config.ReleaseConfigArtifact = &release_config_proto.ReleaseConfigArtifact{
|
||||
Name: proto.String(config.Name),
|
||||
OtherNames: config.OtherNames,
|
||||
FlagArtifacts: func() []*release_config_proto.FlagArtifact {
|
||||
ret := []*release_config_proto.FlagArtifact{}
|
||||
for _, flag := range myFlags {
|
||||
ret = append(ret, &release_config_proto.FlagArtifact{
|
||||
FlagDeclaration: flag.FlagDeclaration,
|
||||
Traces: flag.Traces,
|
||||
Value: flag.Value,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}(),
|
||||
AconfigValueSets: myAconfigValueSets,
|
||||
Inherits: myInherits,
|
||||
Directories: directories,
|
||||
}
|
||||
|
||||
config.compileInProgress = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDefaultOutDir() string {
|
||||
outEnv := os.Getenv("OUT_DIR")
|
||||
if outEnv == "" {
|
||||
outEnv = "out"
|
||||
}
|
||||
return filepath.Join(outEnv, "soong", "release-config")
|
||||
}
|
||||
func GetDefaultMapPaths() StringList {
|
||||
var defaultMapPaths StringList
|
||||
defaultLocations := StringList{
|
||||
"build/release/release_config_map.textproto",
|
||||
"vendor/google_shared/build/release/release_config_map.textproto",
|
||||
"vendor/google/release/release_config_map.textproto",
|
||||
}
|
||||
for _, path := range defaultLocations {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
defaultMapPaths = append(defaultMapPaths, path)
|
||||
}
|
||||
}
|
||||
prodMaps := os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
|
||||
if prodMaps != "" {
|
||||
defaultMapPaths = append(defaultMapPaths, strings.Split(prodMaps, " ")...)
|
||||
}
|
||||
return defaultMapPaths
|
||||
}
|
||||
|
||||
func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string) (*ReleaseConfigs, error) {
|
||||
var err error
|
||||
|
||||
if len(releaseConfigMapPaths) == 0 {
|
||||
releaseConfigMapPaths = GetDefaultMapPaths()
|
||||
if len(releaseConfigMapPaths) == 0 {
|
||||
return nil, fmt.Errorf("No maps found")
|
||||
}
|
||||
fmt.Printf("No --map argument provided. Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
|
||||
}
|
||||
|
||||
configs := ReleaseConfigsFactory()
|
||||
for idx, releaseConfigMapPath := range releaseConfigMapPaths {
|
||||
// Maintain an ordered list of release config directories.
|
||||
|
@ -667,36 +363,3 @@ func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease strin
|
|||
err = configs.GenerateReleaseConfigs(targetRelease)
|
||||
return configs, err
|
||||
}
|
||||
|
||||
func main() {
|
||||
var targetRelease string
|
||||
var outputDir string
|
||||
|
||||
flag.BoolVar(&verboseFlag, "debug", false, "print debugging information")
|
||||
flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
|
||||
flag.StringVar(&targetRelease, "release", "trunk_staging", "TARGET_RELEASE for this build")
|
||||
flag.StringVar(&outputDir, "out_dir", GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
|
||||
flag.Parse()
|
||||
|
||||
if len(releaseConfigMapPaths) == 0 {
|
||||
releaseConfigMapPaths = GetDefaultMapPaths()
|
||||
if len(releaseConfigMapPaths) == 0 {
|
||||
panic(fmt.Errorf("No maps found"))
|
||||
}
|
||||
fmt.Printf("No --map argument provided. Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
|
||||
}
|
||||
|
||||
configs, err := ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = os.MkdirAll(outputDir, 0775)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = configs.DumpMakefile(outputDir, targetRelease)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
DumpProtos(outputDir, &configs.Artifact)
|
||||
}
|
90
cmd/release_config/release_config_lib/util.go
Normal file
90
cmd/release_config/release_config_lib/util.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package release_config_lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type StringList []string
|
||||
|
||||
func (l *StringList) Set(v string) error {
|
||||
*l = append(*l, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *StringList) String() string {
|
||||
return fmt.Sprintf("%v", *l)
|
||||
}
|
||||
|
||||
func LoadTextproto(path string, message proto.Message) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret := prototext.Unmarshal(data, message)
|
||||
return ret
|
||||
}
|
||||
|
||||
func WalkTextprotoFiles(root string, subdir string, Func fs.WalkDirFunc) error {
|
||||
path := filepath.Join(root, subdir)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
// Missing subdirs are not an error.
|
||||
return nil
|
||||
}
|
||||
return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(d.Name(), ".textproto") && d.Type().IsRegular() {
|
||||
return Func(path, d, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetDefaultOutDir() string {
|
||||
outEnv := os.Getenv("OUT_DIR")
|
||||
if outEnv == "" {
|
||||
outEnv = "out"
|
||||
}
|
||||
return filepath.Join(outEnv, "soong", "release-config")
|
||||
}
|
||||
|
||||
func GetDefaultMapPaths() StringList {
|
||||
var defaultMapPaths StringList
|
||||
defaultLocations := StringList{
|
||||
"build/release/release_config_map.textproto",
|
||||
"vendor/google_shared/build/release/release_config_map.textproto",
|
||||
"vendor/google/release/release_config_map.textproto",
|
||||
}
|
||||
for _, path := range defaultLocations {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
defaultMapPaths = append(defaultMapPaths, path)
|
||||
}
|
||||
}
|
||||
prodMaps := os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
|
||||
if prodMaps != "" {
|
||||
defaultMapPaths = append(defaultMapPaths, strings.Split(prodMaps, " ")...)
|
||||
}
|
||||
return defaultMapPaths
|
||||
}
|
|
@ -17,7 +17,7 @@ package {
|
|||
}
|
||||
|
||||
bootstrap_go_package {
|
||||
name: "soong-release_config_proto",
|
||||
name: "soong-cmd-release_config-proto",
|
||||
pkgPath: "android/soong/release_config/release_config_proto",
|
||||
deps: [
|
||||
"golang-protobuf-reflect-protoreflect",
|
||||
|
|
Loading…
Reference in a new issue