Loading cmd/find_input_delta/find_input_delta/Android.bp 0 → 100644 +18 −0 Original line number Diff line number Diff line package { default_applicable_licenses: ["Android-Apache-2.0"], } bootstrap_go_package { name: "soong-cmd-find_input_delta-find_input_delta", pkgPath: "android/soong/cmd/find_input_delta/find_input_delta", deps: [ "golang-protobuf-encoding-prototext", "golang-protobuf-reflect-protoreflect", "golang-protobuf-runtime-protoimpl", "soong-cmd-find_input_delta-proto", "soong-cmd-find_input_delta-lib", ], srcs: [ "main.go", ], } cmd/find_input_delta/find_input_delta/main.go 0 → 100644 +88 −0 Original line number Diff line number Diff line // 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" "strings" fid_lib "android/soong/cmd/find_input_delta/find_input_delta_lib" ) func main() { var top string var prior_state_file string var new_state_file string var target string var inputs_file string var template string var inputs []string var inspect bool var err error flag.StringVar(&top, "top", ".", "path to top of workspace") flag.StringVar(&prior_state_file, "prior_state", "", "prior internal state file") flag.StringVar(&new_state_file, "new_state", "", "new internal state file") flag.StringVar(&target, "target", "", "name of ninja output file for build action") flag.StringVar(&inputs_file, "inputs_file", "", "file containing list of input files") flag.StringVar(&template, "template", fid_lib.DefaultTemplate, "output template for FileList") flag.BoolVar(&inspect, "inspect", false, "whether to inspect file contents") flag.Parse() if target == "" { panic("must specify --target") } if prior_state_file == "" { prior_state_file = target + ".pc_state" } if new_state_file == "" { new_state_file = prior_state_file + ".new" } if err = os.Chdir(top); err != nil { panic(err) } inputs = flag.Args() if inputs_file != "" { data, err := os.ReadFile(inputs_file) if err != nil { panic(err) } inputs = append(inputs, strings.Split(string(data), "\n")...) } // Read the prior state prior_state, err := fid_lib.LoadState(prior_state_file, fid_lib.OsFs) if err != nil { panic(err) } // Create the new state new_state, err := fid_lib.CreateState(inputs, inspect, fid_lib.OsFs) if err != nil { panic(err) } if err = fid_lib.WriteState(new_state, new_state_file); err != nil { panic(err) } file_list := *fid_lib.CompareInternalState(prior_state, new_state, target) if err = file_list.Format(os.Stdout, template); err != nil { panic(err) } } cmd/find_input_delta/find_input_delta_lib/Android.bp 0 → 100644 +34 −0 Original line number Diff line number Diff line // 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-find_input_delta-lib", pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_lib", deps: [ "golang-protobuf-encoding-prototext", "golang-protobuf-reflect-protoreflect", "golang-protobuf-runtime-protoimpl", "soong-cmd-find_input_delta-proto", "blueprint-pathtools", ], srcs: [ "fs.go", "file_list.go", "internal_state.go", ], } cmd/find_input_delta/find_input_delta_lib/file_list.go 0 → 100644 +78 −0 Original line number Diff line number Diff line // 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 find_input_delta_lib import ( "io" "text/template" fid_exp "android/soong/cmd/find_input_delta/find_input_delta_proto" "google.golang.org/protobuf/proto" ) var DefaultTemplate = ` {{- define "contents"}} {{- range .Deletions}}-{{.}} {{end}} {{- range .Additions}}+{{.}} {{end}} {{- range .Changes}}+{{- .Name}} {{end}} {{- range .Changes}} {{- if or .Additions .Deletions .Changes}}--file {{.Name}} {{template "contents" .}}--endfile {{end}} {{- end}} {{- end}} {{- template "contents" .}}` type FileList struct { // The name of the parent for the list of file differences. // For the outermost FileList, this is the name of the ninja target. // Under `Changes`, it is the name of the changed file. Name string // The added files Additions []string // The deleted files Deletions []string // The modified files Changes []FileList } func (fl FileList) Marshal() (*fid_exp.FileList, error) { ret := &fid_exp.FileList{ Name: proto.String(fl.Name), } if len(fl.Additions) > 0 { ret.Additions = fl.Additions } for _, ch := range fl.Changes { change, err := ch.Marshal() if err != nil { return nil, err } ret.Changes = append(ret.Changes, change) } if len(fl.Deletions) > 0 { ret.Deletions = fl.Deletions } return ret, nil } func (fl FileList) Format(wr io.Writer, format string) error { tmpl, err := template.New("filelist").Parse(format) if err != nil { return err } return tmpl.Execute(wr, fl) } cmd/find_input_delta/find_input_delta_lib/file_list_test.go 0 → 100644 +131 −0 Original line number Diff line number Diff line // 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 find_input_delta_lib import ( "bytes" "slices" "testing" // For Assert*. "android/soong/android" ) func (fl *FileList) Equal(other *FileList) bool { if fl.Name != other.Name { return false } if !slices.Equal(fl.Additions, other.Additions) { return false } if !slices.Equal(fl.Deletions, other.Deletions) { return false } if len(fl.Changes) != len(other.Changes) { return false } for idx, ch := range fl.Changes { if !ch.Equal(&other.Changes[idx]) { return false } } return true } func TestFormat(t *testing.T) { testCases := []struct { Name string Template string Input FileList Expected string Err error }{ { Name: "no contents", Template: DefaultTemplate, Input: FileList{ Name: "target", Additions: []string{"add1", "add2"}, Deletions: []string{"del1", "del2"}, Changes: []FileList{ FileList{Name: "mod1"}, FileList{Name: "mod2"}, }, }, Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 ", Err: nil, }, { Name: "adds", Template: DefaultTemplate, Input: FileList{ Name: "target", Additions: []string{"add1", "add2"}, }, Expected: "+add1 +add2 ", Err: nil, }, { Name: "deletes", Template: DefaultTemplate, Input: FileList{ Name: "target", Deletions: []string{"del1", "del2"}, }, Expected: "-del1 -del2 ", Err: nil, }, { Name: "changes", Template: DefaultTemplate, Input: FileList{ Name: "target", Changes: []FileList{ FileList{Name: "mod1"}, FileList{Name: "mod2"}, }, }, Expected: "+mod1 +mod2 ", Err: nil, }, { Name: "with contents", Template: DefaultTemplate, Input: FileList{ Name: "target", Additions: []string{"add1", "add2"}, Deletions: []string{"del1", "del2"}, Changes: []FileList{ FileList{ Name: "mod1", }, FileList{ Name: "mod2", Additions: []string{"a1"}, Deletions: []string{"d1"}, }, }, }, Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 --file mod2 -d1 +a1 --endfile ", Err: nil, }, } for _, tc := range testCases { buf := bytes.NewBuffer([]byte{}) err := tc.Input.Format(buf, tc.Template) android.AssertSame(t, tc.Name, tc.Err, err) android.AssertSame(t, tc.Name, tc.Expected, buf.String()) } } Loading
cmd/find_input_delta/find_input_delta/Android.bp 0 → 100644 +18 −0 Original line number Diff line number Diff line package { default_applicable_licenses: ["Android-Apache-2.0"], } bootstrap_go_package { name: "soong-cmd-find_input_delta-find_input_delta", pkgPath: "android/soong/cmd/find_input_delta/find_input_delta", deps: [ "golang-protobuf-encoding-prototext", "golang-protobuf-reflect-protoreflect", "golang-protobuf-runtime-protoimpl", "soong-cmd-find_input_delta-proto", "soong-cmd-find_input_delta-lib", ], srcs: [ "main.go", ], }
cmd/find_input_delta/find_input_delta/main.go 0 → 100644 +88 −0 Original line number Diff line number Diff line // 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" "strings" fid_lib "android/soong/cmd/find_input_delta/find_input_delta_lib" ) func main() { var top string var prior_state_file string var new_state_file string var target string var inputs_file string var template string var inputs []string var inspect bool var err error flag.StringVar(&top, "top", ".", "path to top of workspace") flag.StringVar(&prior_state_file, "prior_state", "", "prior internal state file") flag.StringVar(&new_state_file, "new_state", "", "new internal state file") flag.StringVar(&target, "target", "", "name of ninja output file for build action") flag.StringVar(&inputs_file, "inputs_file", "", "file containing list of input files") flag.StringVar(&template, "template", fid_lib.DefaultTemplate, "output template for FileList") flag.BoolVar(&inspect, "inspect", false, "whether to inspect file contents") flag.Parse() if target == "" { panic("must specify --target") } if prior_state_file == "" { prior_state_file = target + ".pc_state" } if new_state_file == "" { new_state_file = prior_state_file + ".new" } if err = os.Chdir(top); err != nil { panic(err) } inputs = flag.Args() if inputs_file != "" { data, err := os.ReadFile(inputs_file) if err != nil { panic(err) } inputs = append(inputs, strings.Split(string(data), "\n")...) } // Read the prior state prior_state, err := fid_lib.LoadState(prior_state_file, fid_lib.OsFs) if err != nil { panic(err) } // Create the new state new_state, err := fid_lib.CreateState(inputs, inspect, fid_lib.OsFs) if err != nil { panic(err) } if err = fid_lib.WriteState(new_state, new_state_file); err != nil { panic(err) } file_list := *fid_lib.CompareInternalState(prior_state, new_state, target) if err = file_list.Format(os.Stdout, template); err != nil { panic(err) } }
cmd/find_input_delta/find_input_delta_lib/Android.bp 0 → 100644 +34 −0 Original line number Diff line number Diff line // 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-find_input_delta-lib", pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_lib", deps: [ "golang-protobuf-encoding-prototext", "golang-protobuf-reflect-protoreflect", "golang-protobuf-runtime-protoimpl", "soong-cmd-find_input_delta-proto", "blueprint-pathtools", ], srcs: [ "fs.go", "file_list.go", "internal_state.go", ], }
cmd/find_input_delta/find_input_delta_lib/file_list.go 0 → 100644 +78 −0 Original line number Diff line number Diff line // 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 find_input_delta_lib import ( "io" "text/template" fid_exp "android/soong/cmd/find_input_delta/find_input_delta_proto" "google.golang.org/protobuf/proto" ) var DefaultTemplate = ` {{- define "contents"}} {{- range .Deletions}}-{{.}} {{end}} {{- range .Additions}}+{{.}} {{end}} {{- range .Changes}}+{{- .Name}} {{end}} {{- range .Changes}} {{- if or .Additions .Deletions .Changes}}--file {{.Name}} {{template "contents" .}}--endfile {{end}} {{- end}} {{- end}} {{- template "contents" .}}` type FileList struct { // The name of the parent for the list of file differences. // For the outermost FileList, this is the name of the ninja target. // Under `Changes`, it is the name of the changed file. Name string // The added files Additions []string // The deleted files Deletions []string // The modified files Changes []FileList } func (fl FileList) Marshal() (*fid_exp.FileList, error) { ret := &fid_exp.FileList{ Name: proto.String(fl.Name), } if len(fl.Additions) > 0 { ret.Additions = fl.Additions } for _, ch := range fl.Changes { change, err := ch.Marshal() if err != nil { return nil, err } ret.Changes = append(ret.Changes, change) } if len(fl.Deletions) > 0 { ret.Deletions = fl.Deletions } return ret, nil } func (fl FileList) Format(wr io.Writer, format string) error { tmpl, err := template.New("filelist").Parse(format) if err != nil { return err } return tmpl.Execute(wr, fl) }
cmd/find_input_delta/find_input_delta_lib/file_list_test.go 0 → 100644 +131 −0 Original line number Diff line number Diff line // 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 find_input_delta_lib import ( "bytes" "slices" "testing" // For Assert*. "android/soong/android" ) func (fl *FileList) Equal(other *FileList) bool { if fl.Name != other.Name { return false } if !slices.Equal(fl.Additions, other.Additions) { return false } if !slices.Equal(fl.Deletions, other.Deletions) { return false } if len(fl.Changes) != len(other.Changes) { return false } for idx, ch := range fl.Changes { if !ch.Equal(&other.Changes[idx]) { return false } } return true } func TestFormat(t *testing.T) { testCases := []struct { Name string Template string Input FileList Expected string Err error }{ { Name: "no contents", Template: DefaultTemplate, Input: FileList{ Name: "target", Additions: []string{"add1", "add2"}, Deletions: []string{"del1", "del2"}, Changes: []FileList{ FileList{Name: "mod1"}, FileList{Name: "mod2"}, }, }, Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 ", Err: nil, }, { Name: "adds", Template: DefaultTemplate, Input: FileList{ Name: "target", Additions: []string{"add1", "add2"}, }, Expected: "+add1 +add2 ", Err: nil, }, { Name: "deletes", Template: DefaultTemplate, Input: FileList{ Name: "target", Deletions: []string{"del1", "del2"}, }, Expected: "-del1 -del2 ", Err: nil, }, { Name: "changes", Template: DefaultTemplate, Input: FileList{ Name: "target", Changes: []FileList{ FileList{Name: "mod1"}, FileList{Name: "mod2"}, }, }, Expected: "+mod1 +mod2 ", Err: nil, }, { Name: "with contents", Template: DefaultTemplate, Input: FileList{ Name: "target", Additions: []string{"add1", "add2"}, Deletions: []string{"del1", "del2"}, Changes: []FileList{ FileList{ Name: "mod1", }, FileList{ Name: "mod2", Additions: []string{"a1"}, Deletions: []string{"d1"}, }, }, }, Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 --file mod2 -d1 +a1 --endfile ", Err: nil, }, } for _, tc := range testCases { buf := bytes.NewBuffer([]byte{}) err := tc.Input.Format(buf, tc.Template) android.AssertSame(t, tc.Name, tc.Err, err) android.AssertSame(t, tc.Name, tc.Expected, buf.String()) } }