Rust: Vendor support for Rust static libraries.

We don't have Rust VNDK support yet, but static linkage can be
supported in the interim. This adds support for making rust_ffi_static
libraries available to CC vendor modules.

Since rust_ffi_static modules will link against rlibs, we allow rlib
linkage into vendor as well, but only for the variants which use the
rlib libstd.

Bug: 172525289
Test: New Soong tests pass
Test: Example vendor cc_binary links against rust_ffi_static module.
Change-Id: Idf3aeb51e32293866f1ad965e329aa6b9e0bf2ef
diff --git a/rust/Android.bp b/rust/Android.bp
index 8618207..df731db 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -15,6 +15,7 @@
         "clippy.go",
         "compiler.go",
         "coverage.go",
+        "image.go",
         "library.go",
         "prebuilt.go",
         "proc_macro.go",
@@ -33,6 +34,7 @@
         "clippy_test.go",
         "compiler_test.go",
         "coverage_test.go",
+        "image_test.go",
         "library_test.go",
         "project_json_test.go",
         "protobuf_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index c181d67..e9da6fa 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -58,6 +58,7 @@
 				entries.AddStrings("LOCAL_PROC_MACRO_LIBRARIES", mod.Properties.AndroidMkProcMacroLibs...)
 				entries.AddStrings("LOCAL_SHARED_LIBRARIES", mod.Properties.AndroidMkSharedLibs...)
 				entries.AddStrings("LOCAL_STATIC_LIBRARIES", mod.Properties.AndroidMkStaticLibs...)
+				entries.AddStrings("LOCAL_SOONG_LINK_TYPE", mod.makeLinkType)
 			},
 		},
 	}
diff --git a/rust/image.go b/rust/image.go
new file mode 100644
index 0000000..4951d2b
--- /dev/null
+++ b/rust/image.go
@@ -0,0 +1,153 @@
+// Copyright 2020 The Android Open Source Project
+//
+// 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 rust
+
+import (
+	"strings"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+var _ android.ImageInterface = (*Module)(nil)
+
+func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return mod.Properties.CoreVariantNeeded
+}
+
+func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
+	return mod.InRamdisk()
+}
+
+func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
+	return mod.InRecovery()
+}
+
+func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
+	return mod.Properties.ExtraVariants
+}
+
+func (ctx *moduleContext) ProductSpecific() bool {
+	return false
+}
+
+func (mod *Module) InRecovery() bool {
+	// TODO(b/165791368)
+	return false
+}
+
+func (mod *Module) OnlyInRamdisk() bool {
+	// TODO(b/165791368)
+	return false
+}
+
+func (mod *Module) OnlyInRecovery() bool {
+	// TODO(b/165791368)
+	return false
+}
+
+func (mod *Module) OnlyInVendorRamdisk() bool {
+	return false
+}
+
+// Returns true when this module is configured to have core and vendor variants.
+func (mod *Module) HasVendorVariant() bool {
+	return mod.IsVndk() || Bool(mod.VendorProperties.Vendor_available)
+}
+
+func (c *Module) VendorAvailable() bool {
+	return Bool(c.VendorProperties.Vendor_available)
+}
+
+func (c *Module) InProduct() bool {
+	return false
+}
+
+func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+	m := module.(*Module)
+	if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
+		m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
+
+		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
+		// Hide other vendor variants to avoid collision.
+		vndkVersion := ctx.DeviceConfig().VndkVersion()
+		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
+			m.Properties.HideFromMake = true
+			m.SkipInstall()
+		}
+	}
+}
+
+func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+
+	// Rust does not support installing to the product image yet.
+	if mod.VendorProperties.Product_available != nil {
+		mctx.PropertyErrorf("product_available",
+			"Rust modules do not yet support being available to the product image")
+	} else if mctx.ProductSpecific() {
+		mctx.PropertyErrorf("product_specific",
+			"Rust modules do not yet support installing to the product image.")
+	} else if mod.VendorProperties.Double_loadable != nil {
+		mctx.PropertyErrorf("double_loadable",
+			"Rust modules do not yet support double loading")
+	}
+
+	coreVariantNeeded := true
+	var vendorVariants []string
+
+	if mod.VendorProperties.Vendor_available != nil {
+		if vendorSpecific {
+			mctx.PropertyErrorf("vendor_available",
+				"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+		}
+
+		if lib, ok := mod.compiler.(libraryInterface); ok {
+			// Explicitly disallow rust_ffi variants which produce shared libraries from setting vendor_available.
+			// Vendor variants do not produce an error for dylibs, rlibs with dylib-std linkage are disabled in the respective library
+			// mutators until support is added.
+			//
+			// We can't check shared() here because image mutator is called before the library mutator, so we need to
+			// check buildShared()
+			if lib.buildShared() {
+				mctx.PropertyErrorf("vendor_available",
+					"vendor_available can only be set for rust_ffi_static modules.")
+			} else if Bool(mod.VendorProperties.Vendor_available) == true {
+				vendorVariants = append(vendorVariants, platformVndkVersion)
+			}
+		}
+	}
+
+	if vendorSpecific {
+		if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && !lib.static()) {
+			mctx.ModuleErrorf("Rust vendor specific modules are currently only supported for rust_ffi_static modules.")
+		} else {
+			coreVariantNeeded = false
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
+	}
+
+	mod.Properties.CoreVariantNeeded = coreVariantNeeded
+	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
+		mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, cc.VendorVariationPrefix+variant)
+	}
+
+}
diff --git a/rust/image_test.go b/rust/image_test.go
new file mode 100644
index 0000000..025b0fd
--- /dev/null
+++ b/rust/image_test.go
@@ -0,0 +1,73 @@
+// Copyright 2020 The Android Open Source Project
+//
+// 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 rust
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+// Test that cc_binaries can link against rust_ffi_static libraries.
+func TestVendorLinkage(t *testing.T) {
+	ctx := testRust(t, `
+			cc_binary {
+				name: "fizz_vendor",
+				static_libs: ["libfoo_vendor"],
+				soc_specific: true,
+			}
+			rust_ffi_static {
+				name: "libfoo_vendor",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_available: true,
+			}
+		`)
+
+	vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_arm64_armv8-a").Module().(*cc.Module)
+
+	if !android.InList("libfoo_vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should have a dependency on libfoo_vendor")
+	}
+}
+
+// Test that shared libraries cannot be made vendor available until proper support is added.
+func TestForbiddenVendorLinkage(t *testing.T) {
+	testRustError(t, "vendor_available can only be set for rust_ffi_static modules", `
+		rust_ffi_shared {
+			name: "libfoo_vendor",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+			vendor_available: true,
+		}
+	`)
+	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
+		rust_ffi {
+			name: "libfoo_vendor",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+			vendor: true,
+		}
+	`)
+	testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
+		rust_library {
+			name: "libfoo_vendor",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+			vendor: true,
+		}
+	`)
+}
diff --git a/rust/library.go b/rust/library.go
index 971588d..48b3ad4 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -601,6 +601,11 @@
 			v.(*Module).compiler.(libraryInterface).setRlib()
 		case dylibVariation:
 			v.(*Module).compiler.(libraryInterface).setDylib()
+			if v.(*Module).ModuleBase.ImageVariation().Variation != android.CoreVariation {
+				// TODO(b/165791368)
+				// Disable dylib non-core variations until we support these.
+				v.(*Module).Disable()
+			}
 		case "source":
 			v.(*Module).compiler.(libraryInterface).setSource()
 			// The source variant does not produce any library.
@@ -637,6 +642,12 @@
 				dylib := modules[1].(*Module)
 				rlib.compiler.(libraryInterface).setRlibStd()
 				dylib.compiler.(libraryInterface).setDylibStd()
+				if dylib.ModuleBase.ImageVariation().Variation != android.CoreVariation {
+					// TODO(b/165791368)
+					// Disable rlibs that link against dylib-std on non-core variations until non-core dylib
+					// variants are properly supported.
+					dylib.Disable()
+				}
 				rlib.Properties.SubName += RlibStdlibSuffix
 				dylib.Properties.SubName += DylibStdlibSuffix
 			}
diff --git a/rust/rust.go b/rust/rust.go
index 4094094..3bb4861 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -65,7 +65,13 @@
 	AndroidMkSharedLibs    []string
 	AndroidMkStaticLibs    []string
 
-	SubName string `blueprint:"mutated"`
+	ImageVariationPrefix string `blueprint:"mutated"`
+	VndkVersion          string `blueprint:"mutated"`
+	SubName              string `blueprint:"mutated"`
+
+	// Set by imageMutator
+	CoreVariantNeeded bool     `blueprint:"mutated"`
+	ExtraVariants     []string `blueprint:"mutated"`
 
 	PreventInstall bool
 	HideFromMake   bool
@@ -76,11 +82,15 @@
 	android.DefaultableModuleBase
 	android.ApexModuleBase
 
+	VendorProperties cc.VendorProperties
+
 	Properties BaseProperties
 
 	hod      android.HostOrDeviceSupported
 	multilib android.Multilib
 
+	makeLinkType string
+
 	compiler         compiler
 	coverage         *coverage
 	clippy           *clippy
@@ -109,33 +119,6 @@
 	}
 }
 
-var _ android.ImageInterface = (*Module)(nil)
-
-func (mod *Module) ImageMutatorBegin(ctx android.BaseModuleContext) {}
-
-func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return true
-}
-
-func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
-	return mod.InRamdisk()
-}
-
-func (mod *Module) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool {
-	return mod.InVendorRamdisk()
-}
-
-func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
-	return mod.InRecovery()
-}
-
-func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
-	return nil
-}
-
-func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
-}
-
 func (mod *Module) SelectedStl() string {
 	return ""
 }
@@ -176,24 +159,18 @@
 	panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))
 }
 
-func (mod *Module) OnlyInRamdisk() bool {
-	return false
-}
-
-func (mod *Module) OnlyInVendorRamdisk() bool {
-	return false
-}
-
-func (mod *Module) OnlyInRecovery() bool {
-	return false
-}
-
 func (mod *Module) UseSdk() bool {
 	return false
 }
 
+// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
+// "product" and "vendor" variant modules return true for this function.
+// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
+// "soc_specific: true" and more vendor installed modules are included here.
+// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or
+// "product_specific: true" modules are included here.
 func (mod *Module) UseVndk() bool {
-	return false
+	return mod.Properties.VndkVersion != ""
 }
 
 func (mod *Module) MustUseVendorVariant() bool {
@@ -201,10 +178,7 @@
 }
 
 func (mod *Module) IsVndk() bool {
-	return false
-}
-
-func (mod *Module) HasVendorVariant() bool {
+	// TODO(b/165791368)
 	return false
 }
 
@@ -216,10 +190,6 @@
 	return false
 }
 
-func (mod *Module) InProduct() bool {
-	return false
-}
-
 func (mod *Module) SdkVersion() string {
 	return ""
 }
@@ -388,6 +358,7 @@
 	module.AddProperties(props...)
 	module.AddProperties(
 		&BaseProperties{},
+		&cc.VendorProperties{},
 		&BindgenProperties{},
 		&BaseCompilerProperties{},
 		&BinaryCompilerProperties{},
@@ -484,11 +455,6 @@
 	return mod.outputFile
 }
 
-func (mod *Module) InRecovery() bool {
-	// For now, Rust has no notion of the recovery image
-	return false
-}
-
 func (mod *Module) CoverageFiles() android.Paths {
 	if mod.compiler != nil {
 		if !mod.compiler.nativeCoverage() {
@@ -508,6 +474,7 @@
 
 func (mod *Module) Init() android.Module {
 	mod.AddProperties(&mod.Properties)
+	mod.AddProperties(&mod.VendorProperties)
 
 	if mod.compiler != nil {
 		mod.AddProperties(mod.compiler.compilerProps()...)
@@ -627,6 +594,12 @@
 	}
 
 	toolchain := mod.toolchain(ctx)
+	mod.makeLinkType = cc.GetMakeLinkType(actx, mod)
+
+	// Differentiate static libraries that are vendor available
+	if mod.UseVndk() {
+		mod.Properties.SubName += ".vendor"
+	}
 
 	if !toolchain.Supported() {
 		// This toolchain's unsupported, there's nothing to do for this mod.
@@ -968,10 +941,6 @@
 
 	deps := mod.deps(ctx)
 	var commonDepVariations []blueprint.Variation
-	if !mod.Host() {
-		commonDepVariations = append(commonDepVariations,
-			blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
-	}
 
 	stdLinkage := "dylib-std"
 	if mod.compiler.stdLinkage(ctx) == RlibLinkage {
diff --git a/rust/testing.go b/rust/testing.go
index a8496d9..ca766a1 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -92,6 +92,7 @@
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
 			host_supported: true,
+			vendor_available: true,
                         native_coverage: false,
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
@@ -102,6 +103,7 @@
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
 			host_supported: true,
+			vendor_available: true,
                         native_coverage: false,
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],