Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78ef5dde authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add find_input_delta" into main

parents 25fc1093 b40260df
Loading
Loading
Loading
Loading
+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",
    ],
}
+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)
	}
}
+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",
    ],
}
+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)
}
+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