| // Copyright 2019 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 ( |
| "fmt" |
| "path/filepath" |
| "strings" |
| |
| "github.com/google/blueprint" |
| |
| "android/soong/android" |
| "android/soong/rust/config" |
| ) |
| |
| var ( |
| zip = pctx.AndroidStaticRule("zip", |
| blueprint.RuleParams{ |
| Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp", |
| CommandDeps: []string{"${SoongZipCmd}"}, |
| Rspfile: "$out.rsp", |
| RspfileContent: "$in", |
| }) |
| |
| cpDir = pctx.AndroidStaticRule("cpDir", |
| blueprint.RuleParams{ |
| Command: "cp `cat $outDir.rsp` $outDir", |
| Rspfile: "${outDir}.rsp", |
| RspfileContent: "$in", |
| }, |
| "outDir") |
| |
| cp = pctx.AndroidStaticRule("cp", |
| blueprint.RuleParams{ |
| Command: "rm -f $out && cp $in $out", |
| Description: "cp $out", |
| }) |
| |
| // Cross-referencing: |
| _ = pctx.SourcePathVariable("rustExtractor", |
| "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/rust_extractor") |
| _ = pctx.VariableFunc("kytheCorpus", |
| func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() }) |
| _ = pctx.VariableFunc("kytheCuEncoding", |
| func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() }) |
| ) |
| |
| type buildOutput struct { |
| outputFile android.Path |
| kytheFile android.Path |
| } |
| |
| func init() { |
| pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") |
| } |
| |
| func TransformSrcToBinary(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags, |
| outputFile android.WritablePath) buildOutput { |
| flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") |
| |
| return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "bin") |
| } |
| |
| func TransformSrctoRlib(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags, |
| outputFile android.WritablePath) buildOutput { |
| return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "rlib") |
| } |
| |
| func TransformSrctoDylib(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags, |
| outputFile android.WritablePath) buildOutput { |
| flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") |
| |
| return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "dylib") |
| } |
| |
| func TransformSrctoStatic(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags, |
| outputFile android.WritablePath) buildOutput { |
| flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") |
| return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "staticlib") |
| } |
| |
| func TransformSrctoShared(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags, |
| outputFile android.WritablePath) buildOutput { |
| flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") |
| return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "cdylib") |
| } |
| |
| func TransformSrctoProcMacro(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, |
| flags Flags, outputFile android.WritablePath) buildOutput { |
| return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "proc-macro") |
| } |
| |
| func rustLibsToPaths(libs RustLibraries) android.Paths { |
| var paths android.Paths |
| for _, lib := range libs { |
| paths = append(paths, lib.Path) |
| } |
| return paths |
| } |
| |
| func makeLibFlags(deps PathDeps, ruleCmd *android.RuleBuilderCommand) []string { |
| var libFlags []string |
| |
| // Collect library/crate flags |
| for _, lib := range deps.Rlibs.ToListDirect() { |
| libPath := ruleCmd.PathForInput(lib.Path) |
| libFlags = append(libFlags, "--extern "+lib.CrateName+"="+libPath) |
| } |
| for _, lib := range deps.Dylibs.ToListDirect() { |
| libPath := ruleCmd.PathForInput(lib.Path) |
| libFlags = append(libFlags, "--extern "+lib.CrateName+"="+libPath) |
| } |
| for _, procMacro := range deps.ProcMacros.ToListDirect() { |
| procMacroPath := ruleCmd.PathForInput(procMacro.Path) |
| libFlags = append(libFlags, "--extern "+procMacro.CrateName+"="+procMacroPath) |
| } |
| |
| for _, path := range deps.linkDirs { |
| libFlags = append(libFlags, "-L "+ruleCmd.PathForInput(path)) |
| } |
| |
| return libFlags |
| } |
| |
| func collectImplicits(deps PathDeps) android.Paths { |
| depPaths := android.Paths{} |
| depPaths = append(depPaths, rustLibsToPaths(deps.Rlibs.ToList())...) |
| depPaths = append(depPaths, rustLibsToPaths(deps.Dylibs.ToList())...) |
| depPaths = append(depPaths, rustLibsToPaths(deps.ProcMacros.ToList())...) |
| depPaths = append(depPaths, deps.AfdoProfiles...) |
| depPaths = append(depPaths, deps.WholeStaticLibs...) |
| depPaths = append(depPaths, deps.SrcDeps...) |
| depPaths = append(depPaths, deps.srcProviderFiles...) |
| depPaths = append(depPaths, deps.LibDeps...) |
| depPaths = append(depPaths, deps.linkObjects...) |
| depPaths = append(depPaths, deps.BuildToolSrcDeps...) |
| return depPaths |
| } |
| |
| func rustEnvVars(ctx ModuleContext, deps PathDeps, cmd *android.RuleBuilderCommand) []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 outDir string |
| if filepath.IsAbs(moduleGenDir.String()) { |
| // If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything. |
| outDir = moduleGenDir.String() |
| } else if moduleGenDir.Valid() { |
| // If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/') |
| outDir = filepath.Join("$$PWD/", cmd.PathForInput(moduleGenDir.Path())) |
| } else { |
| outDir = "$$PWD/" |
| } |
| envVars = append(envVars, "OUT_DIR="+outDir) |
| } else { |
| // TODO(pcc): Change this to "OUT_DIR=" after fixing crates to not rely on this value. |
| envVars = append(envVars, "OUT_DIR=out") |
| } |
| |
| envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx)) |
| |
| if ctx.RustModule().compiler.CargoEnvCompat() { |
| if bin, ok := ctx.RustModule().compiler.(*binaryDecorator); ok { |
| envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx)) |
| } |
| envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName()) |
| envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName()) |
| pkgVersion := ctx.RustModule().compiler.CargoPkgVersion() |
| if pkgVersion != "" { |
| envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion) |
| |
| // Ensure the version is in the form of "x.y.z" (approximately semver compliant). |
| // |
| // For our purposes, we don't care to enforce that these are integers since they may |
| // include other characters at times (e.g. sometimes the patch version is more than an integer). |
| if strings.Count(pkgVersion, ".") == 2 { |
| var semver_parts = strings.Split(pkgVersion, ".") |
| envVars = append(envVars, "CARGO_PKG_VERSION_MAJOR="+semver_parts[0]) |
| envVars = append(envVars, "CARGO_PKG_VERSION_MINOR="+semver_parts[1]) |
| envVars = append(envVars, "CARGO_PKG_VERSION_PATCH="+semver_parts[2]) |
| } |
| } |
| } |
| |
| envVars = append(envVars, "AR="+cmd.PathForTool(deps.Llvm_ar)) |
| |
| if ctx.Darwin() { |
| envVars = append(envVars, "ANDROID_RUST_DARWIN=true") |
| } |
| |
| return envVars |
| } |
| |
| func transformSrctoCrate(ctx ModuleContext, comp compiler, main android.Path, deps PathDeps, flags Flags, |
| outputFile android.WritablePath, crateType string) buildOutput { |
| |
| var inputs android.Paths |
| var output buildOutput |
| var rustcFlags, linkFlags []string |
| var earlyLinkFlags []string |
| |
| output.outputFile = outputFile |
| crateName := ctx.RustModule().CrateName() |
| targetTriple := ctx.toolchain().RustTriple() |
| |
| inputs = append(inputs, main) |
| |
| // Collect rustc flags |
| rustcFlags = append(rustcFlags, flags.GlobalRustFlags...) |
| rustcFlags = append(rustcFlags, flags.RustFlags...) |
| rustcFlags = append(rustcFlags, "--crate-type="+crateType) |
| if crateName != "" { |
| rustcFlags = append(rustcFlags, "--crate-name="+crateName) |
| } |
| if targetTriple != "" { |
| rustcFlags = append(rustcFlags, "--target="+targetTriple) |
| linkFlags = append(linkFlags, "-target "+targetTriple) |
| } |
| |
| // Suppress an implicit sysroot |
| rustcFlags = append(rustcFlags, "--sysroot=/dev/null") |
| |
| // Enable incremental compilation if requested by user |
| if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") { |
| incrementalPath := android.PathForOutput(ctx, "rustc").String() |
| rustcFlags = append(rustcFlags, "-Cincremental="+incrementalPath) |
| } |
| |
| // Disallow experimental features |
| modulePath := android.PathForModuleSrc(ctx).String() |
| if !(android.IsThirdPartyPath(modulePath) || strings.HasPrefix(modulePath, "prebuilts")) { |
| rustcFlags = append(rustcFlags, "-Zallow-features=\"\"") |
| } |
| |
| // Collect linker flags |
| if !ctx.Darwin() { |
| earlyLinkFlags = append(earlyLinkFlags, "-Wl,--as-needed") |
| } |
| |
| // Collect dependencies |
| var linkImplicits android.Paths |
| implicits := collectImplicits(deps) |
| toolImplicits := android.Concat(deps.BuildToolDeps) |
| linkImplicits = append(linkImplicits, deps.CrtBegin...) |
| linkImplicits = append(linkImplicits, deps.CrtEnd...) |
| implicits = append(implicits, comp.compilationSourcesAndData(ctx)...) |
| |
| if len(deps.SrcDeps) > 0 { |
| moduleGenDir := ctx.RustModule().compiler.CargoOutDir() |
| var outputs android.WritablePaths |
| |
| for _, genSrc := range deps.SrcDeps { |
| if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { |
| ctx.PropertyErrorf("srcs", |
| "multiple source providers generate the same filename output: "+genSrc.Base()) |
| } |
| outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) |
| } |
| |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: cpDir, |
| Description: "cp " + moduleGenDir.Path().Rel(), |
| Outputs: outputs, |
| Inputs: deps.SrcDeps, |
| Args: map[string]string{ |
| "outDir": moduleGenDir.String(), |
| }, |
| }) |
| implicits = append(implicits, outputs.Paths()...) |
| } |
| |
| if flags.Clippy { |
| // TODO(b/298461712) remove this hack to let slim manifest branches build |
| if deps.Clippy_driver == nil { |
| deps.Clippy_driver = config.RustPath(ctx, "bin/clippy-driver") |
| } |
| |
| clippyRule := getRuleBuilder(ctx, pctx, false, "clippy") |
| clippyCmd := clippyRule.Command() |
| clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") |
| clippyDepInfoFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy.d.raw") |
| clippyDepFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy.d") |
| |
| clippyCmd. |
| Flags(rustEnvVars(ctx, deps, clippyCmd)). |
| Tool(deps.Clippy_driver). |
| Flag("--emit metadata"). |
| FlagWithOutput("-o ", clippyFile). |
| FlagWithOutput("--emit dep-info=", clippyDepInfoFile). |
| Inputs(inputs). |
| Flags(makeLibFlags(deps, clippyCmd)). |
| Flags(rustcFlags). |
| Flags(flags.ClippyFlags). |
| ImplicitTools(toolImplicits). |
| Implicits(implicits) |
| |
| depfileCreationCmd := clippyRule.Command() |
| depfileCreationCmd. |
| Flag(fmt.Sprintf( |
| `grep "^%s:" %s >`, |
| depfileCreationCmd.PathForOutput(clippyFile), |
| depfileCreationCmd.PathForOutput(clippyDepInfoFile), |
| )). |
| DepFile(clippyDepFile) |
| |
| clippyRule.BuildWithUnescapedNinjaVars("clippy", "clippy "+main.Rel()) |
| |
| // Declare the clippy build as an implicit dependency of the original crate. |
| implicits = append(implicits, clippyFile) |
| } |
| |
| sboxDirectory := "rustc" |
| rustSboxOutputFile := android.PathForModuleOut(ctx, sboxDirectory, outputFile.Base()) |
| depFile := android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".d") |
| depInfoFile := android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".d.raw") |
| var rustcImplicitOutputs android.WritablePaths |
| |
| sandboxedCompilation := comp.crateRoot(ctx) != nil |
| rustcRule := getRuleBuilder(ctx, pctx, sandboxedCompilation, sboxDirectory) |
| rustcCmd := rustcRule.Command() |
| |
| linkFlags = append(linkFlags, flags.GlobalLinkFlags...) |
| linkFlags = append(linkFlags, flags.LinkFlags...) |
| linkFlags = append(linkFlags, rustcCmd.PathsForInputs(deps.linkObjects)...) |
| |
| // Check if this module needs to use the bootstrap linker |
| if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() { |
| dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker" |
| if ctx.toolchain().Is64Bit() { |
| dynamicLinker += "64" |
| } |
| linkFlags = append(linkFlags, dynamicLinker) |
| } |
| |
| libFlags := makeLibFlags(deps, rustcCmd) |
| |
| usesLinker := crateType == "bin" || crateType == "dylib" || crateType == "cdylib" || crateType == "proc-macro" |
| if usesLinker { |
| rustSboxOutputFile = android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".rsp") |
| rustcImplicitOutputs = android.WritablePaths{ |
| android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".whole.a"), |
| android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".a"), |
| } |
| } |
| |
| // TODO(b/298461712) remove this hack to let slim manifest branches build |
| if deps.Rustc == nil { |
| deps.Rustc = config.RustPath(ctx, "bin/rustc") |
| } |
| |
| rustcCmd. |
| Flags(rustEnvVars(ctx, deps, rustcCmd)). |
| Tool(deps.Rustc). |
| FlagWithInput("-C linker=", android.PathForSource(ctx, "build", "soong", "scripts", "mkcratersp.py")). |
| Flag("--emit link"). |
| Flag("-o"). |
| Output(rustSboxOutputFile). |
| FlagWithOutput("--emit dep-info=", depInfoFile). |
| Inputs(inputs). |
| Flags(libFlags). |
| ImplicitTools(toolImplicits). |
| Implicits(implicits). |
| Flags(rustcFlags). |
| ImplicitOutputs(rustcImplicitOutputs) |
| |
| depfileCreationCmd := rustcRule.Command() |
| depfileCreationCmd. |
| Flag(fmt.Sprintf( |
| `grep "^%s:" %s >`, |
| depfileCreationCmd.PathForOutput(rustSboxOutputFile), |
| depfileCreationCmd.PathForOutput(depInfoFile), |
| )). |
| DepFile(depFile) |
| |
| if !usesLinker { |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: cp, |
| Input: rustSboxOutputFile, |
| Output: outputFile, |
| }) |
| } else { |
| // TODO: delmerico - separate rustLink into its own rule |
| // mkcratersp.py hardcodes paths to files within the sandbox, so |
| // those need to be renamed/symlinked to something in the rustLink sandbox |
| // if we want to separate the rules |
| linkerSboxOutputFile := android.PathForModuleOut(ctx, sboxDirectory, outputFile.Base()) |
| rustLinkCmd := rustcRule.Command() |
| rustLinkCmd. |
| Tool(deps.Clang). |
| Flag("-o"). |
| Output(linkerSboxOutputFile). |
| Inputs(deps.CrtBegin). |
| Flags(earlyLinkFlags). |
| FlagWithInput("@", rustSboxOutputFile). |
| Flags(linkFlags). |
| Inputs(deps.CrtEnd). |
| ImplicitTools(toolImplicits). |
| Implicits(rustcImplicitOutputs.Paths()). |
| Implicits(implicits). |
| Implicits(linkImplicits) |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: cp, |
| Input: linkerSboxOutputFile, |
| Output: outputFile, |
| }) |
| } |
| |
| rustcRule.BuildWithUnescapedNinjaVars("rustc", "rustc "+main.Rel()) |
| |
| if flags.EmitXrefs { |
| kytheRule := getRuleBuilder(ctx, pctx, false, "kythe") |
| kytheCmd := kytheRule.Command() |
| kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") |
| kytheCmd. |
| Flag("KYTHE_CORPUS=${kytheCorpus}"). |
| FlagWithOutput("KYTHE_OUTPUT_FILE=", kytheFile). |
| FlagWithInput("KYTHE_VNAMES=", android.PathForSource(ctx, "build", "soong", "vnames.json")). |
| Flag("KYTHE_KZIP_ENCODING=${kytheCuEncoding}"). |
| Flag("KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative"). |
| Tool(ctx.Config().PrebuiltBuildTool(ctx, "rust_extractor")). |
| Flags(rustEnvVars(ctx, deps, kytheCmd)). |
| Tool(deps.Rustc). |
| Flag("-C linker=true"). |
| Inputs(inputs). |
| Flags(makeLibFlags(deps, kytheCmd)). |
| Flags(rustcFlags). |
| ImplicitTools(toolImplicits). |
| Implicits(implicits) |
| kytheRule.BuildWithUnescapedNinjaVars("kythe", "Xref Rust extractor "+main.Rel()) |
| output.kytheFile = kytheFile |
| } |
| return output |
| } |
| |
| func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags) android.ModuleOutPath { |
| // TODO(b/298461712) remove this hack to let slim manifest branches build |
| if deps.Rustdoc == nil { |
| deps.Rustdoc = config.RustPath(ctx, "bin/rustdoc") |
| } |
| |
| rustdocRule := getRuleBuilder(ctx, pctx, false, "rustdoc") |
| rustdocCmd := rustdocRule.Command() |
| |
| rustdocFlags := append([]string{}, flags.RustdocFlags...) |
| rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null") |
| |
| // Build an index for all our crates. -Z unstable options is required to use |
| // this flag. |
| rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page") |
| |
| targetTriple := ctx.toolchain().RustTriple() |
| |
| // Collect rustc flags |
| if targetTriple != "" { |
| rustdocFlags = append(rustdocFlags, "--target="+targetTriple) |
| } |
| |
| crateName := ctx.RustModule().CrateName() |
| rustdocFlags = append(rustdocFlags, "--crate-name "+crateName) |
| |
| rustdocFlags = append(rustdocFlags, makeLibFlags(deps, rustdocCmd)...) |
| docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp") |
| |
| // Silence warnings about renamed lints for third-party crates |
| modulePath := android.PathForModuleSrc(ctx).String() |
| if android.IsThirdPartyPath(modulePath) { |
| rustdocFlags = append(rustdocFlags, " -A warnings") |
| } |
| |
| // Yes, the same out directory is used simultaneously by all rustdoc builds. |
| // This is what cargo does. The docs for individual crates get generated to |
| // a subdirectory named for the crate, and rustdoc synchronizes writes to |
| // shared pieces like the index and search data itself. |
| // https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/render/write_shared.rs#L144-L146 |
| docDir := android.PathForOutput(ctx, "rustdoc") |
| |
| rustdocCmd. |
| Flags(rustEnvVars(ctx, deps, rustdocCmd)). |
| Tool(deps.Rustdoc). |
| Flags(rustdocFlags). |
| Input(main). |
| Flag("-o "+docDir.String()). |
| FlagWithOutput("&& touch ", docTimestampFile). |
| Implicit(ctx.RustModule().UnstrippedOutputFile()) |
| |
| rustdocRule.BuildWithUnescapedNinjaVars("rustdoc", "rustdoc "+main.Rel()) |
| return docTimestampFile |
| } |
| |
| func getRuleBuilder(ctx android.ModuleContext, pctx android.PackageContext, sbox bool, sboxDirectory string) *android.RuleBuilder { |
| r := android.NewRuleBuilder(pctx, ctx) |
| if sbox { |
| r = r.Sbox( |
| android.PathForModuleOut(ctx, sboxDirectory), |
| android.PathForModuleOut(ctx, sboxDirectory+".sbox.textproto"), |
| ).SandboxInputs() |
| } |
| return r |
| } |