Merge "Create EXTRA_INSTALL_ZIPS variable" into main
diff --git a/android/config.go b/android/config.go
index f6711e6..76c590a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2109,6 +2109,7 @@
 		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS",
 		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION",
 		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION",
+		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS",
 		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING",
 		"RELEASE_APEX_CONTRIBUTIONS_RESOLV",
 		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING",
diff --git a/android/module.go b/android/module.go
index 40a5910..56cfc4c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2145,9 +2145,9 @@
 	ctx := e.ctx
 	m := e.m
 	switch condition.FunctionName() {
-	case "release_variable":
+	case "release_flag":
 		if condition.NumArgs() != 1 {
-			ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", condition.NumArgs())
+			ctx.OtherModulePropertyErrorf(m, property, "release_flag requires 1 argument, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
 		if v, ok := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]; ok {
diff --git a/android/override_module.go b/android/override_module.go
index 55f384f..21cf381 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,7 +28,6 @@
 // module based on it.
 
 import (
-	"fmt"
 	"sort"
 	"sync"
 
@@ -121,7 +120,7 @@
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
-	override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule)
+	override(ctx BaseModuleContext, m Module, o OverrideModule)
 	GetOverriddenBy() string
 	GetOverriddenByModuleDir() string
 
@@ -192,14 +191,15 @@
 }
 
 // Overrides a base module with the given OverrideModule.
-func (b *OverridableModuleBase) override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule) {
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) {
+
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
 				err := proptools.ExtendProperties(p, op, nil, proptools.OrderReplace)
 				if err != nil {
 					if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-						ctx.OtherModulePropertyErrorf(bm, propertyErr.Property, "%s", propertyErr.Err.Error())
+						ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
 					} else {
 						panic(err)
 					}
@@ -210,7 +210,7 @@
 	// Adds the base module to the overrides property, if exists, of the overriding module. See the
 	// comment on OverridableModuleBase.overridesProperty for details.
 	if b.overridesProperty != nil {
-		*b.overridesProperty = append(*b.overridesProperty, ctx.OtherModuleName(bm))
+		*b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
 	}
 	b.overridableModuleProperties.OverriddenBy = o.Name()
 	b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
@@ -235,7 +235,7 @@
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
-	ctx.Transition("override", &overrideTransitionMutator{})
+	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
@@ -262,6 +262,18 @@
 			ctx.PropertyErrorf("base", "%q is not a valid module name", base)
 			return
 		}
+		// See if there's a prebuilt module that overrides this override module with prefer flag,
+		// in which case we call HideFromMake on the corresponding variant later.
+		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
+			prebuilt := GetEmbeddedPrebuilt(dep)
+			if prebuilt == nil {
+				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
+			}
+			if prebuilt.UsePrebuilt() {
+				module.setOverriddenByPrebuilt(dep)
+				return
+			}
+		})
 		baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
 		if o, ok := baseModule.(OverridableModule); ok {
 			overrideModule := ctx.Module().(OverrideModule)
@@ -273,13 +285,11 @@
 
 // Now, goes through all overridable modules, finds all modules overriding them, creates a local
 // variant for each of them, and performs the actual overriding operation by calling override().
-type overrideTransitionMutator struct{}
-
-func (overrideTransitionMutator) Split(ctx BaseModuleContext) []string {
+func performOverrideMutator(ctx BottomUpMutatorContext) {
 	if b, ok := ctx.Module().(OverridableModule); ok {
 		overrides := b.getOverrides()
 		if len(overrides) == 0 {
-			return []string{""}
+			return
 		}
 		variants := make([]string, len(overrides)+1)
 		// The first variant is for the original, non-overridden, base module.
@@ -287,69 +297,27 @@
 		for i, o := range overrides {
 			variants[i+1] = o.(Module).Name()
 		}
-		return variants
+		mods := ctx.CreateLocalVariations(variants...)
+		// Make the original variation the default one to depend on if no other override module variant
+		// is specified.
+		ctx.AliasVariation(variants[0])
+		for i, o := range overrides {
+			mods[i+1].(OverridableModule).override(ctx, mods[i+1], o)
+			if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil {
+				// The overriding module itself, too, is overridden by a prebuilt.
+				// Perform the same check for replacement
+				checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt)
+				// Copy the flag and hide it in make
+				mods[i+1].ReplacedByPrebuilt()
+			}
+		}
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
 		// Create a variant of the overriding module with its own name. This matches the above local
 		// variant name rule for overridden modules, and thus allows ReplaceDependencies to match the
 		// two.
-		return []string{o.Name()}
-	}
-
-	return []string{""}
-}
-
-func (overrideTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
-	if o, ok := ctx.Module().(OverrideModule); ok {
-		if ctx.DepTag() == overrideBaseDepTag {
-			return o.Name()
-		}
-	}
-
-	// Variations are always local and shouldn't affect the variant used for dependencies
-	return ""
-}
-
-func (overrideTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
-	if _, ok := ctx.Module().(OverridableModule); ok {
-		return incomingVariation
-	} else if o, ok := ctx.Module().(OverrideModule); ok {
-		// To allow dependencies to be added without having to know the variation.
-		return o.Name()
-	}
-
-	return ""
-}
-
-func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
-	if o, ok := ctx.Module().(OverrideModule); ok {
-		overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag)
-		if len(overridableDeps) > 1 {
-			panic(fmt.Errorf("expected a single dependency with overrideBaseDepTag, found %q", overridableDeps))
-		} else if len(overridableDeps) == 1 {
-			b := overridableDeps[0].(OverridableModule)
-			b.override(ctx, b, o)
-
-			checkPrebuiltReplacesOverride(ctx, b)
-		}
-	}
-}
-
-func checkPrebuiltReplacesOverride(ctx BottomUpMutatorContext, b OverridableModule) {
-	// See if there's a prebuilt module that overrides this override module with prefer flag,
-	// in which case we call HideFromMake on the corresponding variant later.
-	prebuiltDeps := ctx.GetDirectDepsWithTag(PrebuiltDepTag)
-	for _, prebuiltDep := range prebuiltDeps {
-		prebuilt := GetEmbeddedPrebuilt(prebuiltDep)
-		if prebuilt == nil {
-			panic("PrebuiltDepTag leads to a non-prebuilt module " + prebuiltDep.Name())
-		}
-		if prebuilt.UsePrebuilt() {
-			// The overriding module itself, too, is overridden by a prebuilt.
-			// Perform the same check for replacement
-			checkInvariantsForSourceAndPrebuilt(ctx, b, prebuiltDep)
-			// Copy the flag and hide it in make
-			b.ReplacedByPrebuilt()
-		}
+		ctx.CreateLocalVariations(o.Name())
+		// To allow dependencies to be added without having to know the above variation.
+		ctx.AliasVariation(o.Name())
 	}
 }
 
diff --git a/cc/Android.bp b/cc/Android.bp
index 9ce8933..3bbcaa9 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -51,6 +51,7 @@
         "vndk.go",
         "vndk_prebuilt.go",
 
+        "cmake_snapshot.go",
         "cmakelists.go",
         "compdb.go",
         "compiler.go",
@@ -92,6 +93,7 @@
         "binary_test.go",
         "cc_test.go",
         "cc_test_only_property_test.go",
+        "cmake_snapshot_test.go",
         "compiler_test.go",
         "gen_test.go",
         "genrule_test.go",
@@ -109,5 +111,12 @@
         "tidy_test.go",
         "vendor_public_library_test.go",
     ],
+    embedSrcs: [
+        "cmake_ext_add_aidl_library.txt",
+        "cmake_ext_append_flags.txt",
+        "cmake_main.txt",
+        "cmake_module_aidl.txt",
+        "cmake_module_cc.txt",
+    ],
     pluginFor: ["soong_build"],
 }
diff --git a/cc/cc.go b/cc/cc.go
index 2980c1a..81e7be0 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -353,6 +353,10 @@
 	// for building binaries that are started before APEXes are activated.
 	Bootstrap *bool
 
+	// Allows this module to be included in CMake release snapshots to be built outside of Android
+	// build system and source tree.
+	Cmake_snapshot_supported *bool
+
 	// Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant.
 	// see soong/cc/config/vndk.go
 	MustUseVendorVariant bool `blueprint:"mutated"`
@@ -588,6 +592,7 @@
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags
 	compilerProps() []interface{}
+	baseCompilerProps() BaseCompilerProperties
 
 	appendCflags([]string)
 	appendAsflags([]string)
@@ -602,6 +607,7 @@
 	linkerDeps(ctx DepsContext, deps Deps) Deps
 	linkerFlags(ctx ModuleContext, flags Flags) Flags
 	linkerProps() []interface{}
+	baseLinkerProps() BaseLinkerProperties
 	useClangLld(actx ModuleContext) bool
 
 	link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path
@@ -2163,6 +2169,10 @@
 
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
 
+	if Bool(c.Properties.Cmake_snapshot_supported) {
+		android.SetProvider(ctx, cmakeSnapshotSourcesProvider, android.GlobFiles(ctx, ctx.ModuleDir()+"/**/*", nil))
+	}
+
 	android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
 
 	c.maybeInstall(ctx, apexInfo)
diff --git a/cc/cmake_ext_add_aidl_library.txt b/cc/cmake_ext_add_aidl_library.txt
new file mode 100644
index 0000000..dcf805a
--- /dev/null
+++ b/cc/cmake_ext_add_aidl_library.txt
@@ -0,0 +1,47 @@
+function(add_aidl_library NAME LANG SOURCES AIDLFLAGS)
+    if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20")
+        cmake_policy(SET CMP0116 NEW)
+    endif()
+
+    set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/.intermediates/${NAME}-source")
+    set(GEN_SOURCES)
+    foreach(SOURCE ${SOURCES})
+        get_filename_component(SOURCE_WE ${SOURCE} NAME_WE)
+        get_filename_component(SOURCE_ABSOLUTE ${SOURCE} ABSOLUTE)
+        get_filename_component(SOURCE_DIR ${SOURCE_ABSOLUTE} DIRECTORY)
+        set(GEN_SOURCE "${GEN_DIR}/${SOURCE_WE}.cpp")
+        set(DEPFILE_ARG)
+        if (NOT ${CMAKE_GENERATOR} MATCHES "Unix Makefiles")
+            set(DEPFILE_ARG DEPFILE "${GEN_SOURCE}.d")
+        endif()
+        add_custom_command(
+            OUTPUT "${GEN_SOURCE}"
+            MAIN_DEPENDENCY "${SOURCE_ABSOLUTE}"
+            ${DEPFILE_ARG}
+            COMMAND "${AIDL_BIN}"
+            ARGS
+            --lang=${LANG}
+            --include="${SOURCE_DIR}"
+            --dep="${GEN_SOURCE}.d"
+            --out="${GEN_DIR}"
+            --header_out="${GEN_DIR}/include"
+            --ninja
+            --structured
+            --min_sdk_version=current
+            ${AIDLFLAGS}
+            "${SOURCE_ABSOLUTE}"
+        )
+        list(APPEND GEN_SOURCES "${GEN_SOURCE}")
+    endforeach()
+
+    add_library(${NAME} ${GEN_SOURCES})
+
+    target_include_directories(${NAME}
+        PUBLIC
+        "${GEN_DIR}/include"
+        "${ANDROID_BUILD_TOP}/frameworks/native/libs/binder/ndk/include_${LANG}"
+    )
+    target_link_libraries(${NAME}
+        libbinder_sdk
+    )
+endfunction()
diff --git a/cc/cmake_ext_append_flags.txt b/cc/cmake_ext_append_flags.txt
new file mode 100644
index 0000000..2cfb1ac
--- /dev/null
+++ b/cc/cmake_ext_append_flags.txt
@@ -0,0 +1,10 @@
+include(CheckCXXCompilerFlag)
+
+macro(append_cxx_flags_if_supported VAR)
+  foreach(FLAG ${ARGN})
+    check_cxx_compiler_flag(${FLAG} HAS_FLAG${FLAG})
+    if(${HAS_FLAG${FLAG}})
+      list(APPEND ${VAR} ${FLAG})
+    endif()
+  endforeach()
+endmacro()
diff --git a/cc/cmake_main.txt b/cc/cmake_main.txt
new file mode 100644
index 0000000..deb1de1
--- /dev/null
+++ b/cc/cmake_main.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.18)
+project(<<.M.Name>> CXX)
+set(CMAKE_CXX_STANDARD 20)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+include(AddAidlLibrary)
+include(AppendCxxFlagsIfSupported)
+
+if (NOT ANDROID_BUILD_TOP)
+    set(ANDROID_BUILD_TOP "${CMAKE_CURRENT_SOURCE_DIR}")
+endif()
+
+set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux-x86/bin")
+if (NOT AIDL_BIN)
+    find_program(AIDL_BIN aidl REQUIRED HINTS "${PREBUILTS_BIN_DIR}")
+endif()
+
+<<cflagsList .M.Name "_CFLAGS" .M.Properties.Cflags .M.Properties.Unportable_flags .M.Properties.Cflags_ignored>>
+
+<<range .Pprop.SystemPackages ->>
+find_package(<<.>> REQUIRED)
+<<end >>
+<<range .Pprop.PregeneratedPackages ->>
+add_subdirectory("${ANDROID_BUILD_TOP}/<<.>>" "<<.>>/build" EXCLUDE_FROM_ALL)
+<<end>>
+add_compile_options(${<<.M.Name>>_CFLAGS})
+<<range $moduleDir, $value := .ModuleDirs ->>
+add_subdirectory(<<$moduleDir>>)
+<<end>>
diff --git a/cc/cmake_module_aidl.txt b/cc/cmake_module_aidl.txt
new file mode 100644
index 0000000..4509a88
--- /dev/null
+++ b/cc/cmake_module_aidl.txt
@@ -0,0 +1,8 @@
+# <<.M.Name>>
+
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (getCompilerProperties .M).AidlInterface.Sources>>
+
+<<setList .M.Name "_AIDLFLAGS" "" (getCompilerProperties .M).AidlInterface.Flags>>
+
+add_aidl_library(<<.M.Name>> <<(getCompilerProperties .M).AidlInterface.Lang>> "${<<.M.Name>>_SRCS}" "${<<.M.Name>>_AIDLFLAGS}")
+add_library(android::<<.M.Name>> ALIAS <<.M.Name>>)
diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt
new file mode 100644
index 0000000..571f27c
--- /dev/null
+++ b/cc/cmake_module_cc.txt
@@ -0,0 +1,35 @@
+<<$srcs := getSources .M>>
+<<$includeDirs := getIncludeDirs .Ctx .M>>
+<<$cflags := (getCompilerProperties .M).Cflags>>
+<<$deps := mapLibraries (concat5
+(getLinkerProperties .M).Whole_static_libs
+(getLinkerProperties .M).Static_libs
+(getLinkerProperties .M).Shared_libs
+(getLinkerProperties .M).Header_libs
+(getExtraLibs .M)
+) .Pprop.LibraryMapping>>
+
+# <<.M.Name>>
+<<if $srcs>>
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (toStrings $srcs)>>
+add_<<getModuleType .M>>(<<.M.Name>> ${<<.M.Name>>_SRCS})
+<<- else>>
+add_<<getModuleType .M>>(<<.M.Name>> INTERFACE)
+<<- end>>
+add_<<getModuleType .M>>(android::<<.M.Name>> ALIAS <<.M.Name>>)
+<<print "">>
+
+<<- if $includeDirs>>
+<<setList .M.Name "_INCLUDES" "${ANDROID_BUILD_TOP}/" $includeDirs>>
+target_include_directories(<<.M.Name>> <<if $srcs>>PUBLIC<<else>>INTERFACE<<end>> ${<<.M.Name>>_INCLUDES})
+<<end>>
+
+<<- if and $srcs $cflags>>
+<<cflagsList .M.Name "_CFLAGS" $cflags .Snapshot.Properties.Unportable_flags .Snapshot.Properties.Cflags_ignored>>
+target_compile_options(<<.M.Name>> PRIVATE ${<<.M.Name>>_CFLAGS})
+<<end>>
+
+<<- if $deps>>
+<<setList .M.Name "_DEPENDENCIES" "" $deps>>
+target_link_libraries(<<.M.Name>> <<if not $srcs>>INTERFACE <<end ->> ${<<.M.Name>>_DEPENDENCIES})
+<<end>>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
new file mode 100644
index 0000000..3ac7db1
--- /dev/null
+++ b/cc/cmake_snapshot.go
@@ -0,0 +1,509 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"bytes"
+	_ "embed"
+	"fmt"
+	"path/filepath"
+	"slices"
+	"sort"
+	"strings"
+	"text/template"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+const veryVerbose bool = false
+
+//go:embed cmake_main.txt
+var templateCmakeMainRaw string
+var templateCmakeMain *template.Template = parseTemplate(templateCmakeMainRaw)
+
+//go:embed cmake_module_cc.txt
+var templateCmakeModuleCcRaw string
+var templateCmakeModuleCc *template.Template = parseTemplate(templateCmakeModuleCcRaw)
+
+//go:embed cmake_module_aidl.txt
+var templateCmakeModuleAidlRaw string
+var templateCmakeModuleAidl *template.Template = parseTemplate(templateCmakeModuleAidlRaw)
+
+//go:embed cmake_ext_add_aidl_library.txt
+var cmakeExtAddAidlLibrary string
+
+//go:embed cmake_ext_append_flags.txt
+var cmakeExtAppendFlags string
+
+var defaultUnportableFlags []string = []string{
+	"-Wno-class-memaccess",
+	"-Wno-exit-time-destructors",
+	"-Wno-inconsistent-missing-override",
+	"-Wreorder-init-list",
+	"-Wno-reorder-init-list",
+	"-Wno-restrict",
+	"-Wno-stringop-overread",
+	"-Wno-subobject-linkage",
+}
+
+var ignoredSystemLibs []string = []string{
+	"libc++",
+	"libc++_static",
+	"prebuilt_libclang_rt.builtins",
+	"prebuilt_libclang_rt.ubsan_minimal",
+}
+
+// Mapping entry between Android's library name and the one used when building outside Android tree.
+type LibraryMappingProperty struct {
+	// Android library name.
+	Android_name string
+
+	// Library name used when building outside Android.
+	Mapped_name string
+
+	// If the make file is already present in Android source tree, specify its location.
+	Package_pregenerated string
+
+	// If the package is expected to be installed on the build host OS, specify its name.
+	Package_system string
+}
+
+type CmakeSnapshotProperties struct {
+	// Modules to add to the snapshot package. Their dependencies are pulled in automatically.
+	Modules []string
+
+	// Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android.
+	Prebuilts []string
+
+	// Global cflags to add when building outside Android.
+	Cflags []string
+
+	// Flags to skip when building outside Android.
+	Cflags_ignored []string
+
+	// Mapping between library names used in Android tree and externally.
+	Library_mapping []LibraryMappingProperty
+
+	// List of cflags that are not portable between compilers that could potentially be used to
+	// build a generated package. If left empty, it's initialized with a default list.
+	Unportable_flags []string
+
+	// Whether to include source code as part of the snapshot package.
+	Include_sources bool
+}
+
+var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]()
+
+type CmakeSnapshot struct {
+	android.ModuleBase
+
+	Properties CmakeSnapshotProperties
+
+	zipPath android.WritablePath
+}
+
+type cmakeProcessedProperties struct {
+	LibraryMapping       map[string]LibraryMappingProperty
+	PregeneratedPackages []string
+	SystemPackages       []string
+}
+
+type cmakeSnapshotDependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var (
+	cmakeSnapshotModuleTag   = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"}
+	cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"}
+)
+
+func parseTemplate(templateContents string) *template.Template {
+	funcMap := template.FuncMap{
+		"setList": func(name string, nameSuffix string, itemPrefix string, items []string) string {
+			var list strings.Builder
+			list.WriteString("set(" + name + nameSuffix)
+			templateListBuilder(&list, itemPrefix, items)
+			return list.String()
+		},
+		"toStrings": func(files android.Paths) []string {
+			strings := make([]string, len(files))
+			for idx, file := range files {
+				strings[idx] = file.String()
+			}
+			return strings
+		},
+		"concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string {
+			return append(append(append(append(list1, list2...), list3...), list4...), list5...)
+		},
+		"cflagsList": func(name string, nameSuffix string, flags []string,
+			unportableFlags []string, ignoredFlags []string) string {
+			if len(unportableFlags) == 0 {
+				unportableFlags = defaultUnportableFlags
+			}
+
+			var filteredPortable []string
+			var filteredUnportable []string
+			for _, flag := range flags {
+				if slices.Contains(ignoredFlags, flag) {
+					continue
+				} else if slices.Contains(unportableFlags, flag) {
+					filteredUnportable = append(filteredUnportable, flag)
+				} else {
+					filteredPortable = append(filteredPortable, flag)
+				}
+			}
+
+			var list strings.Builder
+
+			list.WriteString("set(" + name + nameSuffix)
+			templateListBuilder(&list, "", filteredPortable)
+
+			if len(filteredUnportable) > 0 {
+				list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix)
+				templateListBuilder(&list, "", filteredUnportable)
+			}
+
+			return list.String()
+		},
+		"getSources": func(m *Module) android.Paths {
+			return m.compiler.(CompiledInterface).Srcs()
+		},
+		"getModuleType": getModuleType,
+		"getCompilerProperties": func(m *Module) BaseCompilerProperties {
+			return m.compiler.baseCompilerProps()
+		},
+		"getLinkerProperties": func(m *Module) BaseLinkerProperties {
+			return m.linker.baseLinkerProps()
+		},
+		"getExtraLibs":   getExtraLibs,
+		"getIncludeDirs": getIncludeDirs,
+		"mapLibraries": func(libs []string, mapping map[string]LibraryMappingProperty) []string {
+			var mappedLibs []string
+			for _, lib := range libs {
+				mappedLib, exists := mapping[lib]
+				if exists {
+					lib = mappedLib.Mapped_name
+				} else {
+					lib = "android::" + lib
+				}
+				if lib == "" {
+					continue
+				}
+				mappedLibs = append(mappedLibs, lib)
+			}
+			sort.Strings(mappedLibs)
+			mappedLibs = slices.Compact(mappedLibs)
+			return mappedLibs
+		},
+	}
+
+	return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents))
+}
+
+func sliceWithPrefix(prefix string, slice []string) []string {
+	output := make([]string, len(slice))
+	for i, elem := range slice {
+		output[i] = prefix + elem
+	}
+	return output
+}
+
+func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) {
+	if len(items) > 0 {
+		builder.WriteString("\n")
+		for _, item := range items {
+			builder.WriteString("    " + itemPrefix + item + "\n")
+		}
+	}
+	builder.WriteString(")")
+}
+
+func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string {
+	buffer.Reset()
+	if err := templ.Execute(buffer, data); err != nil {
+		panic(err)
+	}
+	output := strings.TrimSpace(buffer.String())
+	buffer.Reset()
+	return output
+}
+
+func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+	variations := []blueprint.Variation{
+		{"os", "linux_glibc"},
+		{"arch", "x86_64"},
+	}
+	ctx.AddVariationDependencies(variations, cmakeSnapshotModuleTag, m.Properties.Modules...)
+	ctx.AddVariationDependencies(variations, cmakeSnapshotPrebuiltTag, m.Properties.Prebuilts...)
+}
+
+func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var templateBuffer bytes.Buffer
+	var pprop cmakeProcessedProperties
+	m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+
+	// Process Library_mapping for more efficient lookups
+	pprop.LibraryMapping = map[string]LibraryMappingProperty{}
+	for _, elem := range m.Properties.Library_mapping {
+		pprop.LibraryMapping[elem.Android_name] = elem
+
+		if elem.Package_pregenerated != "" {
+			pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated)
+		}
+		sort.Strings(pprop.PregeneratedPackages)
+		pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages)
+
+		if elem.Package_system != "" {
+			pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system)
+		}
+		sort.Strings(pprop.SystemPackages)
+		pprop.SystemPackages = slices.Compact(pprop.SystemPackages)
+	}
+
+	// Generating CMakeLists.txt rules for all modules in dependency tree
+	moduleDirs := map[string][]string{}
+	sourceFiles := map[string]android.Path{}
+	visitedModules := map[string]bool{}
+	var pregeneratedModules []*Module
+	ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
+		moduleName := ctx.OtherModuleName(dep_a)
+		dep, ok := dep_a.(*Module)
+		if !ok {
+			return false // not a cc module
+		}
+		if visited := visitedModules[moduleName]; visited {
+			return false // visit only once
+		}
+		visitedModules[moduleName] = true
+		if mapping, ok := pprop.LibraryMapping[moduleName]; ok {
+			if mapping.Package_pregenerated != "" {
+				pregeneratedModules = append(pregeneratedModules, dep)
+			}
+			return false // mapped to system or pregenerated (we'll handle these later)
+		}
+		if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag {
+			return false // we'll handle cmakeSnapshotPrebuiltTag later
+		}
+		if slices.Contains(ignoredSystemLibs, moduleName) {
+			return false // system libs built in-tree for Android
+		}
+		if dep.compiler == nil {
+			return false // unsupported module type (e.g. prebuilt)
+		}
+		isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
+
+		if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+			ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
+				"CMake snapshots not supported, despite being a dependency for %s",
+				ctx.OtherModuleName(parent))
+			return false
+		}
+
+		if veryVerbose {
+			fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName)
+		}
+
+		// Generate CMakeLists.txt fragment for this module
+		templateToUse := templateCmakeModuleCc
+		if isAidlModule {
+			templateToUse = templateCmakeModuleAidl
+		}
+		moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
+			Ctx      *android.ModuleContext
+			M        *Module
+			Snapshot *CmakeSnapshot
+			Pprop    *cmakeProcessedProperties
+		}{
+			&ctx,
+			dep,
+			m,
+			&pprop,
+		})
+		moduleDir := ctx.OtherModuleDir(dep)
+		moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment)
+
+		if m.Properties.Include_sources {
+			files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
+			for _, file := range files {
+				sourceFiles[file.String()] = file
+			}
+		}
+
+		// if it's AIDL module, no need to dive into their dependencies
+		return !isAidlModule
+	})
+
+	// Enumerate sources for pregenerated modules
+	if m.Properties.Include_sources {
+		for _, dep := range pregeneratedModules {
+			if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+				ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
+					"Pregenerated CMake snapshots not supported, despite being requested for %s",
+					ctx.ModuleName())
+				continue
+			}
+
+			files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
+			for _, file := range files {
+				sourceFiles[file.String()] = file
+			}
+		}
+	}
+
+	// Merging CMakeLists.txt contents for every module directory
+	var makefilesList android.Paths
+	for moduleDir, fragments := range moduleDirs {
+		moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
+		makefilesList = append(makefilesList, moduleCmakePath)
+		sort.Strings(fragments)
+		android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n"))
+	}
+
+	// Generating top-level CMakeLists.txt
+	mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt")
+	makefilesList = append(makefilesList, mainCmakePath)
+	mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct {
+		Ctx        *android.ModuleContext
+		M          *CmakeSnapshot
+		ModuleDirs map[string][]string
+		Pprop      *cmakeProcessedProperties
+	}{
+		&ctx,
+		m,
+		moduleDirs,
+		&pprop,
+	})
+	android.WriteFileRule(ctx, mainCmakePath, mainContents)
+
+	// Generating CMake extensions
+	extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake")
+	makefilesList = append(makefilesList, extPath)
+	android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags)
+	extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake")
+	makefilesList = append(makefilesList, extPath)
+	android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary)
+
+	// Generating the final zip file
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+	zipCmd := zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", m.zipPath)
+
+	// Packaging all sources into the zip file
+	if m.Properties.Include_sources {
+		var sourcesList android.Paths
+		for _, file := range sourceFiles {
+			sourcesList = append(sourcesList, file)
+		}
+
+		sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
+		zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList)
+	}
+
+	// Packaging all make files into the zip file
+	makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
+	zipCmd.
+		FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
+		FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
+
+	// Packaging all prebuilts into the zip file
+	if len(m.Properties.Prebuilts) > 0 {
+		var prebuiltsList android.Paths
+
+		ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
+			for _, file := range dep.FilesToInstall() {
+				prebuiltsList = append(prebuiltsList, file)
+			}
+		})
+
+		prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp")
+		zipCmd.
+			FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()).
+			FlagWithArg("-P ", "prebuilts").
+			FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList)
+	}
+
+	// Finish generating the final zip file
+	zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
+}
+
+func (m *CmakeSnapshot) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{m.zipPath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "DATA",
+		OutputFile: android.OptionalPathForPath(m.zipPath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+			},
+		},
+	}}
+}
+
+func getModuleType(m *Module) string {
+	switch m.linker.(type) {
+	case *binaryDecorator:
+		return "executable"
+	case *libraryDecorator:
+		return "library"
+	case *testBinary:
+		return "executable"
+	}
+	panic(fmt.Sprintf("Unexpected module type: %T", m.compiler))
+}
+
+func getExtraLibs(m *Module) []string {
+	switch decorator := m.linker.(type) {
+	case *testBinary:
+		if decorator.testDecorator.gtest() {
+			return []string{"libgtest"}
+		}
+	}
+	return nil
+}
+
+func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
+	moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
+	switch decorator := m.compiler.(type) {
+	case *libraryDecorator:
+		return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs)
+	}
+	return nil
+}
+
+// cmake_snapshot allows defining source packages for release outside of Android build tree.
+// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions
+// for selected source modules, their dependencies and optionally also the source code itself.
+func CmakeSnapshotFactory() android.Module {
+	module := &CmakeSnapshot{}
+	module.AddProperties(&module.Properties)
+	android.InitAndroidModule(module)
+	return module
+}
+
+func init() {
+	android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
+}
diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go
new file mode 100644
index 0000000..cda187f
--- /dev/null
+++ b/cc/cmake_snapshot_test.go
@@ -0,0 +1,69 @@
+// 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 cc
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func wasGenerated(t *testing.T, m *android.TestingModule, fileName string, ruleType string) {
+	t.Helper()
+	ruleName := m.Output(fileName).Rule.String()
+	if !strings.HasSuffix(ruleName, ruleType) {
+		t.Errorf("Main Cmake file wasn't generated, expected rule %v, found %v", ruleType, ruleName)
+	}
+}
+
+func TestEmptyCmakeSnapshot(t *testing.T) {
+	t.Parallel()
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_cmake_snapshot {
+			name: "foo",
+			modules: [],
+			prebuilts: ["libc++"],
+			include_sources: true,
+		}`)
+
+	snapshotModule := result.ModuleForTests("foo", "")
+
+	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
+	wasGenerated(t, &snapshotModule, "foo.zip", "")
+}
+
+func TestCmakeSnapshotWithBinary(t *testing.T) {
+	t.Parallel()
+	xtra := android.FixtureAddTextFile("some/module/Android.bp", `
+		cc_binary {
+			name: "foo_binary",
+			host_supported: true,
+			cmake_snapshot_supported: true,
+		}
+	`)
+	result := android.GroupFixturePreparers(PrepareForIntegrationTestWithCc, xtra).RunTestWithBp(t, `
+		cc_cmake_snapshot {
+			name: "foo",
+			modules: [
+				"foo_binary",
+			],
+			include_sources: true,
+		}`)
+
+	snapshotModule := result.ModuleForTests("foo", "")
+
+	wasGenerated(t, &snapshotModule, "some/module/CMakeLists.txt", "rawFileCopy")
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index a1b329e..aee584d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -278,6 +278,10 @@
 	return []interface{}{&compiler.Properties, &compiler.Proto}
 }
 
+func (compiler *baseCompiler) baseCompilerProps() BaseCompilerProperties {
+	return compiler.Properties
+}
+
 func includeBuildDirectory(prop *bool) bool {
 	return proptools.BoolDefault(prop, true)
 }
diff --git a/cc/linker.go b/cc/linker.go
index 56a68b2..1d0f205 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -287,6 +287,10 @@
 	return []interface{}{&linker.Properties, &linker.dynamicProperties}
 }
 
+func (linker *baseLinker) baseLinkerProps() BaseLinkerProperties {
+	return linker.Properties
+}
+
 func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
 	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
diff --git a/cc/testing.go b/cc/testing.go
index 4b4e866..c3a33cb 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -35,6 +35,7 @@
 
 	ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
+	ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go
index 4d763c8..cd39ffd 100644
--- a/cmd/release_config/crunch_flags/main.go
+++ b/cmd/release_config/crunch_flags/main.go
@@ -137,7 +137,9 @@
 		workflow := rc_proto.Workflow(rc_proto.Workflow_PREBUILT)
 		switch {
 		case declName == "RELEASE_ACONFIG_VALUE_SETS":
-			rootAconfigModule = declValue[1 : len(declValue)-1]
+			if strings.HasPrefix(declValue, "\"") {
+				rootAconfigModule = declValue[1 : len(declValue)-1]
+			}
 			continue
 		case strings.HasPrefix(declValue, "\""):
 			// String values mean that the flag workflow is (most likely) either MANUAL or PREBUILT.
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 8a4e2d5..2487f2e 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -291,6 +291,9 @@
 		allReleaseNames = append(allReleaseNames, v.Name)
 		allReleaseNames = append(allReleaseNames, v.OtherNames...)
 	}
+	slices.SortFunc(allReleaseNames, func(a, b string) int {
+		return cmp.Compare(a, b)
+	})
 	config, err := configs.GetReleaseConfig(targetRelease)
 	if err != nil {
 		return err
diff --git a/python/python.go b/python/python.go
index e14fdf3..1ee533f 100644
--- a/python/python.go
+++ b/python/python.go
@@ -506,8 +506,8 @@
 	}
 
 	for _, d := range expandedData {
-		if d.Ext() == pyExt || d.Ext() == protoExt {
-			ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String())
+		if d.Ext() == pyExt {
+			ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String())
 			continue
 		}
 		runfilesPath := filepath.Join(pkgPath, d.Rel())
@@ -523,19 +523,19 @@
 	relativeRootMap := make(map[string]android.Paths)
 	var protoSrcs android.Paths
 	addPathMapping := func(path pathMapping) {
-		// handle proto sources separately
-		if path.src.Ext() == protoExt {
-			protoSrcs = append(protoSrcs, path.src)
-		} else {
-			relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
-			relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
-		}
+		relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+		relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
 	}
 
 	// "srcs" or "data" properties may contain filegroups so it might happen that
 	// the root directory for each source path is different.
 	for _, path := range p.srcsPathMappings {
-		addPathMapping(path)
+		// handle proto sources separately
+		if path.src.Ext() == protoExt {
+			protoSrcs = append(protoSrcs, path.src)
+		} else {
+			addPathMapping(path)
+		}
 	}
 	for _, path := range p.dataPathMappings {
 		addPathMapping(path)
diff --git a/python/python_test.go b/python/python_test.go
index c0b7295..6a6bd1d 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -50,7 +50,7 @@
 		" Second file: in module %s at path %q."
 	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
 	badSrcFileExtErr  = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
-	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
+	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!"
 	bpFile            = "Android.bp"
 
 	data = []struct {