Merge "Add find_input_delta" into main
diff --git a/cmd/find_input_delta/find_input_delta/Android.bp b/cmd/find_input_delta/find_input_delta/Android.bp
new file mode 100644
index 0000000..6b2dbc7
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta/Android.bp
@@ -0,0 +1,18 @@
+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",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta/main.go b/cmd/find_input_delta/find_input_delta/main.go
new file mode 100644
index 0000000..6b657ea
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta/main.go
@@ -0,0 +1,88 @@
+// 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)
+ }
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/Android.bp b/cmd/find_input_delta/find_input_delta_lib/Android.bp
new file mode 100644
index 0000000..795b140
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/Android.bp
@@ -0,0 +1,34 @@
+// 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",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list.go b/cmd/find_input_delta/find_input_delta_lib/file_list.go
new file mode 100644
index 0000000..23337ad
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list.go
@@ -0,0 +1,78 @@
+// 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)
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list_test.go b/cmd/find_input_delta/find_input_delta_lib/file_list_test.go
new file mode 100644
index 0000000..2459f1e
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list_test.go
@@ -0,0 +1,131 @@
+// 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())
+ }
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/fs.go b/cmd/find_input_delta/find_input_delta_lib/fs.go
new file mode 100644
index 0000000..4a83ed7
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/fs.go
@@ -0,0 +1,46 @@
+// 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"
+ "io/fs"
+ "os"
+)
+
+// OsFs provides a minimal implementation so that we can use testing/fstest for
+// unit tests.
+var OsFs fileSystem = osFS{}
+
+type fileSystem interface {
+ Open(path string) (fs.File, error)
+ Stat(path string) (os.FileInfo, error)
+ ReadFile(path string) ([]byte, error)
+}
+
+type file interface {
+ io.Closer
+ io.Reader
+ io.ReaderAt
+ io.Seeker
+ Stat() (os.FileInfo, error)
+}
+
+// osFS implements fileSystem using the local disk.
+type osFS struct{}
+
+func (osFS) Open(path string) (fs.File, error) { return os.Open(path) }
+func (osFS) Stat(path string) (os.FileInfo, error) { return os.Stat(path) }
+func (osFS) ReadFile(path string) ([]byte, error) { return os.ReadFile(path) }
diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state.go b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
new file mode 100644
index 0000000..b2ff8c7
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
@@ -0,0 +1,122 @@
+// 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 (
+ "errors"
+ "fmt"
+ "io/fs"
+ "slices"
+
+ fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
+ "github.com/google/blueprint/pathtools"
+ "google.golang.org/protobuf/proto"
+)
+
+// Load the internal state from a file.
+// If the file does not exist, an empty state is returned.
+func LoadState(filename string, fsys fs.ReadFileFS) (*fid_proto.PartialCompileInputs, error) {
+ var message = &fid_proto.PartialCompileInputs{}
+ data, err := fsys.ReadFile(filename)
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ return message, err
+ }
+ proto.Unmarshal(data, message)
+ return message, nil
+}
+
+type StatReadFileFS interface {
+ fs.StatFS
+ fs.ReadFileFS
+}
+
+// Create the internal state by examining the inputs.
+func CreateState(inputs []string, inspect_contents bool, fsys StatReadFileFS) (*fid_proto.PartialCompileInputs, error) {
+ ret := &fid_proto.PartialCompileInputs{}
+ slices.Sort(inputs)
+ for _, input := range inputs {
+ stat, err := fs.Stat(fsys, input)
+ if err != nil {
+ return ret, err
+ }
+ pci := &fid_proto.PartialCompileInput{
+ Name: proto.String(input),
+ MtimeNsec: proto.Int64(stat.ModTime().UnixNano()),
+ // If we ever have an easy hash, assign it here.
+ }
+ if inspect_contents {
+ contents, err := InspectFileContents(input)
+ if err != nil {
+ return ret, err
+ }
+ if contents != nil {
+ pci.Contents = contents
+ }
+ }
+ ret.InputFiles = append(ret.InputFiles, pci)
+ }
+ return ret, nil
+}
+
+// Inspect the file and extract the state of the elements in the archive.
+// If this is not an archive of some sort, nil is returned.
+func InspectFileContents(name string) ([]*fid_proto.PartialCompileInput, error) {
+ // TODO: Actually inspect the contents.
+ fmt.Printf("inspecting contents for %s\n", name)
+ return nil, nil
+}
+
+func WriteState(s *fid_proto.PartialCompileInputs, path string) error {
+ data, err := proto.Marshal(s)
+ if err != nil {
+ return err
+ }
+ return pathtools.WriteFileIfChanged(path, data, 0644)
+}
+
+func CompareInternalState(prior, other *fid_proto.PartialCompileInputs, target string) *FileList {
+ return CompareInputFiles(prior.GetInputFiles(), other.GetInputFiles(), target)
+}
+
+func CompareInputFiles(prior, other []*fid_proto.PartialCompileInput, name string) *FileList {
+ fl := &FileList{
+ Name: name,
+ }
+ PriorMap := make(map[string]*fid_proto.PartialCompileInput, len(prior))
+ // We know that the lists are properly sorted, so we can simply compare them.
+ for _, v := range prior {
+ PriorMap[v.GetName()] = v
+ }
+ otherMap := make(map[string]*fid_proto.PartialCompileInput, len(other))
+ for _, v := range other {
+ name = v.GetName()
+ otherMap[name] = v
+ if _, ok := PriorMap[name]; !ok {
+ // Added file
+ fl.Additions = append(fl.Additions, name)
+ } else if !proto.Equal(PriorMap[name], v) {
+ // Changed file
+ fl.Changes = append(fl.Changes, *CompareInputFiles(PriorMap[name].GetContents(), v.GetContents(), name))
+ }
+ }
+ for _, v := range prior {
+ name := v.GetName()
+ if _, ok := otherMap[name]; !ok {
+ // Deleted file
+ fl.Deletions = append(fl.Deletions, name)
+ }
+ }
+ return fl
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go b/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go
new file mode 100644
index 0000000..20b8efa
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go
@@ -0,0 +1,232 @@
+// 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 (
+ "errors"
+ "io/fs"
+ "testing"
+ "testing/fstest"
+ "time"
+
+ // For Assert*.
+ "android/soong/android"
+
+ fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
+ "google.golang.org/protobuf/proto"
+)
+
+// Various state files
+
+func marshalProto(t *testing.T, message proto.Message) []byte {
+ data, err := proto.Marshal(message)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ return data
+}
+
+func protoFile(name string, mtime_nsec int64, hash string, contents []*fid_proto.PartialCompileInput) (pci *fid_proto.PartialCompileInput) {
+ pci = &fid_proto.PartialCompileInput{
+ Name: proto.String(name),
+ }
+ if mtime_nsec != 0 {
+ pci.MtimeNsec = proto.Int64(mtime_nsec)
+ }
+ if len(hash) > 0 {
+ pci.Hash = proto.String(hash)
+ }
+ if contents != nil {
+ pci.Contents = contents
+ }
+ return
+}
+
+func TestLoadState(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Filename string
+ Mapfs fs.ReadFileFS
+ Expected *fid_proto.PartialCompileInputs
+ Err error
+ }{
+ {
+ Name: "missing file",
+ Filename: "missing",
+ Mapfs: fstest.MapFS{},
+ Expected: &fid_proto.PartialCompileInputs{},
+ Err: nil,
+ },
+ {
+ Name: "bad file",
+ Filename: ".",
+ Mapfs: OsFs,
+ Expected: &fid_proto.PartialCompileInputs{},
+ Err: errors.New("read failed"),
+ },
+ {
+ Name: "file with mtime",
+ Filename: "state.old",
+ Mapfs: fstest.MapFS{
+ "state.old": &fstest.MapFile{
+ Data: marshalProto(t, &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "", nil),
+ },
+ }),
+ },
+ },
+ Expected: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "", nil),
+ },
+ },
+ Err: nil,
+ },
+ {
+ Name: "file with mtime and hash",
+ Filename: "state.old",
+ Mapfs: fstest.MapFS{
+ "state.old": &fstest.MapFile{
+ Data: marshalProto(t, &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "crc:crc_value", nil),
+ },
+ }),
+ },
+ },
+ Expected: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "crc:crc_value", nil),
+ },
+ },
+ Err: nil,
+ },
+ }
+ for _, tc := range testCases {
+ actual, err := LoadState(tc.Filename, tc.Mapfs)
+ if tc.Err == nil {
+ android.AssertSame(t, tc.Name, tc.Err, err)
+ } else if err == nil {
+ t.Errorf("%s: expected error, did not get one", tc.Name)
+ }
+ if !proto.Equal(tc.Expected, actual) {
+ t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+ }
+ }
+}
+
+func TestCreateState(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Inputs []string
+ Inspect bool
+ Mapfs StatReadFileFS
+ Expected *fid_proto.PartialCompileInputs
+ Err error
+ }{
+ {
+ Name: "no inputs",
+ Inputs: []string{},
+ Mapfs: fstest.MapFS{},
+ Expected: &fid_proto.PartialCompileInputs{},
+ Err: nil,
+ },
+ {
+ Name: "files found",
+ Inputs: []string{"baz", "foo", "bar"},
+ Mapfs: fstest.MapFS{
+ "foo": &fstest.MapFile{ModTime: time.Unix(0, 100).UTC()},
+ "baz": &fstest.MapFile{ModTime: time.Unix(0, 300).UTC()},
+ "bar": &fstest.MapFile{ModTime: time.Unix(0, 200).UTC()},
+ },
+ Expected: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ // Files are always sorted.
+ protoFile("bar", 200, "", nil),
+ protoFile("baz", 300, "", nil),
+ protoFile("foo", 100, "", nil),
+ },
+ },
+ Err: nil,
+ },
+ }
+ for _, tc := range testCases {
+ actual, err := CreateState(tc.Inputs, tc.Inspect, tc.Mapfs)
+ if tc.Err == nil {
+ android.AssertSame(t, tc.Name, tc.Err, err)
+ } else if err == nil {
+ t.Errorf("%s: expected error, did not get one", tc.Name)
+ }
+ if !proto.Equal(tc.Expected, actual) {
+ t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+ }
+ }
+}
+
+func TestCompareInternalState(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Target string
+ Prior *fid_proto.PartialCompileInputs
+ New *fid_proto.PartialCompileInputs
+ Expected *FileList
+ }{
+ {
+ Name: "prior is empty",
+ Target: "foo",
+ Prior: &fid_proto.PartialCompileInputs{},
+ New: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("file1", 100, "", nil),
+ },
+ },
+ Expected: &FileList{
+ Name: "foo",
+ Additions: []string{"file1"},
+ },
+ },
+ {
+ Name: "one of each",
+ Target: "foo",
+ Prior: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("file0", 100, "", nil),
+ protoFile("file1", 100, "", nil),
+ protoFile("file2", 200, "", nil),
+ },
+ },
+ New: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("file0", 100, "", nil),
+ protoFile("file1", 200, "", nil),
+ protoFile("file3", 300, "", nil),
+ },
+ },
+ Expected: &FileList{
+ Name: "foo",
+ Additions: []string{"file3"},
+ Changes: []FileList{FileList{Name: "file1"}},
+ Deletions: []string{"file2"},
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actual := CompareInternalState(tc.Prior, tc.New, tc.Target)
+ if !tc.Expected.Equal(actual) {
+ t.Errorf("%s: expected %q, actual %q", tc.Name, tc.Expected, actual)
+ }
+ }
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/Android.bp b/cmd/find_input_delta/find_input_delta_proto/Android.bp
new file mode 100644
index 0000000..18eba6b
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/Android.bp
@@ -0,0 +1,29 @@
+// 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-proto",
+ pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "target_delta_files.pb.go",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
new file mode 100644
index 0000000..648ef22
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
@@ -0,0 +1,198 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.33.0
+// protoc v3.21.12
+// source: file_list.proto
+
+package proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type FileList struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The name of the file.
+ // In the outermost message, this is the name of the Ninja target.
+ // When used in `changes`, this is the name of the changed file.
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // The added files.
+ Additions []string `protobuf:"bytes,2,rep,name=additions" json:"additions,omitempty"`
+ // The deleted files.
+ Deletions []string `protobuf:"bytes,3,rep,name=deletions" json:"deletions,omitempty"`
+ // The changed files.
+ Changes []*FileList `protobuf:"bytes,4,rep,name=changes" json:"changes,omitempty"`
+}
+
+func (x *FileList) Reset() {
+ *x = FileList{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_file_list_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *FileList) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FileList) ProtoMessage() {}
+
+func (x *FileList) ProtoReflect() protoreflect.Message {
+ mi := &file_file_list_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FileList.ProtoReflect.Descriptor instead.
+func (*FileList) Descriptor() ([]byte, []int) {
+ return file_file_list_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *FileList) GetName() string {
+ if x != nil && x.Name != nil {
+ return *x.Name
+ }
+ return ""
+}
+
+func (x *FileList) GetAdditions() []string {
+ if x != nil {
+ return x.Additions
+ }
+ return nil
+}
+
+func (x *FileList) GetDeletions() []string {
+ if x != nil {
+ return x.Deletions
+ }
+ return nil
+}
+
+func (x *FileList) GetChanges() []*FileList {
+ if x != nil {
+ return x.Changes
+ }
+ return nil
+}
+
+var File_file_list_proto protoreflect.FileDescriptor
+
+var file_file_list_proto_rawDesc = []byte{
+ 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f,
+ 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x22, 0x9e, 0x01, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+ 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42,
+ 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69,
+ 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x42, 0x26, 0x5a, 0x24, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f,
+ 0x6f, 0x6e, 0x67, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64,
+ 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+ file_file_list_proto_rawDescOnce sync.Once
+ file_file_list_proto_rawDescData = file_file_list_proto_rawDesc
+)
+
+func file_file_list_proto_rawDescGZIP() []byte {
+ file_file_list_proto_rawDescOnce.Do(func() {
+ file_file_list_proto_rawDescData = protoimpl.X.CompressGZIP(file_file_list_proto_rawDescData)
+ })
+ return file_file_list_proto_rawDescData
+}
+
+var file_file_list_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_file_list_proto_goTypes = []interface{}{
+ (*FileList)(nil), // 0: android.find_input_delta_proto.FileList
+}
+var file_file_list_proto_depIdxs = []int32{
+ 0, // 0: android.find_input_delta_proto.FileList.changes:type_name -> android.find_input_delta_proto.FileList
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_file_list_proto_init() }
+func file_file_list_proto_init() {
+ if File_file_list_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_file_list_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*FileList); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_file_list_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_file_list_proto_goTypes,
+ DependencyIndexes: file_file_list_proto_depIdxs,
+ MessageInfos: file_file_list_proto_msgTypes,
+ }.Build()
+ File_file_list_proto = out.File
+ file_file_list_proto_rawDesc = nil
+ file_file_list_proto_goTypes = nil
+ file_file_list_proto_depIdxs = nil
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.proto b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
new file mode 100644
index 0000000..d7faca9
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// 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.
+
+syntax = "proto2";
+package android.find_input_delta_proto;
+option go_package = "android/soong/find_input_delta/proto";
+
+message FileList {
+ // The name of the file.
+ // In the outermost message, this is the name of the Ninja target.
+ // When used in `changes`, this is the name of the changed file.
+ optional string name = 1;
+
+ // The added files.
+ repeated string additions = 2;
+
+ // The deleted files.
+ repeated string deletions = 3;
+
+ // The changed files.
+ repeated FileList changes = 4;
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/regen.sh b/cmd/find_input_delta/find_input_delta_proto/regen.sh
new file mode 100644
index 0000000..d773659
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. file_list.proto
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp b/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
new file mode 100644
index 0000000..00ba9ff
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
@@ -0,0 +1,29 @@
+// 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-proto_internal",
+ pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_proto_internal",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "internal_state.pb.go",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go
new file mode 100644
index 0000000..2229a32
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go
@@ -0,0 +1,268 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.33.0
+// protoc v3.21.12
+// source: internal_state.proto
+
+package proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// The state of all inputs.
+type PartialCompileInputs struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The status of each file.
+ InputFiles []*PartialCompileInput `protobuf:"bytes,1,rep,name=input_files,json=inputFiles" json:"input_files,omitempty"`
+}
+
+func (x *PartialCompileInputs) Reset() {
+ *x = PartialCompileInputs{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_internal_state_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PartialCompileInputs) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PartialCompileInputs) ProtoMessage() {}
+
+func (x *PartialCompileInputs) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_state_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PartialCompileInputs.ProtoReflect.Descriptor instead.
+func (*PartialCompileInputs) Descriptor() ([]byte, []int) {
+ return file_internal_state_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *PartialCompileInputs) GetInputFiles() []*PartialCompileInput {
+ if x != nil {
+ return x.InputFiles
+ }
+ return nil
+}
+
+// The state of one input.
+type PartialCompileInput struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The name of the file.
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // The timestamp of the file in (Unix) nanoseconds.
+ MtimeNsec *int64 `protobuf:"varint,2,opt,name=mtime_nsec,json=mtimeNsec" json:"mtime_nsec,omitempty"`
+ // The hash of the file, in the form ‘{HASHNAME}:{VALUE}’
+ Hash *string `protobuf:"bytes,3,opt,name=hash" json:"hash,omitempty"`
+ // Contents of the file, if the file was inspected (such as jar files, etc).
+ Contents []*PartialCompileInput `protobuf:"bytes,4,rep,name=contents" json:"contents,omitempty"`
+}
+
+func (x *PartialCompileInput) Reset() {
+ *x = PartialCompileInput{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_internal_state_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PartialCompileInput) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PartialCompileInput) ProtoMessage() {}
+
+func (x *PartialCompileInput) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_state_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PartialCompileInput.ProtoReflect.Descriptor instead.
+func (*PartialCompileInput) Descriptor() ([]byte, []int) {
+ return file_internal_state_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PartialCompileInput) GetName() string {
+ if x != nil && x.Name != nil {
+ return *x.Name
+ }
+ return ""
+}
+
+func (x *PartialCompileInput) GetMtimeNsec() int64 {
+ if x != nil && x.MtimeNsec != nil {
+ return *x.MtimeNsec
+ }
+ return 0
+}
+
+func (x *PartialCompileInput) GetHash() string {
+ if x != nil && x.Hash != nil {
+ return *x.Hash
+ }
+ return ""
+}
+
+func (x *PartialCompileInput) GetContents() []*PartialCompileInput {
+ if x != nil {
+ return x.Contents
+ }
+ return nil
+}
+
+var File_internal_state_proto protoreflect.FileDescriptor
+
+var file_internal_state_proto_rawDesc = []byte{
+ 0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+ 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6c, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61,
+ 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x54,
+ 0x0a, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69,
+ 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70,
+ 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46,
+ 0x69, 0x6c, 0x65, 0x73, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c,
+ 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x65, 0x63, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x65, 0x63, 0x12,
+ 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68,
+ 0x61, 0x73, 0x68, 0x12, 0x4f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18,
+ 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+ 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74,
+ 0x65, 0x6e, 0x74, 0x73, 0x42, 0x26, 0x5a, 0x24, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+ 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
+ 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+ file_internal_state_proto_rawDescOnce sync.Once
+ file_internal_state_proto_rawDescData = file_internal_state_proto_rawDesc
+)
+
+func file_internal_state_proto_rawDescGZIP() []byte {
+ file_internal_state_proto_rawDescOnce.Do(func() {
+ file_internal_state_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_state_proto_rawDescData)
+ })
+ return file_internal_state_proto_rawDescData
+}
+
+var file_internal_state_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_internal_state_proto_goTypes = []interface{}{
+ (*PartialCompileInputs)(nil), // 0: android.find_input_delta_proto.PartialCompileInputs
+ (*PartialCompileInput)(nil), // 1: android.find_input_delta_proto.PartialCompileInput
+}
+var file_internal_state_proto_depIdxs = []int32{
+ 1, // 0: android.find_input_delta_proto.PartialCompileInputs.input_files:type_name -> android.find_input_delta_proto.PartialCompileInput
+ 1, // 1: android.find_input_delta_proto.PartialCompileInput.contents:type_name -> android.find_input_delta_proto.PartialCompileInput
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_internal_state_proto_init() }
+func file_internal_state_proto_init() {
+ if File_internal_state_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_internal_state_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PartialCompileInputs); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_internal_state_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PartialCompileInput); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_internal_state_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_internal_state_proto_goTypes,
+ DependencyIndexes: file_internal_state_proto_depIdxs,
+ MessageInfos: file_internal_state_proto_msgTypes,
+ }.Build()
+ File_internal_state_proto = out.File
+ file_internal_state_proto_rawDesc = nil
+ file_internal_state_proto_goTypes = nil
+ file_internal_state_proto_depIdxs = nil
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto
new file mode 100644
index 0000000..113fc64
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// 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.
+
+syntax = "proto2";
+package android.find_input_delta_proto;
+option go_package = "android/soong/find_input_delta/proto";
+
+// The state of all inputs.
+message PartialCompileInputs {
+ // The status of each file.
+ repeated PartialCompileInput input_files = 1;
+}
+
+// The state of one input.
+message PartialCompileInput {
+ // The name of the file.
+ optional string name = 1;
+
+ // The timestamp of the file in (Unix) nanoseconds.
+ optional int64 mtime_nsec = 2;
+
+ // The hash of the file, in the form ‘{HASHNAME}:{VALUE}’
+ optional string hash = 3;
+
+ // Contents of the file, if the file was inspected (such as jar files, etc).
+ repeated PartialCompileInput contents = 4;
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
new file mode 100644
index 0000000..cbaf7d0
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. internal_state.proto