• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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