Move the caching/restoring code from soong to blueprint to fully skip build actions.

Bug: 358425833
Test: Manually verified the generated ninja and mk files and CI.
Change-Id: Ieebb822c46f37c0ff55fad08531e9870a76cbd7b
diff --git a/android/Android.bp b/android/Android.bp
index 78eca11..16a34b7 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -59,6 +59,7 @@
         "gen_notice.go",
         "hooks.go",
         "image.go",
+        "init.go",
         "license.go",
         "license_kind.go",
         "license_metadata.go",
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index a7b65e0..38f1382 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"encoding/csv"
+	"encoding/gob"
 	"fmt"
 	"slices"
 	"strconv"
@@ -131,6 +132,28 @@
 	}
 }
 
+func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := encoder.Encode(c.properties)
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
+	r := bytes.NewBuffer(data)
+	decoder := gob.NewDecoder(r)
+	err := decoder.Decode(&c.properties)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
 	if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
 		panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
diff --git a/android/depset_generic.go b/android/depset_generic.go
index 45c1937..690987a 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 )
 
@@ -65,6 +68,30 @@
 	transitive []*DepSet[T]
 }
 
+func (d *DepSet[T]) GobEncode() ([]byte, error) {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := errors.Join(encoder.Encode(d.preorder), encoder.Encode(d.reverse),
+		encoder.Encode(d.order), encoder.Encode(d.direct), encoder.Encode(d.transitive))
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+func (d *DepSet[T]) GobDecode(data []byte) error {
+	r := bytes.NewBuffer(data)
+	decoder := gob.NewDecoder(r)
+	err := errors.Join(decoder.Decode(&d.preorder), decoder.Decode(&d.reverse),
+		decoder.Decode(&d.order), decoder.Decode(&d.direct), decoder.Decode(&d.transitive))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
 func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] {
 	var directCopy []T
diff --git a/android/init.go b/android/init.go
new file mode 100644
index 0000000..d5b486b
--- /dev/null
+++ b/android/init.go
@@ -0,0 +1,22 @@
+// 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 android
+
+import "encoding/gob"
+
+func init() {
+	gob.Register(ModuleOutPath{})
+	gob.Register(unstableInfo{})
+}
diff --git a/android/module.go b/android/module.go
index 7ce4e05..29d2dba 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 	"net/url"
 	"path/filepath"
@@ -1879,61 +1882,16 @@
 			return
 		}
 
-		incrementalAnalysis := false
-		incrementalEnabled := false
-		var cacheKey *blueprint.BuildActionCacheKey = nil
-		var incrementalModule *blueprint.Incremental = nil
-		if ctx.bp.GetIncrementalEnabled() {
-			if im, ok := m.module.(blueprint.Incremental); ok {
-				incrementalModule = &im
-				incrementalEnabled = im.IncrementalSupported()
-				incrementalAnalysis = ctx.bp.GetIncrementalAnalysis() && incrementalEnabled
-			}
-		}
-		if incrementalEnabled {
-			hash, err := proptools.CalculateHash(m.GetProperties())
-			if err != nil {
-				ctx.ModuleErrorf("failed to calculate properties hash: %s", err)
-				return
-			}
-			cacheInput := new(blueprint.BuildActionCacheInput)
-			cacheInput.PropertiesHash = hash
-			ctx.VisitDirectDeps(func(module Module) {
-				cacheInput.ProvidersHash =
-					append(cacheInput.ProvidersHash, ctx.bp.OtherModuleProviderInitialValueHashes(module))
-			})
-			hash, err = proptools.CalculateHash(&cacheInput)
-			if err != nil {
-				ctx.ModuleErrorf("failed to calculate cache input hash: %s", err)
-				return
-			}
-			cacheKey = &blueprint.BuildActionCacheKey{
-				Id:        ctx.bp.ModuleCacheKey(),
-				InputHash: hash,
-			}
+		m.module.GenerateAndroidBuildActions(ctx)
+		if ctx.Failed() {
+			return
 		}
 
-		restored := false
-		if incrementalAnalysis && cacheKey != nil {
-			restored = ctx.bp.RestoreBuildActions(cacheKey)
-		}
-
-		if !restored {
-			m.module.GenerateAndroidBuildActions(ctx)
-			if ctx.Failed() {
-				return
-			}
-
-			if x, ok := m.module.(IDEInfo); ok {
-				var result IdeInfo
-				x.IDEInfo(ctx, &result)
-				result.BaseModuleName = x.BaseModuleName()
-				SetProvider(ctx, IdeInfoProviderKey, result)
-			}
-		}
-
-		if incrementalEnabled && cacheKey != nil {
-			ctx.bp.CacheBuildActions(cacheKey, incrementalModule)
+		if x, ok := m.module.(IDEInfo); ok {
+			var result IdeInfo
+			x.IDEInfo(ctx, &result)
+			result.BaseModuleName = x.BaseModuleName()
+			SetProvider(ctx, IdeInfoProviderKey, result)
 		}
 
 		// Create the set of tagged dist files after calling GenerateAndroidBuildActions
@@ -2116,11 +2074,61 @@
 	absFrom string
 }
 
+func (p *katiInstall) GobEncode() ([]byte, error) {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := errors.Join(encoder.Encode(p.from), encoder.Encode(p.to),
+		encoder.Encode(p.implicitDeps), encoder.Encode(p.orderOnlyDeps),
+		encoder.Encode(p.executable), encoder.Encode(p.extraFiles),
+		encoder.Encode(p.absFrom))
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+func (p *katiInstall) GobDecode(data []byte) error {
+	r := bytes.NewBuffer(data)
+	decoder := gob.NewDecoder(r)
+	err := errors.Join(decoder.Decode(&p.from), decoder.Decode(&p.to),
+		decoder.Decode(&p.implicitDeps), decoder.Decode(&p.orderOnlyDeps),
+		decoder.Decode(&p.executable), decoder.Decode(&p.extraFiles),
+		decoder.Decode(&p.absFrom))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 type extraFilesZip struct {
 	zip Path
 	dir InstallPath
 }
 
+func (p *extraFilesZip) GobEncode() ([]byte, error) {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := errors.Join(encoder.Encode(p.zip), encoder.Encode(p.dir))
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+func (p *extraFilesZip) GobDecode(data []byte) error {
+	r := bytes.NewBuffer(data)
+	decoder := gob.NewDecoder(r)
+	err := errors.Join(decoder.Decode(&p.zip), decoder.Decode(&p.dir))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 type katiInstalls []katiInstall
 
 // BuiltInstalled returns the katiInstalls in the form used by $(call copy-many-files) in Make, a
diff --git a/android/packaging.go b/android/packaging.go
index 42009d5..6bc1c74 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 	"path/filepath"
 	"sort"
@@ -58,6 +61,36 @@
 	archType ArchType
 }
 
+func (p *PackagingSpec) GobEncode() ([]byte, error) {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := errors.Join(encoder.Encode(p.relPathInPackage), encoder.Encode(p.srcPath),
+		encoder.Encode(p.symlinkTarget), encoder.Encode(p.executable),
+		encoder.Encode(p.effectiveLicenseFiles), encoder.Encode(p.partition),
+		encoder.Encode(p.skipInstall), encoder.Encode(p.aconfigPaths),
+		encoder.Encode(p.archType))
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+func (p *PackagingSpec) GobDecode(data []byte) error {
+	r := bytes.NewBuffer(data)
+	decoder := gob.NewDecoder(r)
+	err := errors.Join(decoder.Decode(&p.relPathInPackage), decoder.Decode(&p.srcPath),
+		decoder.Decode(&p.symlinkTarget), decoder.Decode(&p.executable),
+		decoder.Decode(&p.effectiveLicenseFiles), decoder.Decode(&p.partition),
+		decoder.Decode(&p.skipInstall), decoder.Decode(&p.aconfigPaths),
+		decoder.Decode(&p.archType))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
 	if other == nil {
 		return false
diff --git a/android/paths.go b/android/paths.go
index d20b84a..e457959 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1766,6 +1766,32 @@
 	fullPath string
 }
 
+func (p *InstallPath) GobEncode() ([]byte, error) {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir),
+		encoder.Encode(p.partitionDir), encoder.Encode(p.partition),
+		encoder.Encode(p.makePath), encoder.Encode(p.fullPath))
+	if err != nil {
+		return nil, err
+	}
+
+	return w.Bytes(), nil
+}
+
+func (p *InstallPath) GobDecode(data []byte) error {
+	r := bytes.NewBuffer(data)
+	decoder := gob.NewDecoder(r)
+	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir),
+		decoder.Decode(&p.partitionDir), decoder.Decode(&p.partition),
+		decoder.Decode(&p.makePath), decoder.Decode(&p.fullPath))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // Will panic if called from outside a test environment.
 func ensureTestOnly() {
 	if PrefixInList(os.Args, "-test.") {