// 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/proptools" "android/soong/android" "android/soong/rust/config" ) type RustLinkage int const ( DefaultLinkage RustLinkage = iota RlibLinkage DylibLinkage ) func (compiler *baseCompiler) edition() string { return proptools.StringDefault(compiler.Properties.Edition, config.DefaultEdition) } func (compiler *baseCompiler) setNoStdlibs() { compiler.Properties.No_stdlibs = proptools.BoolPtr(true) } func (compiler *baseCompiler) disableLints() { compiler.Properties.Lints = proptools.StringPtr("none") } func NewBaseCompiler(dir, dir64 string, location installLocation) *baseCompiler { return &baseCompiler{ Properties: BaseCompilerProperties{}, dir: dir, dir64: dir64, location: location, } } type installLocation int const ( InstallInSystem installLocation = 0 InstallInData = iota incorrectSourcesError = "srcs can only contain one path for a rust file and source providers prefixed by \":\"" genSubDir = "out/" ) type BaseCompilerProperties struct { // path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs). // Only a single source file can be defined. Modules which generate source can be included by prefixing // the module name with ":", for example ":libfoo_bindgen" // // If no source file is defined, a single generated source module can be defined to be used as the main source. Srcs []string `android:"path,arch_variant"` // name of the lint set that should be used to validate this module. // // Possible values are "default" (for using a sensible set of lints // depending on the module's location), "android" (for the strictest // lint set that applies to all Android platform code), "vendor" (for // a relaxed set) and "none" (for ignoring all lint warnings and // errors). The default value is "default". Lints *string // flags to pass to rustc. To enable configuration options or features, use the "cfgs" or "features" properties. Flags []string `android:"arch_variant"` // flags to pass to the linker Ld_flags []string `android:"arch_variant"` // list of rust rlib crate dependencies Rlibs []string `android:"arch_variant"` // list of rust dylib crate dependencies Dylibs []string `android:"arch_variant"` // list of rust automatic crate dependencies Rustlibs []string `android:"arch_variant"` // list of rust proc_macro crate dependencies Proc_macros []string `android:"arch_variant"` // list of C shared library dependencies Shared_libs []string `android:"arch_variant"` // list of C static library dependencies. These dependencies do not normally propagate to dependents // and may need to be redeclared. See whole_static_libs for bundling static dependencies into a library. Static_libs []string `android:"arch_variant"` // Similar to static_libs, but will bundle the static library dependency into a library. This is helpful // to avoid having to redeclare the dependency for dependents of this library, but in some cases may also // result in bloat if multiple dependencies all include the same static library whole. // // The common use case for this is when the static library is unlikely to be a dependency of other modules to avoid // having to redeclare the static library dependency for every dependent module. // If you are not sure what to, for rust_library modules most static dependencies should go in static_libraries, // and for rust_ffi modules most static dependencies should go into whole_static_libraries. // // For rust_ffi static variants, these libraries will be included in the resulting static library archive. // // For rust_library rlib variants, these libraries will be bundled into the resulting rlib library. This will // include all of the static libraries symbols in any dylibs or binaries which use this rlib as well. Whole_static_libs []string `android:"arch_variant"` // list of Rust system library dependencies. // // This is usually only needed when `no_stdlibs` is true, in which case it can be used to depend on system crates // like `core` and `alloc`. Stdlibs []string `android:"arch_variant"` // crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider // modules which create library variants (rust_bindgen). This must be the expected extern crate name used in // source, and is required to conform to an enforced format matching library output files (if the output file is // lib, the crate_name property must be ). Crate_name string `android:"arch_variant"` // list of features to enable for this crate Features []string `android:"arch_variant"` // list of configuration options to enable for this crate. To enable features, use the "features" property. Cfgs []string `android:"arch_variant"` // specific rust edition that should be used if the default version is not desired Edition *string `android:"arch_variant"` // sets name of the output Stem *string `android:"arch_variant"` // append to name of output Suffix *string `android:"arch_variant"` // install to a subdirectory of the default install path for the module Relative_install_path *string `android:"arch_variant"` // whether to suppress inclusion of standard crates - defaults to false No_stdlibs *bool // Change the rustlibs linkage to select rlib linkage by default for device targets. // Also link libstd as an rlib as well on device targets. // Note: This is the default behavior for host targets. // // This is primarily meant for rust_binary and rust_ffi modules where the default // linkage of libstd might need to be overridden in some use cases. This should // generally be avoided with other module types since it may cause collisions at // linkage if all dependencies of the root binary module do not link against libstd\ // the same way. Prefer_rlib *bool `android:"arch_variant"` // Enables emitting certain Cargo environment variables. Only intended to be used for compatibility purposes. // Will set CARGO_CRATE_NAME to the crate_name property's value. // Will set CARGO_BIN_NAME to the output filename value without the extension. Cargo_env_compat *bool // If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value. Cargo_pkg_version *string } type baseCompiler struct { Properties BaseCompilerProperties // Install related dir string dir64 string subDir string relative string path android.InstallPath location installLocation sanitize *sanitize distFile android.OptionalPath // unstripped output file. unstrippedOutputFile android.Path // stripped output file. strippedOutputFile android.OptionalPath // If a crate has a source-generated dependency, a copy of the source file // will be available in cargoOutDir (equivalent to Cargo OUT_DIR). cargoOutDir android.ModuleOutPath } func (compiler *baseCompiler) Disabled() bool { return false } func (compiler *baseCompiler) SetDisabled() { panic("baseCompiler does not implement SetDisabled()") } func (compiler *baseCompiler) coverageOutputZipPath() android.OptionalPath { panic("baseCompiler does not implement coverageOutputZipPath()") } func (compiler *baseCompiler) preferRlib() bool { return Bool(compiler.Properties.Prefer_rlib) } func (compiler *baseCompiler) stdLinkage(ctx *depsContext) RustLinkage { // For devices, we always link stdlibs in as dylibs by default. if compiler.preferRlib() { return RlibLinkage } else if ctx.Device() { return DylibLinkage } else { return RlibLinkage } } var _ compiler = (*baseCompiler)(nil) func (compiler *baseCompiler) inData() bool { return compiler.location == InstallInData } func (compiler *baseCompiler) compilerProps() []interface{} { return []interface{}{&compiler.Properties} } func (compiler *baseCompiler) cfgsToFlags() []string { flags := []string{} for _, cfg := range compiler.Properties.Cfgs { flags = append(flags, "--cfg '"+cfg+"'") } return flags } func (compiler *baseCompiler) featuresToFlags() []string { flags := []string{} for _, feature := range compiler.Properties.Features { flags = append(flags, "--cfg 'feature=\""+feature+"\"'") } return flags } func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags { flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...) flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...) return flags } func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags { if ctx.RustModule().UseVndk() { compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk") } flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...) flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...) return flags } func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags { lintFlags, err := config.RustcLintsForDir(ctx.ModuleDir(), compiler.Properties.Lints) if err != nil { ctx.PropertyErrorf("lints", err.Error()) } // linkage-related flags are disallowed. for _, s := range compiler.Properties.Ld_flags { if strings.HasPrefix(s, "-Wl,-l") || strings.HasPrefix(s, "-Wl,-L") { ctx.PropertyErrorf("ld_flags", "'-Wl,-l' and '-Wl,-L' flags cannot be manually specified") } } for _, s := range compiler.Properties.Flags { if strings.HasPrefix(s, "-l") || strings.HasPrefix(s, "-L") { ctx.PropertyErrorf("flags", "'-l' and '-L' flags cannot be manually specified") } if strings.HasPrefix(s, "--extern") { ctx.PropertyErrorf("flags", "'--extern' flag cannot be manually specified") } if strings.HasPrefix(s, "-Clink-args=") || strings.HasPrefix(s, "-C link-args=") { ctx.PropertyErrorf("flags", "'-C link-args' flag cannot be manually specified") } } flags.RustFlags = append(flags.RustFlags, lintFlags) flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...) 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()) flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags()) if ctx.Host() && !ctx.Windows() { rpathPrefix := `\$$ORIGIN/` if ctx.Darwin() { rpathPrefix = "@loader_path/" } var rpath string if ctx.toolchain().Is64Bit() { rpath = "lib64" } else { rpath = "lib" } flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+rpath) flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath) } return flags } func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path { 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) } func (compiler *baseCompiler) CargoOutDir() android.OptionalPath { return android.OptionalPathForPath(compiler.cargoOutDir) } func (compiler *baseCompiler) CargoEnvCompat() bool { return Bool(compiler.Properties.Cargo_env_compat) } func (compiler *baseCompiler) CargoPkgVersion() string { return String(compiler.Properties.Cargo_pkg_version) } func (compiler *baseCompiler) unstrippedOutputFilePath() android.Path { return compiler.unstrippedOutputFile } func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath { return compiler.strippedOutputFile } func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps { deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...) deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...) deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...) deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...) deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...) deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...) deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...) deps.Stdlibs = append(deps.Stdlibs, compiler.Properties.Stdlibs...) if !Bool(compiler.Properties.No_stdlibs) { for _, stdlib := range config.Stdlibs { // If we're building for the build host, use the prebuilt stdlibs if ctx.Target().Os == android.Linux || ctx.Target().Os == android.Darwin { stdlib = "prebuilt_" + stdlib } deps.Stdlibs = append(deps.Stdlibs, stdlib) } } return deps } func bionicDeps(ctx DepsContext, deps Deps, static bool) Deps { bionicLibs := []string{} bionicLibs = append(bionicLibs, "liblog") bionicLibs = append(bionicLibs, "libc") bionicLibs = append(bionicLibs, "libm") bionicLibs = append(bionicLibs, "libdl") if static { deps.StaticLibs = append(deps.StaticLibs, bionicLibs...) } else { deps.SharedLibs = append(deps.SharedLibs, bionicLibs...) } if ctx.RustModule().StaticExecutable() { deps.StaticLibs = append(deps.StaticLibs, "libunwind") } if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" { deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins) } return deps } func muslDeps(ctx DepsContext, deps Deps, static bool) Deps { muslLibs := []string{"libc_musl"} if static { deps.StaticLibs = append(deps.StaticLibs, muslLibs...) } else { deps.SharedLibs = append(deps.SharedLibs, muslLibs...) } if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" { deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins) } return deps } func (compiler *baseCompiler) crateName() string { return compiler.Properties.Crate_name } func (compiler *baseCompiler) everInstallable() bool { // Most modules are installable, so return true by default. return true } func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath { dir := compiler.dir if ctx.toolchain().Is64Bit() && compiler.dir64 != "" { dir = compiler.dir64 } if ctx.Target().NativeBridge == android.NativeBridgeEnabled { dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath) } if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) { dir = filepath.Join(dir, ctx.Arch().ArchType.String()) } if compiler.location == InstallInData && ctx.RustModule().UseVndk() { if ctx.RustModule().InProduct() { dir = filepath.Join(dir, "product") } else if ctx.RustModule().InVendor() { dir = filepath.Join(dir, "vendor") } else { ctx.ModuleErrorf("Unknown data+VNDK installation kind") } } return android.PathForModuleInstall(ctx, dir, compiler.subDir, compiler.relativeInstallPath(), compiler.relative) } func (compiler *baseCompiler) nativeCoverage() bool { return false } func (compiler *baseCompiler) install(ctx ModuleContext) { path := ctx.RustModule().OutputFile() compiler.path = ctx.InstallFile(compiler.installDir(ctx), path.Path().Base(), path.Path()) } func (compiler *baseCompiler) getStem(ctx ModuleContext) string { return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix) } func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string { stem := ctx.ModuleName() if String(compiler.Properties.Stem) != "" { stem = String(compiler.Properties.Stem) } return stem } func (compiler *baseCompiler) relativeInstallPath() string { return String(compiler.Properties.Relative_install_path) } // Returns the Path for the main source file along with Paths for generated source files from modules listed in srcs. func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) (android.Path, android.Paths) { if len(srcs) == 0 { ctx.PropertyErrorf("srcs", "srcs must not be empty") } // The srcs can contain strings with prefix ":". // They are dependent modules of this module, with android.SourceDepTag. // They are not the main source file compiled by rustc. numSrcs := 0 srcIndex := 0 for i, s := range srcs { if android.SrcIsModule(s) == "" { numSrcs++ srcIndex = i } } if numSrcs > 1 { ctx.PropertyErrorf("srcs", incorrectSourcesError) } // If a main source file is not provided we expect only a single SourceProvider module to be defined // within srcs, with the expectation that the first source it provides is the entry point. if srcIndex != 0 { ctx.PropertyErrorf("srcs", "main source file must be the first in srcs") } else if numSrcs > 1 { ctx.PropertyErrorf("srcs", "only a single generated source module can be defined without a main source file.") } paths := android.PathsForModuleSrc(ctx, srcs) return paths[srcIndex], paths[1:] }