Merge "Reland: Add jni_libs property to java tests"
diff --git a/android/bazel.go b/android/bazel.go
index 260f1e5..1f7f7e6 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -134,7 +134,7 @@
 		"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
 	}
 
-	// Per-module denylist to always opt modules out.
+	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
 	bp2buildModuleDoNotConvertList = []string{
 		"libBionicBenchmarksUtils",      // ruperts@, cc_library_static, 'map' file not found
 		"libbionic_spawn_benchmark",     // ruperts@, cc_library_static, depends on //system/libbase
@@ -170,6 +170,8 @@
 		"libc_dns",                      // ruperts@, cc_library_static, 'android/log.h' file not found
 		"libc_static_dispatch",          // eakammer@, cc_library_static, 'private/bionic_asm.h' file not found
 		"libc_dynamic_dispatch",         // eakammer@, cc_library_static, 'private/bionic_ifuncs.h' file not found
+		"note_memtag_heap_async",        // jingwen@, cc_library_static, 'private/bionic_asm.h' file not found (arm64)
+		"note_memtag_heap_sync",         // jingwen@, cc_library_static, 'private/bionic_asm.h' file not found (arm64)
 
 		// List of all full_cc_libraries in //bionic, with their immediate failures
 		"libc",              // jingwen@, cc_library, depends on //external/gwp_asan
@@ -179,21 +181,39 @@
 		"libm",              // jingwen@, cc_library, fatal error: 'freebsd-compat.h' file not found
 		"libseccomp_policy", // jingwen@, cc_library, fatal error: 'seccomp_policy.h' file not found
 		"libstdc++",         // jingwen@, cc_library, depends on //external/gwp_asan
+	}
 
-		// For mixed builds specifically
-		"note_memtag_heap_async", // jingwen@, cc_library_static, OK for bp2build but features.h includes not found for mixed builds (b/185079815)
-		"note_memtag_heap_sync",  // jingwen@, cc_library_static, OK for bp2build but features.h includes not found for mixed builds (b/185079815)
-		"libc_gdtoa",             // ruperts@, cc_library_static, OK for bp2build but undefined symbol: __strtorQ for mixed builds
+	// Per-module denylist to opt modules out of mixed builds. Such modules will
+	// still be generated via bp2build.
+	mixedBuildsDisabledList = []string{
+		"libc_gdtoa", // ruperts@, cc_library_static, OK for bp2build but undefined symbol: __strtorQ for mixed builds
 	}
 
 	// Used for quicker lookups
 	bp2buildModuleDoNotConvert = map[string]bool{}
+	mixedBuildsDisabled        = map[string]bool{}
 )
 
 func init() {
 	for _, moduleName := range bp2buildModuleDoNotConvertList {
 		bp2buildModuleDoNotConvert[moduleName] = true
 	}
+
+	for _, moduleName := range mixedBuildsDisabledList {
+		mixedBuildsDisabled[moduleName] = true
+	}
+}
+
+// MixedBuildsEnabled checks that a module is ready to be replaced by a
+// converted or handcrafted Bazel target.
+func (b *BazelModuleBase) MixedBuildsEnabled(ctx BazelConversionPathContext) bool {
+	if !ctx.Config().BazelContext.BazelEnabled() {
+		return false
+	}
+	if len(b.GetBazelLabel(ctx, ctx.Module())) == 0 {
+		return false
+	}
+	return !mixedBuildsDisabled[ctx.Module().Name()]
 }
 
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
diff --git a/cc/cc.go b/cc/cc.go
index 7f69d56..9eebbae 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1641,7 +1641,7 @@
 func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
 	bazelModuleLabel := c.GetBazelLabel(actx, c)
 	bazelActionsUsed := false
-	if c.bazelHandler != nil && actx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
+	if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil {
 		bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel)
 	}
 	return bazelActionsUsed
diff --git a/genrule/genrule.go b/genrule/genrule.go
index e6a5ab9..3a9aecc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -543,7 +543,7 @@
 
 	bazelModuleLabel := g.GetBazelLabel(ctx, g)
 	bazelActionsUsed := false
-	if ctx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 {
+	if g.MixedBuildsEnabled(ctx) {
 		bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
 	}
 	if !bazelActionsUsed {
diff --git a/python/builder.go b/python/builder.go
index dc2d1f1..7d7239c 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -45,7 +45,7 @@
 	hostPar = pctx.AndroidStaticRule("hostPar",
 		blueprint.RuleParams{
 			Command: `sed -e 's/%interpreter%/$interp/g' -e 's/%main%/$main/g' $template > $stub && ` +
-				`echo "#!/usr/bin/env python" >${out}.prefix &&` +
+				`echo "#!/usr/bin/env $interp" >${out}.prefix &&` +
 				`$mergeParCmd -p --prefix ${out}.prefix -pm $stub $out $srcsZips && ` +
 				`chmod +x $out && (rm -f $stub; rm -f ${out}.prefix)`,
 			CommandDeps: []string{"$mergeParCmd"},
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index a48a86f..138404b 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env '%interpreter%'
 
 import os
 import re
@@ -82,7 +82,7 @@
 
     sys.stdout.flush()
     retCode = subprocess.call(args)
-    exit(retCode)
+    sys.exit(retCode)
   except:
     raise
   finally:
diff --git a/rust/Android.bp b/rust/Android.bp
index a6c4e07..f45404f 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -21,6 +21,7 @@
         "clippy.go",
         "compiler.go",
         "coverage.go",
+        "doc.go",
         "fuzz.go",
         "image.go",
         "library.go",
diff --git a/rust/binary.go b/rust/binary.go
index dfe8744..ffc0413 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -122,7 +122,7 @@
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
 
-	TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile)
 
 	if binary.stripper.NeedsStrip(ctx) {
 		strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index bcc26b8..8edb7c9 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r412851"
+	bindgenClangVersion = "clang-r416183b"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/builder.go b/rust/builder.go
index 208b734..1fcce38 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -44,6 +44,16 @@
 		},
 		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
 
+	_       = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
+	rustdoc = pctx.AndroidStaticRule("rustdoc",
+		blueprint.RuleParams{
+			Command: "rm -rf $outDir && " +
+				"$envVars $rustdocCmd $rustdocFlags $in -o $outDir && " +
+				"touch $out",
+			CommandDeps: []string{"$rustdocCmd"},
+		},
+		"rustdocFlags", "outDir", "envVars")
+
 	_            = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
 	clippyDriver = pctx.AndroidStaticRule("clippy",
 		blueprint.RuleParams{
@@ -85,37 +95,37 @@
 }
 
 func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, linkDirs []string) buildOutput {
+	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
 }
 
 func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", linkDirs)
+	outputFile android.WritablePath) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib")
 }
 
 func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, linkDirs []string) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", linkDirs)
+	outputFile android.WritablePath) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
 }
 
 func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, linkDirs []string) buildOutput {
+	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
 }
 
 func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, linkDirs []string) buildOutput {
+	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
 }
 
 func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
-	flags Flags, outputFile android.WritablePath, linkDirs []string) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", linkDirs)
+	flags Flags, outputFile android.WritablePath) buildOutput {
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro")
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -126,26 +136,69 @@
 	return paths
 }
 
+func makeLibFlags(deps PathDeps) []string {
+	var libFlags []string
+
+	// Collect library/crate flags
+	for _, lib := range deps.RLibs {
+		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+	}
+	for _, lib := range deps.DyLibs {
+		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+	}
+	for _, proc_macro := range deps.ProcMacros {
+		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
+	}
+
+	for _, path := range deps.linkDirs {
+		libFlags = append(libFlags, "-L "+path)
+	}
+
+	return libFlags
+}
+
+func rustEnvVars(ctx ModuleContext, deps PathDeps) []string {
+	var envVars []string
+
+	// libstd requires a specific environment variable to be set. This is
+	// not officially documented and may be removed in the future. See
+	// https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866.
+	if ctx.RustModule().CrateName() == "std" {
+		envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
+	}
+
+	if len(deps.SrcDeps) > 0 {
+		moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
+		// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
+		// assumes that paths are relative to the source file.
+		var outDirPrefix string
+		if !filepath.IsAbs(moduleGenDir.String()) {
+			// If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
+			outDirPrefix = "$$PWD/"
+		} else {
+			// If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything.
+			outDirPrefix = ""
+		}
+		envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
+	}
+
+	return envVars
+}
+
 func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, crate_type string, linkDirs []string) buildOutput {
+	outputFile android.WritablePath, crate_type string) buildOutput {
 
 	var inputs android.Paths
 	var implicits android.Paths
-	var envVars []string
 	var output buildOutput
-	var libFlags, rustcFlags, linkFlags []string
+	var rustcFlags, linkFlags []string
 	var implicitOutputs android.WritablePaths
 
 	output.outputFile = outputFile
 	crateName := ctx.RustModule().CrateName()
 	targetTriple := ctx.toolchain().RustTriple()
 
-	// libstd requires a specific environment variable to be set. This is
-	// not officially documented and may be removed in the future. See
-	// https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866.
-	if crateName == "std" {
-		envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
-	}
+	envVars := rustEnvVars(ctx, deps)
 
 	inputs = append(inputs, main)
 
@@ -168,20 +221,7 @@
 	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
 	linkFlags = append(linkFlags, flags.LinkFlags...)
 
-	// Collect library/crate flags
-	for _, lib := range deps.RLibs {
-		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
-	}
-	for _, lib := range deps.DyLibs {
-		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
-	}
-	for _, proc_macro := range deps.ProcMacros {
-		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
-	}
-
-	for _, path := range linkDirs {
-		libFlags = append(libFlags, "-L "+path)
-	}
+	libFlags := makeLibFlags(deps)
 
 	// Collect dependencies
 	implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
@@ -217,18 +257,6 @@
 			},
 		})
 		implicits = append(implicits, outputs.Paths()...)
-
-		// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
-		// assumes that paths are relative to the source file.
-		var outDirPrefix string
-		if !filepath.IsAbs(moduleGenDir.String()) {
-			// If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
-			outDirPrefix = "$$PWD/"
-		} else {
-			// If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything.
-			outDirPrefix = ""
-		}
-		envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
 	}
 
 	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
@@ -272,3 +300,41 @@
 
 	return output
 }
+
+func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps,
+	flags Flags) android.ModuleOutPath {
+
+	rustdocFlags := append([]string{}, flags.RustdocFlags...)
+	rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null")
+
+	targetTriple := ctx.toolchain().RustTriple()
+
+	// Collect rustc flags
+	if targetTriple != "" {
+		rustdocFlags = append(rustdocFlags, "--target="+targetTriple)
+	}
+
+	crateName := ctx.RustModule().CrateName()
+	if crateName != "" {
+		rustdocFlags = append(rustdocFlags, "--crate-name "+crateName)
+	}
+
+	rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...)
+	docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp")
+	docDir := android.PathForOutput(ctx, "rustdoc", ctx.ModuleName())
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        rustdoc,
+		Description: "rustdoc " + main.Rel(),
+		Output:      docTimestampFile,
+		Input:       main,
+		Implicit:    ctx.RustModule().unstrippedOutputFile.Path(),
+		Args: map[string]string{
+			"rustdocFlags": strings.Join(rustdocFlags, " "),
+			"outDir":       docDir.String(),
+			"envVars":      strings.Join(rustEnvVars(ctx, deps), " "),
+		},
+	})
+
+	return docTimestampFile
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index bc034d7..bfc23b2 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -239,7 +239,10 @@
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
 	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...)
+	flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...)
+	flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...)
 	flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
+	flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition())
 	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
@@ -272,6 +275,12 @@
 	panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
 }
 
+func (compiler *baseCompiler) rustdoc(ctx ModuleContext, flags Flags,
+	deps PathDeps) android.OptionalPath {
+
+	return android.OptionalPath{}
+}
+
 func (compiler *baseCompiler) initialize(ctx ModuleContext) {
 	compiler.cargoOutDir = android.PathForModuleOut(ctx, genSubDir)
 }
diff --git a/rust/doc.go b/rust/doc.go
new file mode 100644
index 0000000..e7f1371
--- /dev/null
+++ b/rust/doc.go
@@ -0,0 +1,43 @@
+// Copyright 2021 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 (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("rustdoc", RustdocSingleton)
+}
+
+func RustdocSingleton() android.Singleton {
+	return &rustdocSingleton{}
+}
+
+type rustdocSingleton struct{}
+
+func (n *rustdocSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	ctx.VisitAllModules(func(module android.Module) {
+		if !module.Enabled() {
+			return
+		}
+
+		if m, ok := module.(*Module); ok {
+			if m.docTimestampFile.Valid() {
+				ctx.Phony("rustdoc", m.docTimestampFile.Path())
+			}
+		}
+	})
+}
diff --git a/rust/library.go b/rust/library.go
index 052fb3a..1bdf83a 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -432,14 +432,10 @@
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
 	var outputFile android.ModuleOutPath
 	var fileName string
-	var srcPath android.Path
+	srcPath := library.srcPath(ctx, deps)
 
 	if library.sourceProvider != nil {
-		// Assume the first source from the source provider is the library entry point.
-		srcPath = library.sourceProvider.Srcs()[0]
 		deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
-	} else {
-		srcPath, _ = srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
 	}
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
@@ -457,22 +453,22 @@
 		fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile)
 	} else if library.dylib() {
 		fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile)
 	} else if library.static() {
 		fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile)
 	} else if library.shared() {
 		fileName = library.sharedLibFilename(ctx)
 		outputFile = android.PathForModuleOut(ctx, fileName)
 
-		TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+		TransformSrctoShared(ctx, srcPath, deps, flags, outputFile)
 	}
 
 	if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
@@ -514,6 +510,31 @@
 	return outputFile
 }
 
+func (library *libraryDecorator) srcPath(ctx ModuleContext, deps PathDeps) android.Path {
+	if library.sourceProvider != nil {
+		// Assume the first source from the source provider is the library entry point.
+		return library.sourceProvider.Srcs()[0]
+	} else {
+		path, _ := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
+		return path
+	}
+}
+
+func (library *libraryDecorator) rustdoc(ctx ModuleContext, flags Flags,
+	deps PathDeps) android.OptionalPath {
+	// rustdoc has builtin support for documenting config specific information
+	// regardless of the actual config it was given
+	// (https://doc.rust-lang.org/rustdoc/advanced-features.html#cfgdoc-documenting-platform-specific-or-feature-specific-information),
+	// so we generate the rustdoc for only the primary module so that we have a
+	// single set of docs to refer to.
+	if ctx.Module() != ctx.PrimaryModule() {
+		return android.OptionalPath{}
+	}
+
+	return android.OptionalPathForPath(Rustdoc(ctx, library.srcPath(ctx, deps),
+		deps, flags))
+}
+
 func (library *libraryDecorator) getStem(ctx ModuleContext) string {
 	stem := library.baseCompiler.getStemWithoutSuffix(ctx)
 	validateLibraryStem(ctx, stem, library.crateName())
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 94fe1e5..49f3c0f 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -103,6 +103,12 @@
 	return srcPath
 }
 
+func (prebuilt *prebuiltLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags,
+	deps PathDeps) android.OptionalPath {
+
+	return android.OptionalPath{}
+}
+
 func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
 	return deps
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 115045a..4eead32 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -68,7 +68,7 @@
 	outputFile := android.PathForModuleOut(ctx, fileName)
 
 	srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
-	TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+	TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
 	return outputFile
 }
 
diff --git a/rust/rust.go b/rust/rust.go
index 9738b46..78a793d 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -58,6 +58,7 @@
 	RustFlags       []string // Flags that apply to rust
 	LinkFlags       []string // Flags that apply to linker
 	ClippyFlags     []string // Flags that apply to clippy-driver, during the linting
+	RustdocFlags    []string // Flags that apply to rustdoc
 	Toolchain       config.Toolchain
 	Coverage        bool
 	Clippy          bool
@@ -124,6 +125,7 @@
 	// as a library. The stripped output which is used for installation can be found via
 	// compiler.strippedOutputFile if it exists.
 	unstrippedOutputFile android.OptionalPath
+	docTimestampFile     android.OptionalPath
 
 	hideApexVariantFromMake bool
 }
@@ -355,10 +357,12 @@
 	compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	crateName() string
+	rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
 
 	// Output directory in which source-generated code from dependencies is
 	// copied. This is equivalent to Cargo's OUT_DIR variable.
 	CargoOutDir() android.OptionalPath
+
 	inData() bool
 	install(ctx ModuleContext)
 	relativeInstallPath() string
@@ -755,6 +759,8 @@
 		mod.unstrippedOutputFile = android.OptionalPathForPath(unstrippedOutputFile)
 		bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), mod.unstrippedOutputFile)
 
+		mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
+
 		apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 		if mod.installable(apexInfo) {
 			mod.compiler.install(ctx)