blob: 5c8fc6aa0d151514d09b597429cb3788f1a9ab2a [file] [log] [blame]
// Copyright 2019 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 (
"slices"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/depset"
"android/soong/android"
"android/soong/cc"
"android/soong/remoteexec"
"android/soong/rust/config"
)
var (
_ = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
rustc, rustcRbe = pctx.RemoteStaticRules("rustc",
blueprint.RuleParams{
Command: "$relPwd $reTemplate/usr/bin/env $envVars ${RustcWrapper} ${rustcCmd} " +
"-C linker=${RustcLinkerCmd} -C link-args=\"--android-clang-bin=${config.ClangCmd} ${linkerScriptFlags}\" " +
"-C link-args=@${out}.clang.rsp " +
"--emit ${emitType} -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags",
CommandDeps: []string{"$rustcCmd", "${RustcLinkerCmd}", "${config.ClangCmd}", "${RustcWrapper}"},
Rspfile: "${out}.clang.rsp",
RspfileContent: "${crtBegin} ${earlyLinkFlags} ${linkFlags} ${crtEnd}",
Deps: blueprint.DepsGCC,
Depfile: "$out.d",
}, &remoteexec.REParams{
// Until there's a "rust" tool, use clang. This interprets "-L" flags
// to help identify potential build dependencies.
Labels: map[string]string{"type": "link", "tool": "clang"},
Inputs: []string{"${out}.clang.rsp"},
RSPFiles: []string{"$rbeRspFile"},
OutputFiles: []string{"${out}.d", "${out}.d.raw", "${out}"},
ExecStrategy: "${config.RERustExecStrategy}",
ToolchainInputs: []string{
"${rustcCmd}",
"${RustcLinkerCmd}",
"${config.ClangCmd}",
"${config.LlvmDlltool}",
"${RustcWrapper}",
},
Platform: map[string]string{remoteexec.PoolKey: "${config.RERustPool}"},
},
[]string{"rustcFlags", "linkerScriptFlags", "earlyLinkFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "emitType", "envVars"},
[]string{"rbeRspFile"})
_ = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
rustdoc = pctx.AndroidStaticRule("rustdoc",
blueprint.RuleParams{
Command: "$envVars ${RustcWrapper} $rustdocCmd $rustdocFlags $in -o $outDir && " +
"touch $out",
CommandDeps: []string{"$rustdocCmd", "${RustcWrapper}"},
},
"rustdocFlags", "outDir", "envVars")
_ = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
generateClippyRule = func(ruleName string, extraFlags string) blueprint.Rule {
return pctx.AndroidStaticRule(ruleName,
blueprint.RuleParams{
Command: "$envVars ${RustcWrapper} $clippyCmd " +
// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
// Use the metadata output as it has the smallest footprint.
"--emit metadata -o $out --emit dep-info=$out.d.raw $in ${libFlags} " +
"$rustcFlags $clippyFlags " + extraFlags,
CommandDeps: []string{"$clippyCmd", "${RustcWrapper}"},
Deps: blueprint.DepsGCC,
Depfile: "$out.d",
},
"rustcFlags", "libFlags", "clippyFlags", "envVars")
}
clippyDriver = generateClippyRule("clippy", "")
// Same as clippyDriver, but outputting any errors or warnings in a JSON schema to a
// $out.error file. This file will then be parsed by rust_check.py to provide clippy-driver
// errors and warnings to rust-analyzer.
clippyJsonDriver = generateClippyRule("clippyJson", "--error-format=json 2> $out.error")
zip = pctx.AndroidStaticRule("zip",
blueprint.RuleParams{
Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
CommandDeps: []string{"${SoongZipCmd}"},
Rspfile: "$out.rsp",
RspfileContent: "$in",
})
cp = pctx.AndroidStaticRule("cp",
blueprint.RuleParams{
Command: "cp `cat $outDir.rsp` $outDir",
Rspfile: "${outDir}.rsp",
RspfileContent: "$in",
},
"outDir")
)
type buildOutput struct {
outputFile android.Path
kytheFile android.Path
}
func init() {
pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
pctx.HostBinToolVariable("RustcLinkerCmd", "rustc_linker")
pctx.HostBinToolVariable("RustcWrapper", "rustc_wrapper")
pctx.StaticVariable("relPwd", cc.PwdPrefix())
cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib
}
type transformProperties struct {
crateName string
targetTriple string
is64Bit bool
bootstrap bool
inRecovery bool
inRamdisk bool
inVendorRamdisk bool
cargoOutDir android.OptionalPath
synthetic bool
crateType string
emitType string
}
// Populates a standard transformProperties struct for Rust modules
func getTransformProperties(ctx ModuleContext, crateType string) transformProperties {
module := ctx.RustModule()
return transformProperties{
crateName: module.CrateName(),
is64Bit: ctx.toolchain().Is64Bit(),
targetTriple: ctx.toolchain().RustTriple(),
bootstrap: module.Bootstrap(),
inRecovery: module.InRecovery(),
inRamdisk: module.InRamdisk(),
inVendorRamdisk: module.InVendorRamdisk(),
cargoOutDir: module.compiler.cargoOutDir(ctx),
// crateType indicates what type of crate to build
crateType: crateType,
// synthetic indicates whether this is an actual Rust module or not
synthetic: false,
emitType: module.compiler.emitType(),
}
}
func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
if ctx.RustModule().compiler.Thinlto() {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
}
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin"))
}
func TransformSrcToObject(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
// There's no "obj" crate-type since it doesn't get linked, so don't define one.
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, ""))
}
func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
}
// TransformRlibstoStaticlib is assumed to be callable from the cc module, and
// thus needs to reconstruct the common set of flags which need to be passed
// to the rustc compiler.
func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
outputFile android.WritablePath) android.Path {
var rustPathDeps PathDeps
var rustFlags Flags
for _, rlibDep := range deps {
rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName})
rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...)
}
mod := ctx.Module().(cc.LinkableInterface)
toolchain := config.FindToolchain(ctx.Os(), ctx.Arch())
t := transformProperties{
// Crate name can be a predefined value as this is a staticlib and
// it does not need to be unique. The crate name is used for name
// mangling, but it is mixed with the metadata for that purpose, which we
// already set to the module name.
crateName: "generated_rust_staticlib",
is64Bit: toolchain.Is64Bit(),
targetTriple: toolchain.RustTriple(),
bootstrap: mod.Bootstrap(),
inRecovery: mod.InRecovery(),
inRamdisk: mod.InRamdisk(),
inVendorRamdisk: mod.InVendorRamdisk(),
emitType: "link",
// crateType indicates what type of crate to build
crateType: "staticlib",
// synthetic indicates whether this is an actual Rust module or not
synthetic: true,
}
rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags)
rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags)
rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin")
rustFlags.EmitXrefs = false
return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile
}
func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
if ctx.RustModule().compiler.Thinlto() {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
}
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib"))
}
func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
if ctx.RustModule().compiler.Thinlto() {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
}
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib"))
}
func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath) buildOutput {
if ctx.RustModule().compiler.Thinlto() {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
}
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib"))
}
func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
flags Flags, outputFile android.WritablePath) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro"))
}
func rustLibsToPaths(libs RustLibraries) android.Paths {
var paths android.Paths
for _, lib := range libs {
paths = append(paths, lib.Path)
}
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 force:"+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 rustStringifyEnvVars(envVars map[string]string) string {
envVarStrings := []string{}
for _, key := range android.SortedKeys(envVars) {
envVarStrings = append(envVarStrings, key+"="+envVars[key])
}
return strings.Join(envVarStrings, " ")
}
func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) map[string]string {
envVars := make(map[string]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 crateName == "std" {
envVars["STD_ENV_ARCH"] = config.StdEnvArch[ctx.Arch().ArchType]
}
if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() {
moduleGenDir := cargoOutDir
envVars["SOONG_RUST_GEN_DIR"] = moduleGenDir.String()
}
envVars["ANDROID_RUST_VERSION"] = config.GetRustVersion(ctx)
if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() {
// We only emulate cargo environment variables for 3p code, which is only ever built
// by defining a Rust module, so we only need to set these for true Rust modules.
if bin, ok := rustMod.compiler.(*binaryDecorator); ok {
envVars["CARGO_BIN_NAME"] = bin.getStem(ctx)
}
envVars["CARGO_CRATE_NAME"] = crateName
envVars["CARGO_PKG_NAME"] = crateName
pkgVersion := rustMod.compiler.cargoPkgVersion()
if pkgVersion != "" {
envVars["CARGO_PKG_VERSION"] = pkgVersion
// Ensure the version is in the form of "x.y.z" (approximately semver compliant).
//
// For our purposes, we don't care to enforce that these are integers since they may
// include other characters at times (e.g. sometimes the patch version is more than an integer).
if strings.Count(pkgVersion, ".") == 2 {
var semver_parts = strings.Split(pkgVersion, ".")
envVars["CARGO_PKG_VERSION_MAJOR"] = semver_parts[0]
envVars["CARGO_PKG_VERSION_MINOR"] = semver_parts[1]
envVars["CARGO_PKG_VERSION_PATCH"] = semver_parts[2]
}
}
}
if rustModule, ok := ctx.Module().(*Module); ok {
if _, ok := rustModule.compiler.(*toolchainLibraryDecorator); ok {
// some toolchain libraries rely on sources which are pregenerated
// during the toolchain build. This ensures that `include!` macros
// work.
// Note this may be overwritten by rustc_wrapper.sh if SOONG_RUST_GEN_DIR
// is set.
envVars["OUT_DIR"] = "out/"
}
}
if ctx.Darwin() {
envVars["ANDROID_RUST_DARWIN"] = "true"
}
return envVars
}
func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, t transformProperties) buildOutput {
var inputs android.Paths
var implicits android.Paths
var orderOnly android.Paths
var output buildOutput
var linkerScriptFlags, rustcFlags, linkFlags []string
var earlyLinkFlags string
output.outputFile = outputFile
envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir)
inputs = append(inputs, main)
// Collect rustc flags
rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
rustcFlags = append(rustcFlags, flags.RustFlags...)
if t.crateType != "" {
rustcFlags = append(rustcFlags, "--crate-type="+t.crateType)
}
if t.crateName != "" {
rustcFlags = append(rustcFlags, "--crate-name="+t.crateName)
}
if t.targetTriple != "" {
rustcFlags = append(rustcFlags, "--target="+t.targetTriple)
linkFlags = append(linkFlags, "-target "+t.targetTriple)
}
// Suppress an implicit sysroot
rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
// Enable incremental compilation if requested by user
if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") {
incrementalPath := android.PathForOutput(ctx, "rustc").String()
rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath)
} else if ctx.Config().Eng() {
rustcFlags = append(rustcFlags, "-C codegen-units=16")
} else {
rustcFlags = append(rustcFlags, "-C codegen-units=1")
}
// Disallow experimental features
modulePath := ctx.ModuleDir()
if !android.IsThirdPartyPath(modulePath) && !strings.HasPrefix(modulePath, "prebuilts") {
rustcFlags = append(rustcFlags, "-Zallow-features=\"\"")
}
// Collect linker flags
if !ctx.Darwin() {
earlyLinkFlags = "-Wl,--as-needed"
}
linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
linkFlags = append(linkFlags, flags.LinkFlags...)
linkerScriptFlags = append(linkerScriptFlags, flags.LinkerScriptFlags...)
// Check if this module needs to use the bootstrap linker
if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk {
dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker"
if t.is64Bit {
dynamicLinker += "64"
}
linkFlags = append(linkFlags, dynamicLinker)
}
if generatedLib := cc.GenerateRustStaticlib(ctx, deps.ccRlibDeps); generatedLib != nil {
deps.StaticLibs = append(deps.StaticLibs, generatedLib)
linkFlags = append(linkFlags, generatedLib.String())
}
libFlags := makeLibFlags(deps)
// Collect dependencies
implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
implicits = append(implicits, deps.StaticLibs...)
implicits = append(implicits, deps.SharedLibDeps...)
implicits = append(implicits, deps.srcProviderFiles...)
implicits = append(implicits, deps.AfdoProfiles...)
implicits = append(implicits, deps.LinkerDeps...)
implicits = append(implicits, deps.CrtBegin...)
implicits = append(implicits, deps.CrtEnd...)
orderOnly = append(orderOnly, deps.SharedLibs...)
if !t.synthetic {
// Only worry about OUT_DIR for actual Rust modules.
// Libraries built from cc use generated source, and do not utilize OUT_DIR.
if len(deps.SrcDeps) > 0 {
var outputs android.WritablePaths
for _, genSrc := range deps.SrcDeps {
if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
ctx.PropertyErrorf("srcs",
"multiple source providers generate the same filename output: "+genSrc.Base())
}
outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
}
ctx.Build(pctx, android.BuildParams{
Rule: cp,
Description: "cp " + t.cargoOutDir.Path().Rel(),
Outputs: outputs,
Inputs: deps.SrcDeps,
Args: map[string]string{
"outDir": t.cargoOutDir.String(),
},
})
implicits = append(implicits, outputs.Paths()...)
}
}
var implicitOutputs android.WritablePaths
if ctx.Windows() && (t.crateType == "cdylib" || t.crateType == "dylib") {
// On windows, an additional .lib file is produced for dynamic linkages.
importLibraryPath := android.PathForModuleOut(ctx, outputFile.ReplaceExtension(ctx, "lib").Base())
linkFlags = append(linkFlags, "-Wl,--out-implib="+importLibraryPath.String())
implicitOutputs = append(implicitOutputs, importLibraryPath)
// On Windows, we always generate a PDB file
// --strip-debug is needed to also keep COFF symbols which are needed when
// we patch binaries with symbol_inject.
pdb := outputFile.ReplaceExtension(ctx, "pdb")
linkFlags = append(linkFlags, " -Wl,--strip-debug -Wl,--pdb="+pdb.String()+" ")
implicitOutputs = append(implicitOutputs, pdb)
}
if !t.synthetic {
// Only worry about clippy for actual Rust modules.
// Libraries built from cc use generated source, and don't need to run clippy.
checkJsonFile := android.PathForModuleOut(ctx, outputFile.Base()+".checkJson")
args := map[string]string{
"rustcFlags": strings.Join(rustcFlags, " "),
"libFlags": strings.Join(libFlags, " "),
"clippyFlags": strings.Join(flags.ClippyFlags, " "),
"envVars": rustStringifyEnvVars(envVars),
}
if flags.Clippy {
ctx.Build(pctx, android.BuildParams{
Rule: clippyJsonDriver,
Description: "clippy check json " + main.Rel(),
Output: checkJsonFile,
Inputs: inputs,
Implicits: implicits,
Args: args,
})
ctx.Phony("rustClippyJson", checkJsonFile)
clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
ctx.Build(pctx, android.BuildParams{
Rule: clippyDriver,
Description: "clippy " + main.Rel(),
Output: clippyFile,
ImplicitOutputs: nil,
Inputs: inputs,
Implicits: implicits,
OrderOnly: orderOnly,
Args: args,
})
// Declare the clippy build as an implicit dependency of the original crate.
implicits = append(implicits, clippyFile)
}
}
rule := rustc
args := map[string]string{
"rustcFlags": strings.Join(rustcFlags, " "),
"linkerScriptFlags": strings.Join(linkerScriptFlags, " "),
"earlyLinkFlags": earlyLinkFlags,
"linkFlags": strings.Join(linkFlags, " "),
"libFlags": strings.Join(libFlags, " "),
"crtBegin": strings.Join(deps.CrtBegin.Strings(), " "),
"crtEnd": strings.Join(deps.CrtEnd.Strings(), " "),
"envVars": rustStringifyEnvVars(envVars),
"emitType": t.emitType,
}
// If SrcFiles populating is ever tied to some other property being set
// (e.g. crate_root), a check against whether its populated should be added here.
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_RUST") {
rule = rustcRbe
rbeInputs := android.Paths{}
rbeInputs = append(rbeInputs, implicits...)
rbeInputs = append(rbeInputs, deps.SrcDeps...)
rbeInputs = append(rbeInputs, deps.srcProviderFiles...)
rbeInputs = append(rbeInputs, main)
rbeInputs = append(rbeInputs, depset.New(depset.PREORDER, deps.directApexImplementationDeps, deps.transitiveApexImplementationDeps).ToList()...)
rbeInputs = append(rbeInputs, depset.New(depset.PREORDER, deps.directNonApexImplementationDeps, deps.transitiveNonApexImplementationDeps).ToList()...)
rbeInputs = append(rbeInputs, deps.SrcFiles...)
rbeInputs = android.FirstUniquePaths(rbeInputs)
// Produce an rsp file for RBE as the inputs list can easily grow too large.
rbeRustRspFile := android.PathForModuleOut(ctx, "", outputFile.Base()+".rbe.rsp")
android.WriteFileRule(ctx, rbeRustRspFile, strings.Join(rbeInputs.Strings(), "\n"))
implicits = append(implicits, rbeRustRspFile)
args["rbeRspFile"] = rbeRustRspFile.String()
}
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "rustc " + main.Rel(),
Output: outputFile,
Inputs: inputs,
Implicits: implicits,
ImplicitOutputs: implicitOutputs,
OrderOnly: orderOnly,
Args: args,
})
return output
}
func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps,
flags Flags) android.ModuleOutPath {
rustdocFlags := slices.Clone(flags.RustdocFlags)
rustdocFlags = append(rustdocFlags, flags.GlobalRustFlags...)
rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null")
// Build an index for all our crates. -Z unstable options is required to use
// this flag.
rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page")
// Ensure we use any special-case code-paths for Soong.
rustdocFlags = append(rustdocFlags, "--cfg", "soong")
targetTriple := ctx.toolchain().RustTriple()
// Collect rustc flags
if targetTriple != "" {
rustdocFlags = append(rustdocFlags, "--target="+targetTriple)
}
crateName := ctx.RustModule().CrateName()
rustdocFlags = append(rustdocFlags, "--crate-name "+crateName)
rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...)
docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp")
// Silence warnings about renamed lints for third-party crates
modulePath := ctx.ModuleDir()
if android.IsThirdPartyPath(modulePath) {
rustdocFlags = append(rustdocFlags, " -A warnings")
}
// Yes, the same out directory is used simultaneously by all rustdoc builds.
// This is what cargo does. The docs for individual crates get generated to
// a subdirectory named for the crate, and rustdoc synchronizes writes to
// shared pieces like the index and search data itself.
// https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/render/write_shared.rs#L144-L146
docDir := android.PathForOutput(ctx, "rustdoc")
var implicits android.Paths
implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
envVars := rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir(ctx))
ctx.Build(pctx, android.BuildParams{
Rule: rustdoc,
Description: "rustdoc " + main.Rel(),
Output: docTimestampFile,
Input: main,
Implicit: ctx.RustModule().UnstrippedOutputFile(),
Implicits: implicits,
Args: map[string]string{
"rustdocFlags": strings.Join(rustdocFlags, " "),
"outDir": docDir.String(),
"envVars": rustStringifyEnvVars(envVars),
},
})
return docTimestampFile
}