Merge "Add test that LTO isn't propagated to runtime_libs"
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 9ff6b52..d5886dc 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -45,11 +45,14 @@
 		CommandDeps: []string{"${bazelBuildRunfilesTool}"},
 	}, "outDir")
 	allowedBazelEnvironmentVars = []string{
+		// clang-tidy
 		"ALLOW_LOCAL_TIDY_TRUE",
 		"DEFAULT_TIDY_HEADER_DIRS",
 		"TIDY_TIMEOUT",
 		"WITH_TIDY",
 		"WITH_TIDY_FLAGS",
+		"TIDY_EXTERNAL_VENDOR",
+
 		"SKIP_ABI_CHECKS",
 		"UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK",
 		"AUTO_ZERO_INITIALIZE",
diff --git a/android/config.go b/android/config.go
index 600fda0..bb3cc97 100644
--- a/android/config.go
+++ b/android/config.go
@@ -397,11 +397,13 @@
 arch_variant_product_var_constraints = _arch_variant_product_var_constraints
 `,
 	}
-	err = os.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644)
+	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variables.bzl"),
+		[]byte(strings.Join(bzl, "\n")), 0644)
 	if err != nil {
 		return fmt.Errorf("Could not write .bzl config file %s", err)
 	}
-	err = os.WriteFile(filepath.Join(dir, "BUILD"), []byte(bazel.GeneratedBazelFileWarning), 0644)
+	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "BUILD"),
+		[]byte(bazel.GeneratedBazelFileWarning), 0644)
 	if err != nil {
 		return fmt.Errorf("Could not write BUILD config file %s", err)
 	}
diff --git a/android/mutator.go b/android/mutator.go
index 4e55609..4dacb8d 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -709,24 +709,29 @@
 // module and returns it as a list of keyed tags.
 func ApexAvailableTags(mod Module) bazel.StringListAttribute {
 	attr := bazel.StringListAttribute{}
-	tags := []string{}
 	// Transform specific attributes into tags.
 	if am, ok := mod.(ApexModule); ok {
 		// TODO(b/218841706): hidl_interface has the apex_available prop, but it's
 		// defined directly as a prop and not via ApexModule, so this doesn't
 		// pick those props up.
-		// TODO(b/260694842): This does not pick up aidl_interface.backend.ndk.apex_available.
-		for _, a := range am.apexModuleBase().ApexAvailable() {
-			tags = append(tags, "apex_available="+a)
-		}
-	}
-	if len(tags) > 0 {
-		// This avoids creating a tags attr with an empty list if there are no tags.
-		attr.Value = tags
+		attr.Value = ConvertApexAvailableToTags(am.apexModuleBase().ApexAvailable())
 	}
 	return attr
 }
 
+func ConvertApexAvailableToTags(apexAvailable []string) []string {
+	if len(apexAvailable) == 0 {
+		// We need nil specifically to make bp2build not add the tags property at all,
+		// instead of adding it with an empty list
+		return nil
+	}
+	result := make([]string, 0, len(apexAvailable))
+	for _, a := range apexAvailable {
+		result = append(result, "apex_available="+a)
+	}
+	return result
+}
+
 func (t *topDownMutatorContext) createBazelTargetModule(
 	bazelProps bazel.BazelTargetModuleProperties,
 	commonAttrs CommonAttributes,
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 21eebd2..dbdfa33 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 	"strings"
 	"testing"
 
@@ -267,3 +268,22 @@
 		FixtureWithRootAndroidBp(`test {name: "foo"}`),
 	).RunTest(t)
 }
+
+func TestConvertApexAvailableToTags(t *testing.T) {
+	input := []string{
+		"com.android.adbd",
+		"//apex_available:platform",
+	}
+	actual := ConvertApexAvailableToTags(input)
+	expected := []string{
+		"apex_available=com.android.adbd",
+		"apex_available=//apex_available:platform",
+	}
+	if !reflect.DeepEqual(actual, expected) {
+		t.Errorf("Expected: %v, actual: %v", expected, actual)
+	}
+
+	if ConvertApexAvailableToTags(nil) != nil {
+		t.Errorf("Expected providing nil to return nil")
+	}
+}
diff --git a/android/paths.go b/android/paths.go
index 2c50104..6c3009f 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -2099,13 +2098,16 @@
 
 // Writes a file to the output directory.  Attempting to write directly to the output directory
 // will fail due to the sandbox of the soong_build process.
+// Only writes the file if the file doesn't exist or if it has different contents, to prevent
+// updating the timestamp if no changes would be made. (This is better for incremental
+// performance.)
 func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
 	absPath := absolutePath(path.String())
 	err := os.MkdirAll(filepath.Dir(absPath), 0777)
 	if err != nil {
 		return err
 	}
-	return ioutil.WriteFile(absPath, data, perm)
+	return pathtools.WriteFileIfChanged(absPath, data, perm)
 }
 
 func RemoveAllOutputDir(path WritablePath) error {
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 6faed70..1581949 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -55,17 +55,17 @@
 			   touch ${out};
 			else
 				echo -e "\n******************************";
-				echo "ERROR: go/apex-allowed-deps-error";
+				echo "ERROR: go/apex-allowed-deps-error contains more information";
 				echo "******************************";
 				echo "Detected changes to allowed dependencies in updatable modules.";
 				echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
 				echo -e "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)\n";
 				echo "When submitting the generated CL, you must include the following information";
 				echo "in the commit message if you are adding a new dependency:";
-				echo "Apex-Size-Increase:";
-				echo "Previous-Platform-Support:";
-				echo "Aosp-First:";
-				echo "Test-Info:";
+				echo "Apex-Size-Increase: Expected binary size increase for affected APEXes (or the size of the .jar / .so file of the new library)";
+				echo "Previous-Platform-Support: Are the maintainers of the new dependency committed to supporting previous platform releases?";
+				echo "Aosp-First: Is the new dependency being developed AOSP-first or internal?";
+				echo "Test-Info: What’s the testing strategy for the new dependency? Does it have its own tests, and are you adding integration tests? How/when are the tests run?";
 				echo "You do not need OWNERS approval to submit the change, but mainline-modularization@";
 				echo "will periodically review additions and may require changes.";
 				echo -e "******************************\n";
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 183eb12..aac5e7d 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -25,6 +25,7 @@
 	"sync/atomic"
 
 	"android/soong/shared"
+	"github.com/google/blueprint/pathtools"
 )
 
 // A tree structure that describes what to do at each directory in the created
@@ -116,25 +117,13 @@
 		generatedBuildFileContent = packageDefaultVisibilityRegex.ReplaceAll(generatedBuildFileContent, []byte{})
 	}
 
-	outFile, err := os.Create(output)
-	if err != nil {
-		return err
+	newContents := generatedBuildFileContent
+	if newContents[len(newContents)-1] != '\n' {
+		newContents = append(newContents, '\n')
 	}
+	newContents = append(newContents, srcBuildFileContent...)
 
-	_, err = outFile.Write(generatedBuildFileContent)
-	if err != nil {
-		return err
-	}
-
-	if generatedBuildFileContent[len(generatedBuildFileContent)-1] != '\n' {
-		_, err = outFile.WriteString("\n")
-		if err != nil {
-			return err
-		}
-	}
-
-	_, err = outFile.Write(srcBuildFileContent)
-	return err
+	return pathtools.WriteFileIfChanged(output, newContents, 0666)
 }
 
 // Calls readdir() and returns it as a map from the basename of the files in dir
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 67208b2..b3619c8 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -35,7 +35,9 @@
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
-		"-Wl,-z,max-page-size=4096")
+		"-Wl,-z,max-page-size=4096",
+		"-Wl,-plugin-opt,-emulated-tls=0",
+	)
 
 	riscv64Cppflags = []string{}
 
diff --git a/cc/lto.go b/cc/lto.go
index 1afa1dd..581856b 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -91,11 +91,6 @@
 		return flags
 	}
 
-	// TODO(b/254713216): LTO doesn't work on riscv64 yet.
-	if ctx.Arch().ArchType == android.Riscv64 {
-		return flags
-	}
-
 	if lto.LTO(ctx) {
 		var ltoCFlag string
 		var ltoLdFlag string
diff --git a/rust/config/global.go b/rust/config/global.go
index 50ac1f7..ef428b8 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.65.0.p1"
+	RustDefaultVersion = "1.66.1"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index f3bad73..fda5ca0 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -803,7 +803,6 @@
   setup
 
   mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
-  touch a/a.txt
   cat > a/Android.bp <<EOF
 filegroup {
   name: "a",
@@ -813,7 +812,6 @@
 EOF
 
   mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
-  touch b/b.txt
   cat > b/Android.bp <<EOF
 filegroup {
   name: "b",
@@ -826,8 +824,8 @@
     fail "Build should have failed"
   fi
 
-  grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
-  grep -q -v "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} found but not expected"
+  # we should expect at least one error
+  grep -q -E "(a|b)/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for ${GENERATED_BUILD_FILE_NAME} not found"
 }
 
 function test_bp2build_back_and_forth_null_build {