Introduce BazelStringOrLabelFromProp.

Soong supports string properties, but they are overloaded, and can mean
one of three things:

* path reference
* module reference
* string literal

Bazel has different types: label and string attributes. Thus there needs
to be a way to categorize them correctly in bp2build.

This CL introduces a new function to be used on properties like
apex_key.private_key / apex_key.public_key, as well as
android_app.certificate / apex.certificate.

It is important to disambiguate the prop betenn a string literal
attribute or file/rule target label attribute, so this functions does
just that.  The new attributes are then further handled by their
respective macros (apex_key, android_binary, apex).

Bug: 253557437
Fixes: 253557437
Test: presubmits, new tests

Change-Id: Id8111cdd60d3aabcae7d17fe9da84d0ee3966023
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 9c50098..b2ea22f 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -534,3 +534,55 @@
 	}
 	return outs
 }
+
+// BazelStringOrLabelFromProp splits a Soong module property that can be
+// either a string literal, path (with android:path tag) or a module reference
+// into separate bazel string or label attributes. Bazel treats string and label
+// attributes as distinct types, so this function categorizes a string property
+// into either one of them.
+//
+// e.g. apex.private_key = "foo.pem" can either refer to:
+//
+// 1. "foo.pem" in the current directory -> file target
+// 2. "foo.pem" module -> rule target
+// 3. "foo.pem" file in a different directory, prefixed by a product variable handled
+// in a bazel macro. -> string literal
+//
+// For the first two cases, they are defined using the label attribute. For the third case,
+// it's defined with the string attribute.
+func BazelStringOrLabelFromProp(
+	ctx TopDownMutatorContext,
+	propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) {
+
+	var labelAttr bazel.LabelAttribute
+	var strAttr bazel.StringAttribute
+
+	if propToDistinguish == nil {
+		// nil pointer
+		return labelAttr, strAttr
+	}
+
+	prop := String(propToDistinguish)
+	if SrcIsModule(prop) != "" {
+		// If it's a module (SrcIsModule will return the module name), set the
+		// resolved label to the label attribute.
+		labelAttr.SetValue(BazelLabelForModuleDepSingle(ctx, prop))
+	} else {
+		// Not a module name. This could be a string literal or a file target in
+		// the current dir. Check if the path exists:
+		path := ExistentPathForSource(ctx, ctx.ModuleDir(), prop)
+
+		if path.Valid() && parentDir(path.String()) == ctx.ModuleDir() {
+			// If it exists and the path is relative to the current dir, resolve the bazel label
+			// for the _file target_ and set it to the label attribute.
+			//
+			// Resolution is necessary because this could be a file in a subpackage.
+			labelAttr.SetValue(BazelLabelForModuleSrcSingle(ctx, prop))
+		} else {
+			// Otherwise, treat it as a string literal and assign to the string attribute.
+			strAttr.Value = propToDistinguish
+		}
+	}
+
+	return labelAttr, strAttr
+}
diff --git a/apex/apex.go b/apex/apex.go
index edba1f7..88a057f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2665,12 +2665,13 @@
 
 		// Certificate
 		if overridableProperties.Certificate == nil {
-			// delegated to the rule attr default
-			attrs.Certificate = nil
+			// If overridableProperties.Certificate is nil, clear this out as
+			// well with zeroed structs, so the override_apex does not use the
+			// base apex's certificate.
+			attrs.Certificate = bazel.LabelAttribute{}
+			attrs.Certificate_name = bazel.StringAttribute{}
 		} else {
-			certificateName, certificate := java.ParseCertificateToAttribute(ctx, overridableProperties.Certificate)
-			attrs.Certificate_name = certificateName
-			attrs.Certificate = certificate
+			attrs.Certificate, attrs.Certificate_name = android.BazelStringOrLabelFromProp(ctx, overridableProperties.Certificate)
 		}
 
 		// Prebuilts
@@ -3346,8 +3347,8 @@
 	Android_manifest      bazel.LabelAttribute
 	File_contexts         bazel.LabelAttribute
 	Key                   bazel.LabelAttribute
-	Certificate           *bazel.Label // used when the certificate prop is a module
-	Certificate_name      *string      // used when the certificate prop is a string
+	Certificate           bazel.LabelAttribute  // used when the certificate prop is a module
+	Certificate_name      bazel.StringAttribute // used when the certificate prop is a string
 	Min_sdk_version       *string
 	Updatable             bazel.BoolAttribute
 	Installable           bazel.BoolAttribute
@@ -3409,7 +3410,8 @@
 		keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key))
 	}
 
-	certificateName, certificate := java.ParseCertificateToAttribute(ctx, a.overridableProperties.Certificate)
+	// Certificate
+	certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableProperties.Certificate)
 
 	nativeSharedLibs := &convertedNativeSharedLibs{
 		Native_shared_libs_32: bazel.LabelListAttribute{},
diff --git a/apex/key.go b/apex/key.go
index 2b09f1d..0a7e80f 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -201,10 +201,10 @@
 
 type bazelApexKeyAttributes struct {
 	Public_key      bazel.LabelAttribute
-	Public_key_name bazel.LabelAttribute
+	Public_key_name bazel.StringAttribute
 
 	Private_key      bazel.LabelAttribute
-	Private_key_name bazel.LabelAttribute
+	Private_key_name bazel.StringAttribute
 }
 
 // ConvertWithBp2build performs conversion apexKey for bp2build
@@ -213,27 +213,11 @@
 }
 
 func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
-	var privateKeyLabelAttribute bazel.LabelAttribute
-	var privateKeyNameAttribute bazel.LabelAttribute
-	if module.properties.Private_key != nil {
-		m := String(module.properties.Private_key)
-		if android.SrcIsModule(m) == "" {
-			privateKeyNameAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
-		} else {
-			privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.Private_key))
-		}
-	}
+	privateKeyLabelAttribute, privateKeyNameAttribute :=
+		android.BazelStringOrLabelFromProp(ctx, module.properties.Private_key)
 
-	var publicKeyLabelAttribute bazel.LabelAttribute
-	var publicKeyNameAttribute bazel.LabelAttribute
-	if module.properties.Public_key != nil {
-		m := String(module.properties.Public_key)
-		if android.SrcIsModule(m) == "" {
-			publicKeyNameAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
-		} else {
-			publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.Public_key))
-		}
-	}
+	publicKeyLabelAttribute, publicKeyNameAttribute :=
+		android.BazelStringOrLabelFromProp(ctx, module.properties.Public_key)
 
 	attrs := &bazelApexKeyAttributes{
 		Private_key:      privateKeyLabelAttribute,
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index e112be3..2b35521 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -27,6 +27,7 @@
 }
 
 func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 }
 
 func TestMinimalAndroidApp(t *testing.T) {
@@ -76,6 +77,7 @@
         manifest: "manifest/AndroidManifest.xml",
         static_libs: ["static_lib_dep"],
         java_version: "7",
+        certificate: "foocert",
 }
 `,
 		ExpectedBazelTargets: []string{
@@ -86,9 +88,10 @@
         "resa/res.png",
         "resb/res.png",
     ]`,
-				"custom_package": `"com.google"`,
-				"deps":           `[":static_lib_dep"]`,
-				"javacopts":      `["-source 1.7 -target 1.7"]`,
+				"custom_package":   `"com.google"`,
+				"deps":             `[":static_lib_dep"]`,
+				"javacopts":        `["-source 1.7 -target 1.7"]`,
+				"certificate_name": `"foocert"`,
 			}),
 		}})
 }
@@ -130,3 +133,70 @@
 			}),
 		}})
 }
+
+func TestAndroidAppCertIsModule(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app - cert is module",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: simpleModuleDoNotConvertBp2build("filegroup", "foocert") + `
+android_app {
+        name: "TestApp",
+        certificate: ":foocert",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
+				"certificate":    `":foocert"`,
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `[]`,
+			}),
+		}})
+}
+
+func TestAndroidAppCertIsSrcFile(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app - cert is src file",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem: map[string]string{
+			"foocert": "",
+		},
+		Blueprint: `
+android_app {
+        name: "TestApp",
+        certificate: "foocert",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
+				"certificate":    `"foocert"`,
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `[]`,
+			}),
+		}})
+}
+
+func TestAndroidAppCertIsNotSrcOrModule(t *testing.T) {
+	runAndroidAppTestCase(t, Bp2buildTestCase{
+		Description:                "Android app - cert is not src or module",
+		ModuleTypeUnderTest:        "android_app",
+		ModuleTypeUnderTestFactory: java.AndroidAppFactory,
+		Filesystem:                 map[string]string{
+			// deliberate empty
+		},
+		Blueprint: `
+android_app {
+        name: "TestApp",
+        certificate: "foocert",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("android_binary", "TestApp", AttrNameToString{
+				"certificate_name": `"foocert"`,
+				"manifest":         `"AndroidManifest.xml"`,
+				"resource_files":   `[]`,
+			}),
+		}})
+}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 79b1d89..f9a68c9 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -30,12 +30,37 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 }
 
-func TestApexKeySimple_KeysAreSrcFiles(t *testing.T) {
+func TestApexKeySimple_KeysAreSrcFilesInSameDir(t *testing.T) {
 	runApexKeyTestCase(t, Bp2buildTestCase{
-		Description:                "apex key - keys are src files, use key_name attributes",
+		Description:                "apex key - keys are src files, use key attributes",
 		ModuleTypeUnderTest:        "apex_key",
 		ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
-		Filesystem:                 map[string]string{},
+		Filesystem: map[string]string{
+			"com.android.apogee.avbpubkey": "",
+			"com.android.apogee.pem":       "",
+		},
+		Blueprint: `
+apex_key {
+        name: "com.android.apogee.key",
+        public_key: "com.android.apogee.avbpubkey",
+        private_key: "com.android.apogee.pem",
+}
+`,
+		ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{
+			"private_key": `"com.android.apogee.pem"`,
+			"public_key":  `"com.android.apogee.avbpubkey"`,
+		}),
+		}})
+}
+
+func TestApexKeySimple_KeysAreSrcFilesNotInDir(t *testing.T) {
+	runApexKeyTestCase(t, Bp2buildTestCase{
+		Description:                "apex key - keys are not src or module, use key_name attributes",
+		ModuleTypeUnderTest:        "apex_key",
+		ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
+		Filesystem:                 map[string]string{
+			// deliberately left empty
+		},
 		Blueprint: `
 apex_key {
         name: "com.android.apogee.key",
diff --git a/java/app.go b/java/app.go
index bbd9d2d..2a51e10 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1486,22 +1486,8 @@
 	*bazelAapt
 	Deps             bazel.LabelListAttribute
 	Custom_package   *string
-	Certificate      *bazel.Label
-	Certificate_name *string
-}
-
-// ParseCertificateToAttribute splits the certificate prop into a certificate
-// label attribute or a certificate_name string attribute.
-func ParseCertificateToAttribute(ctx android.TopDownMutatorContext, certificate *string) (*string, *bazel.Label) {
-	var certificateLabel *bazel.Label
-	certificateName := proptools.StringDefault(certificate, "")
-	certModule := android.SrcIsModule(certificateName)
-	if certModule != "" {
-		c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
-		certificateLabel = &c
-		certificate = nil
-	}
-	return certificate, certificateLabel
+	Certificate      bazel.LabelAttribute
+	Certificate_name bazel.StringAttribute
 }
 
 // ConvertWithBp2build is used to convert android_app to Bazel.
@@ -1513,7 +1499,8 @@
 
 	aapt := a.convertAaptAttrsWithBp2Build(ctx)
 
-	certificateName, certificate := ParseCertificateToAttribute(ctx, a.overridableAppProperties.Certificate)
+	certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate)
+
 	attrs := &bazelAndroidAppAttributes{
 		commonAttrs,
 		aapt,