Rustdoc support.

Adds `m rustdoc` which generates documentation for all Rust libraries
to $OUT_DIR/soong/rustdoc.

Follow up work:

* Generate an index page that lists all modules.
* Preserve the artifacts so we can have an always-up-to-date go link.

Test: m rustdoc
Bug: None
Change-Id: Id2d6b9cbab5b02e36b575567563d7cc7606b9401
This commit is contained in:
Dan Albert 2021-03-19 15:06:02 -07:00
parent 744fb40e5f
commit 06feee9352
9 changed files with 210 additions and 58 deletions

View File

@ -21,6 +21,7 @@ bootstrap_go_package {
"clippy.go",
"compiler.go",
"coverage.go",
"doc.go",
"fuzz.go",
"image.go",
"library.go",

View File

@ -122,7 +122,7 @@ func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps Path
flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile)
if binary.stripper.NeedsStrip(ctx) {
strippedOutputFile := android.PathForModuleOut(ctx, "stripped", fileName)

View File

@ -44,6 +44,16 @@ var (
},
"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
_ = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
rustdoc = pctx.AndroidStaticRule("rustdoc",
blueprint.RuleParams{
Command: "rm -rf $outDir && " +
"$envVars $rustdocCmd $rustdocFlags $in -o $outDir && " +
"touch $out",
CommandDeps: []string{"$rustdocCmd"},
},
"rustdocFlags", "outDir", "envVars")
_ = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
clippyDriver = pctx.AndroidStaticRule("clippy",
blueprint.RuleParams{
@ -85,37 +95,37 @@ func init() {
}
func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
outputFile android.WritablePath) buildOutput {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
}
func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", linkDirs)
outputFile android.WritablePath) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib")
}
func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", linkDirs)
outputFile android.WritablePath) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
}
func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
outputFile android.WritablePath) buildOutput {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
}
func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, linkDirs []string) buildOutput {
outputFile android.WritablePath) buildOutput {
flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
}
func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
flags Flags, outputFile android.WritablePath, linkDirs []string) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", linkDirs)
flags Flags, outputFile android.WritablePath) buildOutput {
return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro")
}
func rustLibsToPaths(libs RustLibraries) android.Paths {
@ -126,26 +136,69 @@ func rustLibsToPaths(libs RustLibraries) android.Paths {
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 "+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 rustEnvVars(ctx ModuleContext, deps PathDeps) []string {
var envVars []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 ctx.RustModule().CrateName() == "std" {
envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
}
if len(deps.SrcDeps) > 0 {
moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
// assumes that paths are relative to the source file.
var outDirPrefix string
if !filepath.IsAbs(moduleGenDir.String()) {
// If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
outDirPrefix = "$$PWD/"
} else {
// If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything.
outDirPrefix = ""
}
envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
}
return envVars
}
func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, crate_type string, linkDirs []string) buildOutput {
outputFile android.WritablePath, crate_type string) buildOutput {
var inputs android.Paths
var implicits android.Paths
var envVars []string
var output buildOutput
var libFlags, rustcFlags, linkFlags []string
var rustcFlags, linkFlags []string
var implicitOutputs android.WritablePaths
output.outputFile = outputFile
crateName := ctx.RustModule().CrateName()
targetTriple := ctx.toolchain().RustTriple()
// 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 = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
}
envVars := rustEnvVars(ctx, deps)
inputs = append(inputs, main)
@ -168,20 +221,7 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
linkFlags = append(linkFlags, flags.LinkFlags...)
// 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 "+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 linkDirs {
libFlags = append(libFlags, "-L "+path)
}
libFlags := makeLibFlags(deps)
// Collect dependencies
implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
@ -217,18 +257,6 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
},
})
implicits = append(implicits, outputs.Paths()...)
// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
// assumes that paths are relative to the source file.
var outDirPrefix string
if !filepath.IsAbs(moduleGenDir.String()) {
// If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
outDirPrefix = "$$PWD/"
} else {
// If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything.
outDirPrefix = ""
}
envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
}
envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
@ -272,3 +300,41 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
return output
}
func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps,
flags Flags) android.ModuleOutPath {
rustdocFlags := append([]string{}, flags.RustdocFlags...)
rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null")
targetTriple := ctx.toolchain().RustTriple()
// Collect rustc flags
if targetTriple != "" {
rustdocFlags = append(rustdocFlags, "--target="+targetTriple)
}
crateName := ctx.RustModule().CrateName()
if crateName != "" {
rustdocFlags = append(rustdocFlags, "--crate-name "+crateName)
}
rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...)
docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp")
docDir := android.PathForOutput(ctx, "rustdoc", ctx.ModuleName())
ctx.Build(pctx, android.BuildParams{
Rule: rustdoc,
Description: "rustdoc " + main.Rel(),
Output: docTimestampFile,
Input: main,
Implicit: ctx.RustModule().unstrippedOutputFile.Path(),
Args: map[string]string{
"rustdocFlags": strings.Join(rustdocFlags, " "),
"outDir": docDir.String(),
"envVars": strings.Join(rustEnvVars(ctx, deps), " "),
},
})
return docTimestampFile
}

View File

@ -239,7 +239,10 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flag
flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...)
flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...)
flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...)
flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition())
flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
@ -272,6 +275,12 @@ func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathD
panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
}
func (compiler *baseCompiler) rustdoc(ctx ModuleContext, flags Flags,
deps PathDeps) android.OptionalPath {
return android.OptionalPath{}
}
func (compiler *baseCompiler) initialize(ctx ModuleContext) {
compiler.cargoOutDir = android.PathForModuleOut(ctx, genSubDir)
}

43
rust/doc.go Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2021 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 (
"android/soong/android"
)
func init() {
android.RegisterSingletonType("rustdoc", RustdocSingleton)
}
func RustdocSingleton() android.Singleton {
return &rustdocSingleton{}
}
type rustdocSingleton struct{}
func (n *rustdocSingleton) GenerateBuildActions(ctx android.SingletonContext) {
ctx.VisitAllModules(func(module android.Module) {
if !module.Enabled() {
return
}
if m, ok := module.(*Module); ok {
if m.docTimestampFile.Valid() {
ctx.Phony("rustdoc", m.docTimestampFile.Path())
}
}
})
}

View File

@ -432,14 +432,10 @@ func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) F
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
var outputFile android.ModuleOutPath
var fileName string
var srcPath android.Path
srcPath := library.srcPath(ctx, deps)
if library.sourceProvider != nil {
// Assume the first source from the source provider is the library entry point.
srcPath = library.sourceProvider.Srcs()[0]
deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
} else {
srcPath, _ = srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
}
flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
@ -457,22 +453,22 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile)
} else if library.dylib() {
fileName = library.getStem(ctx) + ctx.toolchain().DylibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile)
} else if library.static() {
fileName = library.getStem(ctx) + ctx.toolchain().StaticLibSuffix()
outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile)
} else if library.shared() {
fileName = library.sharedLibFilename(ctx)
outputFile = android.PathForModuleOut(ctx, fileName)
TransformSrctoShared(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
TransformSrctoShared(ctx, srcPath, deps, flags, outputFile)
}
if !library.rlib() && !library.static() && library.stripper.NeedsStrip(ctx) {
@ -513,6 +509,31 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
return outputFile
}
func (library *libraryDecorator) srcPath(ctx ModuleContext, deps PathDeps) android.Path {
if library.sourceProvider != nil {
// Assume the first source from the source provider is the library entry point.
return library.sourceProvider.Srcs()[0]
} else {
path, _ := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
return path
}
}
func (library *libraryDecorator) rustdoc(ctx ModuleContext, flags Flags,
deps PathDeps) android.OptionalPath {
// rustdoc has builtin support for documenting config specific information
// regardless of the actual config it was given
// (https://doc.rust-lang.org/rustdoc/advanced-features.html#cfgdoc-documenting-platform-specific-or-feature-specific-information),
// so we generate the rustdoc for only the primary module so that we have a
// single set of docs to refer to.
if ctx.Module() != ctx.PrimaryModule() {
return android.OptionalPath{}
}
return android.OptionalPathForPath(Rustdoc(ctx, library.srcPath(ctx, deps),
deps, flags))
}
func (library *libraryDecorator) getStem(ctx ModuleContext) string {
stem := library.baseCompiler.getStemWithoutSuffix(ctx)
validateLibraryStem(ctx, stem, library.crateName())

View File

@ -103,6 +103,12 @@ func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags
return srcPath
}
func (prebuilt *prebuiltLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags,
deps PathDeps) android.OptionalPath {
return android.OptionalPath{}
}
func (prebuilt *prebuiltLibraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
return deps

View File

@ -68,7 +68,7 @@ func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, dep
outputFile := android.PathForModuleOut(ctx, fileName)
srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
return outputFile
}

View File

@ -58,6 +58,7 @@ type Flags struct {
RustFlags []string // Flags that apply to rust
LinkFlags []string // Flags that apply to linker
ClippyFlags []string // Flags that apply to clippy-driver, during the linting
RustdocFlags []string // Flags that apply to rustdoc
Toolchain config.Toolchain
Coverage bool
Clippy bool
@ -124,6 +125,7 @@ type Module struct {
// as a library. The stripped output which is used for installation can be found via
// compiler.strippedOutputFile if it exists.
unstrippedOutputFile android.OptionalPath
docTimestampFile android.OptionalPath
hideApexVariantFromMake bool
}
@ -355,10 +357,12 @@ type compiler interface {
compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path
compilerDeps(ctx DepsContext, deps Deps) Deps
crateName() string
rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
// Output directory in which source-generated code from dependencies is
// copied. This is equivalent to Cargo's OUT_DIR variable.
CargoOutDir() android.OptionalPath
inData() bool
install(ctx ModuleContext)
relativeInstallPath() string
@ -755,6 +759,8 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
mod.unstrippedOutputFile = android.OptionalPathForPath(unstrippedOutputFile)
bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), mod.unstrippedOutputFile)
mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if mod.installable(apexInfo) {
mod.compiler.install(ctx)