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=${config.RustLinker} " + 34 "-C link-args=\"${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"}, 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=${config.RustLinker} ` + 106 `-C link-args="${crtBegin} ${linkFlags} ${crtEnd}" ` + 107 `$in ${libFlags} $rustcFlags`, 108 CommandDeps: []string{"$rustExtractor", "$kytheVnames"}, 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 cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib 123} 124 125type transformProperties struct { 126 crateName string 127 targetTriple string 128 is64Bit bool 129 bootstrap bool 130 inRecovery bool 131 inRamdisk bool 132 inVendorRamdisk bool 133 cargoOutDir android.OptionalPath 134 synthetic bool 135 crateType string 136} 137 138// Populates a standard transformProperties struct for Rust modules 139func getTransformProperties(ctx ModuleContext, crateType string) transformProperties { 140 module := ctx.RustModule() 141 return transformProperties{ 142 crateName: module.CrateName(), 143 is64Bit: ctx.toolchain().Is64Bit(), 144 targetTriple: ctx.toolchain().RustTriple(), 145 bootstrap: module.Bootstrap(), 146 inRecovery: module.InRecovery(), 147 inRamdisk: module.InRamdisk(), 148 inVendorRamdisk: module.InVendorRamdisk(), 149 cargoOutDir: module.compiler.cargoOutDir(), 150 151 // crateType indicates what type of crate to build 152 crateType: crateType, 153 154 // synthetic indicates whether this is an actual Rust module or not 155 synthetic: false, 156 } 157} 158 159func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 160 outputFile android.WritablePath) buildOutput { 161 if ctx.RustModule().compiler.Thinlto() { 162 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 163 } 164 165 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin")) 166} 167 168func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 169 outputFile android.WritablePath) buildOutput { 170 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib")) 171} 172 173// TransformRlibstoStaticlib is assumed to be called from the cc module, and 174// thus needs to reconstruct the common set of flags which need to be passed 175// to the rustc compiler. 176func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep, 177 outputFile android.WritablePath) android.Path { 178 179 var rustPathDeps PathDeps 180 var rustFlags Flags 181 182 for _, rlibDep := range deps { 183 rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName}) 184 rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...) 185 } 186 187 ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module) 188 toolchain := config.FindToolchain(ctx.Os(), ctx.Arch()) 189 t := transformProperties{ 190 // Crate name can be a predefined value as this is a staticlib and 191 // it does not need to be unique. The crate name is used for name 192 // mangling, but it is mixed with the metadata for that purpose, which we 193 // already set to the module name. 194 crateName: "generated_rust_staticlib", 195 is64Bit: toolchain.Is64Bit(), 196 targetTriple: toolchain.RustTriple(), 197 bootstrap: ccModule.Bootstrap(), 198 inRecovery: ccModule.InRecovery(), 199 inRamdisk: ccModule.InRamdisk(), 200 inVendorRamdisk: ccModule.InVendorRamdisk(), 201 202 // crateType indicates what type of crate to build 203 crateType: "staticlib", 204 205 // synthetic indicates whether this is an actual Rust module or not 206 synthetic: true, 207 } 208 209 rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags) 210 rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags) 211 rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin") 212 213 rustFlags.EmitXrefs = false 214 215 return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile 216} 217 218func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 219 outputFile android.WritablePath) buildOutput { 220 if ctx.RustModule().compiler.Thinlto() { 221 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 222 } 223 224 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib")) 225} 226 227func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 228 outputFile android.WritablePath) buildOutput { 229 if ctx.RustModule().compiler.Thinlto() { 230 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 231 } 232 233 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib")) 234} 235 236func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, 237 outputFile android.WritablePath) buildOutput { 238 if ctx.RustModule().compiler.Thinlto() { 239 flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") 240 } 241 242 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib")) 243} 244 245func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps, 246 flags Flags, outputFile android.WritablePath) buildOutput { 247 return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro")) 248} 249 250func rustLibsToPaths(libs RustLibraries) android.Paths { 251 var paths android.Paths 252 for _, lib := range libs { 253 paths = append(paths, lib.Path) 254 } 255 return paths 256} 257 258func makeLibFlags(deps PathDeps) []string { 259 var libFlags []string 260 261 // Collect library/crate flags 262 for _, lib := range deps.RLibs { 263 libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String()) 264 } 265 for _, lib := range deps.DyLibs { 266 libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String()) 267 } 268 for _, proc_macro := range deps.ProcMacros { 269 libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String()) 270 } 271 272 for _, path := range deps.linkDirs { 273 libFlags = append(libFlags, "-L "+path) 274 } 275 276 return libFlags 277} 278 279func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) []string { 280 var envVars []string 281 282 // libstd requires a specific environment variable to be set. This is 283 // not officially documented and may be removed in the future. See 284 // https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866. 285 if crateName == "std" { 286 envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.Arch().ArchType]) 287 } 288 289 if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() { 290 moduleGenDir := cargoOutDir 291 // We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this) 292 // assumes that paths are relative to the source file. 293 var outDirPrefix string 294 if !filepath.IsAbs(moduleGenDir.String()) { 295 // If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/') 296 outDirPrefix = "$$PWD/" 297 } else { 298 // If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything. 299 outDirPrefix = "" 300 } 301 envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String())) 302 } else { 303 // TODO(pcc): Change this to "OUT_DIR=" after fixing crates to not rely on this value. 304 envVars = append(envVars, "OUT_DIR=out") 305 } 306 307 envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx)) 308 309 if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() { 310 // We only emulate cargo environment variables for 3p code, which is only ever built 311 // by defining a Rust module, so we only need to set these for true Rust modules. 312 if bin, ok := rustMod.compiler.(*binaryDecorator); ok { 313 envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx)) 314 } 315 envVars = append(envVars, "CARGO_CRATE_NAME="+crateName) 316 envVars = append(envVars, "CARGO_PKG_NAME="+crateName) 317 pkgVersion := rustMod.compiler.cargoPkgVersion() 318 if pkgVersion != "" { 319 envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion) 320 321 // Ensure the version is in the form of "x.y.z" (approximately semver compliant). 322 // 323 // For our purposes, we don't care to enforce that these are integers since they may 324 // include other characters at times (e.g. sometimes the patch version is more than an integer). 325 if strings.Count(pkgVersion, ".") == 2 { 326 var semver_parts = strings.Split(pkgVersion, ".") 327 envVars = append(envVars, "CARGO_PKG_VERSION_MAJOR="+semver_parts[0]) 328 envVars = append(envVars, "CARGO_PKG_VERSION_MINOR="+semver_parts[1]) 329 envVars = append(envVars, "CARGO_PKG_VERSION_PATCH="+semver_parts[2]) 330 } 331 } 332 } 333 334 if ctx.Darwin() { 335 envVars = append(envVars, "ANDROID_RUST_DARWIN=true") 336 } 337 338 return envVars 339} 340 341func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags, 342 outputFile android.WritablePath, t transformProperties) buildOutput { 343 344 var inputs android.Paths 345 var implicits android.Paths 346 var orderOnly android.Paths 347 var output buildOutput 348 var rustcFlags, linkFlags []string 349 var earlyLinkFlags string 350 351 output.outputFile = outputFile 352 353 envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir) 354 355 inputs = append(inputs, main) 356 357 // Collect rustc flags 358 rustcFlags = append(rustcFlags, flags.GlobalRustFlags...) 359 rustcFlags = append(rustcFlags, flags.RustFlags...) 360 rustcFlags = append(rustcFlags, "--crate-type="+t.crateType) 361 if t.crateName != "" { 362 rustcFlags = append(rustcFlags, "--crate-name="+t.crateName) 363 } 364 if t.targetTriple != "" { 365 rustcFlags = append(rustcFlags, "--target="+t.targetTriple) 366 linkFlags = append(linkFlags, "-target "+t.targetTriple) 367 } 368 369 // Suppress an implicit sysroot 370 rustcFlags = append(rustcFlags, "--sysroot=/dev/null") 371 372 // Enable incremental compilation if requested by user 373 if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") { 374 incrementalPath := android.PathForOutput(ctx, "rustc").String() 375 376 rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath) 377 } else { 378 rustcFlags = append(rustcFlags, "-C codegen-units=1") 379 } 380 381 // Disallow experimental features 382 modulePath := ctx.ModuleDir() 383 if !(android.IsThirdPartyPath(modulePath) || strings.HasPrefix(modulePath, "prebuilts")) { 384 rustcFlags = append(rustcFlags, "-Zallow-features=\"\"") 385 } 386 387 // Collect linker flags 388 if !ctx.Darwin() { 389 earlyLinkFlags = "-Wl,--as-needed" 390 } 391 392 linkFlags = append(linkFlags, flags.GlobalLinkFlags...) 393 linkFlags = append(linkFlags, flags.LinkFlags...) 394 395 // Check if this module needs to use the bootstrap linker 396 if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk { 397 dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker" 398 if t.is64Bit { 399 dynamicLinker += "64" 400 } 401 linkFlags = append(linkFlags, dynamicLinker) 402 } 403 404 libFlags := makeLibFlags(deps) 405 406 // Collect dependencies 407 implicits = append(implicits, rustLibsToPaths(deps.RLibs)...) 408 implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...) 409 implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...) 410 implicits = append(implicits, deps.StaticLibs...) 411 implicits = append(implicits, deps.SharedLibDeps...) 412 implicits = append(implicits, deps.srcProviderFiles...) 413 implicits = append(implicits, deps.AfdoProfiles...) 414 415 implicits = append(implicits, deps.CrtBegin...) 416 implicits = append(implicits, deps.CrtEnd...) 417 418 orderOnly = append(orderOnly, deps.SharedLibs...) 419 420 if !t.synthetic { 421 // Only worry about OUT_DIR for actual Rust modules. 422 // Libraries built from cc use generated source, and do not utilize OUT_DIR. 423 if len(deps.SrcDeps) > 0 { 424 var outputs android.WritablePaths 425 426 for _, genSrc := range deps.SrcDeps { 427 if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { 428 ctx.PropertyErrorf("srcs", 429 "multiple source providers generate the same filename output: "+genSrc.Base()) 430 } 431 outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) 432 } 433 434 ctx.Build(pctx, android.BuildParams{ 435 Rule: cp, 436 Description: "cp " + t.cargoOutDir.Path().Rel(), 437 Outputs: outputs, 438 Inputs: deps.SrcDeps, 439 Args: map[string]string{ 440 "outDir": t.cargoOutDir.String(), 441 }, 442 }) 443 implicits = append(implicits, outputs.Paths()...) 444 } 445 } 446 447 if !t.synthetic { 448 // Only worry about clippy for actual Rust modules. 449 // Libraries built from cc use generated source, and don't need to run clippy. 450 if flags.Clippy { 451 clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") 452 ctx.Build(pctx, android.BuildParams{ 453 Rule: clippyDriver, 454 Description: "clippy " + main.Rel(), 455 Output: clippyFile, 456 ImplicitOutputs: nil, 457 Inputs: inputs, 458 Implicits: implicits, 459 OrderOnly: orderOnly, 460 Args: map[string]string{ 461 "rustcFlags": strings.Join(rustcFlags, " "), 462 "libFlags": strings.Join(libFlags, " "), 463 "clippyFlags": strings.Join(flags.ClippyFlags, " "), 464 "envVars": strings.Join(envVars, " "), 465 }, 466 }) 467 // Declare the clippy build as an implicit dependency of the original crate. 468 implicits = append(implicits, clippyFile) 469 } 470 } 471 472 ctx.Build(pctx, android.BuildParams{ 473 Rule: rustc, 474 Description: "rustc " + main.Rel(), 475 Output: outputFile, 476 Inputs: inputs, 477 Implicits: implicits, 478 OrderOnly: orderOnly, 479 Args: map[string]string{ 480 "rustcFlags": strings.Join(rustcFlags, " "), 481 "earlyLinkFlags": earlyLinkFlags, 482 "linkFlags": strings.Join(linkFlags, " "), 483 "libFlags": strings.Join(libFlags, " "), 484 "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), 485 "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), 486 "envVars": strings.Join(envVars, " "), 487 }, 488 }) 489 490 if !t.synthetic { 491 // Only emit xrefs for true Rust modules. 492 if flags.EmitXrefs { 493 kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") 494 ctx.Build(pctx, android.BuildParams{ 495 Rule: kytheExtract, 496 Description: "Xref Rust extractor " + main.Rel(), 497 Output: kytheFile, 498 Inputs: inputs, 499 Implicits: implicits, 500 OrderOnly: orderOnly, 501 Args: map[string]string{ 502 "rustcFlags": strings.Join(rustcFlags, " "), 503 "linkFlags": strings.Join(linkFlags, " "), 504 "libFlags": strings.Join(libFlags, " "), 505 "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), 506 "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), 507 "envVars": strings.Join(envVars, " "), 508 }, 509 }) 510 output.kytheFile = kytheFile 511 } 512 } 513 return output 514} 515 516func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps, 517 flags Flags) android.ModuleOutPath { 518 519 rustdocFlags := append([]string{}, flags.RustdocFlags...) 520 rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null") 521 522 // Build an index for all our crates. -Z unstable options is required to use 523 // this flag. 524 rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page") 525 526 // Ensure we use any special-case code-paths for Soong. 527 rustdocFlags = append(rustdocFlags, "--cfg", "soong") 528 529 targetTriple := ctx.toolchain().RustTriple() 530 531 // Collect rustc flags 532 if targetTriple != "" { 533 rustdocFlags = append(rustdocFlags, "--target="+targetTriple) 534 } 535 536 crateName := ctx.RustModule().CrateName() 537 rustdocFlags = append(rustdocFlags, "--crate-name "+crateName) 538 539 rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...) 540 docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp") 541 542 // Silence warnings about renamed lints for third-party crates 543 modulePath := ctx.ModuleDir() 544 if android.IsThirdPartyPath(modulePath) { 545 rustdocFlags = append(rustdocFlags, " -A warnings") 546 } 547 548 // Yes, the same out directory is used simultaneously by all rustdoc builds. 549 // This is what cargo does. The docs for individual crates get generated to 550 // a subdirectory named for the crate, and rustdoc synchronizes writes to 551 // shared pieces like the index and search data itself. 552 // https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/render/write_shared.rs#L144-L146 553 docDir := android.PathForOutput(ctx, "rustdoc") 554 555 ctx.Build(pctx, android.BuildParams{ 556 Rule: rustdoc, 557 Description: "rustdoc " + main.Rel(), 558 Output: docTimestampFile, 559 Input: main, 560 Implicit: ctx.RustModule().UnstrippedOutputFile(), 561 Args: map[string]string{ 562 "rustdocFlags": strings.Join(rustdocFlags, " "), 563 "outDir": docDir.String(), 564 "envVars": strings.Join(rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir()), " "), 565 }, 566 }) 567 568 return docTimestampFile 569} 570