xref: /aosp_15_r20/build/soong/rust/builder.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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