1// Copyright 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package rust 16 17import ( 18 "path/filepath" 19 "strings" 20 21 "github.com/google/blueprint" 22 23 "android/soong/android" 24 "android/soong/cc" 25 "android/soong/rust/config" 26) 27 28var ( 29 _ = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc") 30 rustc = pctx.AndroidStaticRule("rustc", 31 blueprint.RuleParams{ 32 Command: "$envVars $rustcCmd " + 33 "-C linker=${RustcLinkerCmd} " + 34 "-C link-args=\"--android-clang-bin=${config.ClangCmd} ${crtBegin} ${earlyLinkFlags} ${linkFlags} ${crtEnd}\" " + 35 "--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" + 36 " && grep ^$out: $out.d.raw > $out.d", 37 CommandDeps: []string{"$rustcCmd", "${RustcLinkerCmd}", "${config.ClangCmd}"}, 38 // Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633 39 // Rustc emits unneeded dependency lines for the .d and input .rs files. 40 // Those extra lines cause ninja warning: 41 // "warning: depfile has multiple output paths" 42 // For ninja, we keep/grep only the dependency rule for the rust $out file. 43 Deps: blueprint.DepsGCC, 44 Depfile: "$out.d", 45 }, 46 "rustcFlags", "earlyLinkFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars") 47 48 _ = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc") 49 rustdoc = pctx.AndroidStaticRule("rustdoc", 50 blueprint.RuleParams{ 51 Command: "$envVars $rustdocCmd $rustdocFlags $in -o $outDir && " + 52 "touch $out", 53 CommandDeps: []string{"$rustdocCmd"}, 54 }, 55 "rustdocFlags", "outDir", "envVars") 56 57 _ = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver") 58 clippyDriver = pctx.AndroidStaticRule("clippy", 59 blueprint.RuleParams{ 60 Command: "$envVars $clippyCmd " + 61 // Because clippy-driver uses rustc as backend, we need to have some output even during the linting. 62 // Use the metadata output as it has the smallest footprint. 63 "--emit metadata -o $out --emit dep-info=$out.d.raw $in ${libFlags} " + 64 "$rustcFlags $clippyFlags" + 65 " && grep ^$out: $out.d.raw > $out.d", 66 CommandDeps: []string{"$clippyCmd"}, 67 Deps: blueprint.DepsGCC, 68 Depfile: "$out.d", 69 }, 70 "rustcFlags", "libFlags", "clippyFlags", "envVars") 71 72 zip = pctx.AndroidStaticRule("zip", 73 blueprint.RuleParams{ 74 Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp", 75 CommandDeps: []string{"${SoongZipCmd}"}, 76 Rspfile: "$out.rsp", 77 RspfileContent: "$in", 78 }) 79 80 cp = pctx.AndroidStaticRule("cp", 81 blueprint.RuleParams{ 82 Command: "cp `cat $outDir.rsp` $outDir", 83 Rspfile: "${outDir}.rsp", 84 RspfileContent: "$in", 85 }, 86 "outDir") 87 88 // Cross-referencing: 89 _ = pctx.SourcePathVariable("rustExtractor", 90 "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/rust_extractor") 91 _ = pctx.VariableFunc("kytheCorpus", 92 func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() }) 93 _ = pctx.VariableFunc("kytheCuEncoding", 94 func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() }) 95 _ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json") 96 kytheExtract = pctx.AndroidStaticRule("kythe", 97 blueprint.RuleParams{ 98 Command: `KYTHE_CORPUS=${kytheCorpus} ` + 99 `KYTHE_OUTPUT_FILE=$out ` + 100 `KYTHE_VNAMES=$kytheVnames ` + 101 `KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` + 102 `KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` + 103 `$rustExtractor $envVars ` + 104 `$rustcCmd ` + 105 `-C linker=${RustcLinkerCmd} ` + 106 `-C link-args="--android-clang-bin=${config.ClangCmd} ${crtBegin} ${linkFlags} ${crtEnd}" ` + 107 `$in ${libFlags} $rustcFlags`, 108 CommandDeps: []string{"$rustExtractor", "$kytheVnames", "${RustcLinkerCmd}", "${config.ClangCmd}"}, 109 Rspfile: "${out}.rsp", 110 RspfileContent: "$in", 111 }, 112 "rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars") 113) 114 115type buildOutput struct { 116 outputFile android.Path 117 kytheFile android.Path 118} 119 120func init() { 121 pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") 122 pctx.HostBinToolVariable("RustcLinkerCmd", "rustc_linker") 123 cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib 124} 125 126type transformProperties struct { 127 crateName string 128 targetTriple string 129 is64Bit bool 130 bootstrap bool 131 inRecovery bool 132 inRamdisk bool 133 inVendorRamdisk bool 134 cargoOutDir android.OptionalPath 135 synthetic bool 136 crateType string 137} 138 139// Populates a standard transformProperties struct for Rust modules 140func getTransformProperties(ctx ModuleContext, crateType string) transformProperties { 141 module := ctx.RustModule() 142 return transformProperties{ 143 crateName: module.CrateName(), 144 is64Bit: ctx.toolchain().Is64Bit(), 145 targetTriple: ctx.toolchain().RustTriple(), 146 bootstrap: module.Bootstrap(), 147 inRecovery: module.InRecovery(), 148 inRamdisk: module.InRamdisk(), 149 inVendorRamdisk: module.InVendorRamdisk(), 150 cargoOutDir: module.compiler.cargoOutDir(), 151 152 // crateType indicates what type of crate to build 153 crateType: crateType, 154 155 // synthetic indicates whether this is an actual Rust module or not 156 synthetic: false, 157 } 158} 159 160func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 161 outputFile android.WritablePath) buildOutput { 162 if ctx.RustModule().compiler.Thinlto() { 163 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 164 } 165 166 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin")) 167} 168 169func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 170 outputFile android.WritablePath) buildOutput { 171 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib")) 172} 173 174// TransformRlibstoStaticlib is assumed to be callable from the cc module, and 175// thus needs to reconstruct the common set of flags which need to be passed 176// to the rustc compiler. 177func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep, 178 outputFile android.WritablePath) android.Path { 179 180 var rustPathDeps PathDeps 181 var rustFlags Flags 182 183 for _, rlibDep := range deps { 184 rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName}) 185 rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...) 186 } 187 188 mod := ctx.Module().(cc.LinkableInterface) 189 toolchain := config.FindToolchain(ctx.Os(), ctx.Arch()) 190 t := transformProperties{ 191 // Crate name can be a predefined value as this is a staticlib and 192 // it does not need to be unique. The crate name is used for name 193 // mangling, but it is mixed with the metadata for that purpose, which we 194 // already set to the module name. 195 crateName: "generated_rust_staticlib", 196 is64Bit: toolchain.Is64Bit(), 197 targetTriple: toolchain.RustTriple(), 198 bootstrap: mod.Bootstrap(), 199 inRecovery: mod.InRecovery(), 200 inRamdisk: mod.InRamdisk(), 201 inVendorRamdisk: mod.InVendorRamdisk(), 202 203 // crateType indicates what type of crate to build 204 crateType: "staticlib", 205 206 // synthetic indicates whether this is an actual Rust module or not 207 synthetic: true, 208 } 209 210 rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags) 211 rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags) 212 rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin") 213 214 rustFlags.EmitXrefs = false 215 216 return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile 217} 218 219func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 220 outputFile android.WritablePath) buildOutput { 221 if ctx.RustModule().compiler.Thinlto() { 222 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 223 } 224 225 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib")) 226} 227 228func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 229 outputFile android.WritablePath) buildOutput { 230 if ctx.RustModule().compiler.Thinlto() { 231 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 232 } 233 234 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib")) 235} 236 237func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 238 outputFile android.WritablePath) buildOutput { 239 if ctx.RustModule().compiler.Thinlto() { 240 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 241 } 242 243 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib")) 244} 245 246func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps, 247 flags Flags, outputFile android.WritablePath) buildOutput { 248 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro")) 249} 250 251func rustLibsToPaths(libs RustLibraries) android.Paths { 252 var paths android.Paths 253 for _, lib := range libs { 254 paths = append(paths, lib.Path) 255 } 256 return paths 257} 258 259func makeLibFlags(deps PathDeps) []string { 260 var libFlags []string 261 262 // Collect library/crate flags 263 for _, lib := range deps.RLibs { 264 libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String()) 265 } 266 for _, lib := range deps.DyLibs { 267 libFlags = append(libFlags, "--extern force:"+lib.CrateName+"="+lib.Path.String()) 268 } 269 for _, proc_macro := range deps.ProcMacros { 270 libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String()) 271 } 272 273 for _, path := range deps.linkDirs { 274 libFlags = append(libFlags, "-L "+path) 275 } 276 277 return libFlags 278} 279 280func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) []string { 281 var envVars []string 282 283 // libstd requires a specific environment variable to be set. This is 284 // not officially documented and may be removed in the future. See 285 // https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866. 286 if crateName == "std" { 287 envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.Arch().ArchType]) 288 } 289 290 if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() { 291 moduleGenDir := cargoOutDir 292 // We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this) 293 // assumes that paths are relative to the source file. 294 var outDirPrefix string 295 if !filepath.IsAbs(moduleGenDir.String()) { 296 // If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/') 297 outDirPrefix = "$$PWD/" 298 } else { 299 // If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything. 300 outDirPrefix = "" 301 } 302 envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String())) 303 } else { 304 // TODO(pcc): Change this to "OUT_DIR=" after fixing crates to not rely on this value. 305 envVars = append(envVars, "OUT_DIR=out") 306 } 307 308 envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx)) 309 310 if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() { 311 // We only emulate cargo environment variables for 3p code, which is only ever built 312 // by defining a Rust module, so we only need to set these for true Rust modules. 313 if bin, ok := rustMod.compiler.(*binaryDecorator); ok { 314 envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx)) 315 } 316 envVars = append(envVars, "CARGO_CRATE_NAME="+crateName) 317 envVars = append(envVars, "CARGO_PKG_NAME="+crateName) 318 pkgVersion := rustMod.compiler.cargoPkgVersion() 319 if pkgVersion != "" { 320 envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion) 321 322 // Ensure the version is in the form of "x.y.z" (approximately semver compliant). 323 // 324 // For our purposes, we don't care to enforce that these are integers since they may 325 // include other characters at times (e.g. sometimes the patch version is more than an integer). 326 if strings.Count(pkgVersion, ".") == 2 { 327 var semver_parts = strings.Split(pkgVersion, ".") 328 envVars = append(envVars, "CARGO_PKG_VERSION_MAJOR="+semver_parts[0]) 329 envVars = append(envVars, "CARGO_PKG_VERSION_MINOR="+semver_parts[1]) 330 envVars = append(envVars, "CARGO_PKG_VERSION_PATCH="+semver_parts[2]) 331 } 332 } 333 } 334 335 if ctx.Darwin() { 336 envVars = append(envVars, "ANDROID_RUST_DARWIN=true") 337 } 338 339 return envVars 340} 341 342func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags, 343 outputFile android.WritablePath, t transformProperties) buildOutput { 344 345 var inputs android.Paths 346 var implicits android.Paths 347 var orderOnly android.Paths 348 var output buildOutput 349 var rustcFlags, linkFlags []string 350 var earlyLinkFlags string 351 352 output.outputFile = outputFile 353 354 envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir) 355 356 inputs = append(inputs, main) 357 358 // Collect rustc flags 359 rustcFlags = append(rustcFlags, flags.GlobalRustFlags...) 360 rustcFlags = append(rustcFlags, flags.RustFlags...) 361 rustcFlags = append(rustcFlags, "--crate-type="+t.crateType) 362 if t.crateName != "" { 363 rustcFlags = append(rustcFlags, "--crate-name="+t.crateName) 364 } 365 if t.targetTriple != "" { 366 rustcFlags = append(rustcFlags, "--target="+t.targetTriple) 367 linkFlags = append(linkFlags, "-target "+t.targetTriple) 368 } 369 370 // Suppress an implicit sysroot 371 rustcFlags = append(rustcFlags, "--sysroot=/dev/null") 372 373 // Enable incremental compilation if requested by user 374 if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") { 375 incrementalPath := android.PathForOutput(ctx, "rustc").String() 376 377 rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath) 378 } else { 379 rustcFlags = append(rustcFlags, "-C codegen-units=1") 380 } 381 382 // Disallow experimental features 383 modulePath := ctx.ModuleDir() 384 if !(android.IsThirdPartyPath(modulePath) || strings.HasPrefix(modulePath, "prebuilts")) { 385 rustcFlags = append(rustcFlags, "-Zallow-features=\"\"") 386 } 387 388 // Collect linker flags 389 if !ctx.Darwin() { 390 earlyLinkFlags = "-Wl,--as-needed" 391 } 392 393 linkFlags = append(linkFlags, flags.GlobalLinkFlags...) 394 linkFlags = append(linkFlags, flags.LinkFlags...) 395 396 // Check if this module needs to use the bootstrap linker 397 if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk { 398 dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker" 399 if t.is64Bit { 400 dynamicLinker += "64" 401 } 402 linkFlags = append(linkFlags, dynamicLinker) 403 } 404 405 if generatedLib := cc.GenerateRustStaticlib(ctx, deps.ccRlibDeps); generatedLib != nil { 406 deps.StaticLibs = append(deps.StaticLibs, generatedLib) 407 linkFlags = append(linkFlags, generatedLib.String()) 408 } 409 410 libFlags := makeLibFlags(deps) 411 412 // Collect dependencies 413 implicits = append(implicits, rustLibsToPaths(deps.RLibs)...) 414 implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...) 415 implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...) 416 implicits = append(implicits, deps.StaticLibs...) 417 implicits = append(implicits, deps.SharedLibDeps...) 418 implicits = append(implicits, deps.srcProviderFiles...) 419 implicits = append(implicits, deps.AfdoProfiles...) 420 implicits = append(implicits, deps.LinkerDeps...) 421 422 implicits = append(implicits, deps.CrtBegin...) 423 implicits = append(implicits, deps.CrtEnd...) 424 425 orderOnly = append(orderOnly, deps.SharedLibs...) 426 427 if !t.synthetic { 428 // Only worry about OUT_DIR for actual Rust modules. 429 // Libraries built from cc use generated source, and do not utilize OUT_DIR. 430 if len(deps.SrcDeps) > 0 { 431 var outputs android.WritablePaths 432 433 for _, genSrc := range deps.SrcDeps { 434 if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { 435 ctx.PropertyErrorf("srcs", 436 "multiple source providers generate the same filename output: "+genSrc.Base()) 437 } 438 outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) 439 } 440 441 ctx.Build(pctx, android.BuildParams{ 442 Rule: cp, 443 Description: "cp " + t.cargoOutDir.Path().Rel(), 444 Outputs: outputs, 445 Inputs: deps.SrcDeps, 446 Args: map[string]string{ 447 "outDir": t.cargoOutDir.String(), 448 }, 449 }) 450 implicits = append(implicits, outputs.Paths()...) 451 } 452 } 453 454 if !t.synthetic { 455 // Only worry about clippy for actual Rust modules. 456 // Libraries built from cc use generated source, and don't need to run clippy. 457 if flags.Clippy { 458 clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") 459 ctx.Build(pctx, android.BuildParams{ 460 Rule: clippyDriver, 461 Description: "clippy " + main.Rel(), 462 Output: clippyFile, 463 ImplicitOutputs: nil, 464 Inputs: inputs, 465 Implicits: implicits, 466 OrderOnly: orderOnly, 467 Args: map[string]string{ 468 "rustcFlags": strings.Join(rustcFlags, " "), 469 "libFlags": strings.Join(libFlags, " "), 470 "clippyFlags": strings.Join(flags.ClippyFlags, " "), 471 "envVars": strings.Join(envVars, " "), 472 }, 473 }) 474 // Declare the clippy build as an implicit dependency of the original crate. 475 implicits = append(implicits, clippyFile) 476 } 477 } 478 479 ctx.Build(pctx, android.BuildParams{ 480 Rule: rustc, 481 Description: "rustc " + main.Rel(), 482 Output: outputFile, 483 Inputs: inputs, 484 Implicits: implicits, 485 OrderOnly: orderOnly, 486 Args: map[string]string{ 487 "rustcFlags": strings.Join(rustcFlags, " "), 488 "earlyLinkFlags": earlyLinkFlags, 489 "linkFlags": strings.Join(linkFlags, " "), 490 "libFlags": strings.Join(libFlags, " "), 491 "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), 492 "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), 493 "envVars": strings.Join(envVars, " "), 494 }, 495 }) 496 497 if !t.synthetic { 498 // Only emit xrefs for true Rust modules. 499 if flags.EmitXrefs { 500 kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") 501 ctx.Build(pctx, android.BuildParams{ 502 Rule: kytheExtract, 503 Description: "Xref Rust extractor " + main.Rel(), 504 Output: kytheFile, 505 Inputs: inputs, 506 Implicits: implicits, 507 OrderOnly: orderOnly, 508 Args: map[string]string{ 509 "rustcFlags": strings.Join(rustcFlags, " "), 510 "linkFlags": strings.Join(linkFlags, " "), 511 "libFlags": strings.Join(libFlags, " "), 512 "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), 513 "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), 514 "envVars": strings.Join(envVars, " "), 515 }, 516 }) 517 output.kytheFile = kytheFile 518 } 519 } 520 return output 521} 522 523func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps, 524 flags Flags) android.ModuleOutPath { 525 526 rustdocFlags := append([]string{}, flags.RustdocFlags...) 527 rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null") 528 529 // Build an index for all our crates. -Z unstable options is required to use 530 // this flag. 531 rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page") 532 533 // Ensure we use any special-case code-paths for Soong. 534 rustdocFlags = append(rustdocFlags, "--cfg", "soong") 535 536 targetTriple := ctx.toolchain().RustTriple() 537 538 // Collect rustc flags 539 if targetTriple != "" { 540 rustdocFlags = append(rustdocFlags, "--target="+targetTriple) 541 } 542 543 crateName := ctx.RustModule().CrateName() 544 rustdocFlags = append(rustdocFlags, "--crate-name "+crateName) 545 546 rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...) 547 docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp") 548 549 // Silence warnings about renamed lints for third-party crates 550 modulePath := ctx.ModuleDir() 551 if android.IsThirdPartyPath(modulePath) { 552 rustdocFlags = append(rustdocFlags, " -A warnings") 553 } 554 555 // Yes, the same out directory is used simultaneously by all rustdoc builds. 556 // This is what cargo does. The docs for individual crates get generated to 557 // a subdirectory named for the crate, and rustdoc synchronizes writes to 558 // shared pieces like the index and search data itself. 559 // https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/render/write_shared.rs#L144-L146 560 docDir := android.PathForOutput(ctx, "rustdoc") 561 562 ctx.Build(pctx, android.BuildParams{ 563 Rule: rustdoc, 564 Description: "rustdoc " + main.Rel(), 565 Output: docTimestampFile, 566 Input: main, 567 Implicit: ctx.RustModule().UnstrippedOutputFile(), 568 Args: map[string]string{ 569 "rustdocFlags": strings.Join(rustdocFlags, " "), 570 "outDir": docDir.String(), 571 "envVars": strings.Join(rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir()), " "), 572 }, 573 }) 574 575 return docTimestampFile 576} 577