From 06feee9352b80ebd2a2a3f186ac746f5d1d7026e Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 19 Mar 2021 15:06:02 -0700 Subject: [PATCH] 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 --- rust/Android.bp | 1 + rust/binary.go | 2 +- rust/builder.go | 160 ++++++++++++++++++++++++++++++++------------- rust/compiler.go | 9 +++ rust/doc.go | 43 ++++++++++++ rust/library.go | 39 ++++++++--- rust/prebuilt.go | 6 ++ rust/proc_macro.go | 2 +- rust/rust.go | 6 ++ 9 files changed, 210 insertions(+), 58 deletions(-) create mode 100644 rust/doc.go diff --git a/rust/Android.bp b/rust/Android.bp index a6c4e0787..f45404ff2 100644 --- a/rust/Android.bp +++ b/rust/Android.bp @@ -21,6 +21,7 @@ bootstrap_go_package { "clippy.go", "compiler.go", "coverage.go", + "doc.go", "fuzz.go", "image.go", "library.go", diff --git a/rust/binary.go b/rust/binary.go index dfe8744a1..ffc0413ec 100644 --- a/rust/binary.go +++ b/rust/binary.go @@ -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) diff --git a/rust/builder.go b/rust/builder.go index 208b73450..1fcce3859 100644 --- a/rust/builder.go +++ b/rust/builder.go @@ -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 +} diff --git a/rust/compiler.go b/rust/compiler.go index bc034d7cc..bfc23b209 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -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) } diff --git a/rust/doc.go b/rust/doc.go new file mode 100644 index 000000000..e7f137198 --- /dev/null +++ b/rust/doc.go @@ -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()) + } + } + }) +} diff --git a/rust/library.go b/rust/library.go index 26c104c95..4e6031d86 100644 --- a/rust/library.go +++ b/rust/library.go @@ -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()) diff --git a/rust/prebuilt.go b/rust/prebuilt.go index 94fe1e546..49f3c0f79 100644 --- a/rust/prebuilt.go +++ b/rust/prebuilt.go @@ -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 diff --git a/rust/proc_macro.go b/rust/proc_macro.go index 115045ac6..4eead3267 100644 --- a/rust/proc_macro.go +++ b/rust/proc_macro.go @@ -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 } diff --git a/rust/rust.go b/rust/rust.go index 9738b467f..78a793de9 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -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)