1// Copyright 2020 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 "strings" 19 20 "github.com/google/blueprint" 21 "github.com/google/blueprint/proptools" 22 23 "android/soong/android" 24 "android/soong/cc" 25 cc_config "android/soong/cc/config" 26) 27 28var ( 29 defaultBindgenFlags = []string{""} 30 31 // bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures. 32 bindgenClangVersion = "clang-r530567" 33 34 _ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string { 35 if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" { 36 return override 37 } 38 return bindgenClangVersion 39 }) 40 41 //TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen. 42 _ = pctx.HostBinToolVariable("bindgenCmd", "bindgen") 43 _ = pctx.VariableFunc("bindgenHostPrebuiltTag", func(ctx android.PackageVarContext) string { 44 if ctx.Config().UseHostMusl() { 45 // This is a hack to use the glibc bindgen binary until we have a musl version checked in. 46 return "linux-x86" 47 } else { 48 return "${config.HostPrebuiltTag}" 49 } 50 }) 51 _ = pctx.VariableFunc("bindgenClangLibdir", func(ctx android.PackageVarContext) string { 52 if ctx.Config().UseHostMusl() { 53 return "musl/lib/" 54 } else { 55 return "lib/" 56 } 57 }) 58 _ = pctx.SourcePathVariable("bindgenClang", 59 "${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/bin/clang") 60 _ = pctx.SourcePathVariable("bindgenLibClang", 61 "${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/${bindgenClangLibdir}") 62 63 //TODO(ivanlozano) Switch this to RuleBuilder 64 // 65 //TODO Pass the flag files directly to bindgen e.g. with @file when it supports that. 66 //See https://github.com/rust-lang/rust-bindgen/issues/2508. 67 bindgen = pctx.AndroidStaticRule("bindgen", 68 blueprint.RuleParams{ 69 Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " + 70 "$cmd $flags $$(cat $flagfiles) $in -o $out -- -MD -MF $out.d $cflags", 71 CommandDeps: []string{"$cmd"}, 72 Deps: blueprint.DepsGCC, 73 Depfile: "$out.d", 74 }, 75 "cmd", "flags", "flagfiles", "cflags") 76) 77 78func init() { 79 android.RegisterModuleType("rust_bindgen", RustBindgenFactory) 80 android.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory) 81} 82 83var _ SourceProvider = (*bindgenDecorator)(nil) 84 85type BindgenProperties struct { 86 // The wrapper header file. By default this is assumed to be a C header unless the extension is ".hh" or ".hpp". 87 // This is used to specify how to interpret the header and determines which '-std' flag to use by default. 88 // 89 // If your C++ header must have some other extension, then the default behavior can be overridden by setting the 90 // cpp_std property. 91 Wrapper_src *string `android:"path,arch_variant"` 92 93 // list of bindgen-specific flags and options 94 Bindgen_flags []string `android:"arch_variant"` 95 96 // list of files containing extra bindgen flags 97 Bindgen_flag_files []string `android:"arch_variant"` 98 99 // module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom 100 // binary must expect arguments in a similar fashion to bindgen, e.g. 101 // 102 // "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]" 103 Custom_bindgen string 104 105 // flag to indicate if bindgen should handle `static inline` functions (default is false). 106 // If true, Static_inline_library must be set. 107 Handle_static_inline *bool 108 109 // module name of the corresponding cc_library_static which includes the static_inline wrapper 110 // generated functions from bindgen. Must be used together with handle_static_inline. 111 // 112 // If there are no static inline functions provided through the header file, 113 // then bindgen (as of 0.69.2) will silently fail to output a .c file, and 114 // the cc_library_static depending on this module will fail compilation. 115 Static_inline_library *string 116} 117 118type bindgenDecorator struct { 119 *BaseSourceProvider 120 121 Properties BindgenProperties 122 ClangProperties cc.RustBindgenClangProperties 123} 124 125func (b *bindgenDecorator) getStdVersion(ctx ModuleContext, src android.Path) (string, bool) { 126 // Assume headers are C headers 127 isCpp := false 128 stdVersion := "" 129 130 switch src.Ext() { 131 case ".hpp", ".hh": 132 isCpp = true 133 } 134 135 if String(b.ClangProperties.Cpp_std) != "" && String(b.ClangProperties.C_std) != "" { 136 ctx.PropertyErrorf("c_std", "c_std and cpp_std cannot both be defined at the same time.") 137 } 138 139 if b.ClangProperties.Cpp_std != nil { 140 isCpp = true 141 if String(b.ClangProperties.Cpp_std) == "experimental" { 142 stdVersion = cc_config.ExperimentalCppStdVersion 143 } else if String(b.ClangProperties.Cpp_std) == "default" || String(b.ClangProperties.Cpp_std) == "" { 144 stdVersion = cc_config.CppStdVersion 145 } else { 146 stdVersion = String(b.ClangProperties.Cpp_std) 147 } 148 } else if b.ClangProperties.C_std != nil { 149 isCpp = false 150 if String(b.ClangProperties.C_std) == "experimental" { 151 stdVersion = cc_config.ExperimentalCStdVersion 152 } else if String(b.ClangProperties.C_std) == "default" || String(b.ClangProperties.C_std) == "" { 153 stdVersion = cc_config.CStdVersion 154 } else { 155 stdVersion = String(b.ClangProperties.C_std) 156 } 157 } else if isCpp { 158 stdVersion = cc_config.CppStdVersion 159 } else { 160 stdVersion = cc_config.CStdVersion 161 } 162 163 return stdVersion, isCpp 164} 165 166func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path { 167 ccToolchain := ctx.RustModule().ccToolchain(ctx) 168 169 var cflags []string 170 var implicits android.Paths 171 var implicitOutputs android.WritablePaths 172 var validations android.Paths 173 174 if Bool(b.Properties.Handle_static_inline) && b.Properties.Static_inline_library == nil { 175 ctx.PropertyErrorf("handle_static_inline", 176 "requires declaring static_inline_library to the corresponding cc_library module that includes the generated C source from bindgen.") 177 } 178 179 if b.Properties.Static_inline_library != nil && !Bool(b.Properties.Handle_static_inline) { 180 ctx.PropertyErrorf("static_inline_library", 181 "requires declaring handle_static_inline.") 182 } 183 184 implicits = append(implicits, deps.depGeneratedHeaders...) 185 186 // Default clang flags 187 cflags = append(cflags, "${cc_config.CommonGlobalCflags}") 188 if ctx.Device() { 189 cflags = append(cflags, "${cc_config.DeviceGlobalCflags}", "-nostdlibinc") 190 } 191 192 // Toolchain clang flags 193 cflags = append(cflags, "-target "+ccToolchain.ClangTriple()) 194 cflags = append(cflags, strings.ReplaceAll(ccToolchain.Cflags(), "${config.", "${cc_config.")) 195 cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainCflags(), "${config.", "${cc_config.")) 196 197 if ctx.RustModule().InVendorOrProduct() { 198 cflags = append(cflags, "-D__ANDROID_VNDK__") 199 if ctx.RustModule().InVendor() { 200 cflags = append(cflags, "-D__ANDROID_VENDOR__") 201 } else if ctx.RustModule().InProduct() { 202 cflags = append(cflags, "-D__ANDROID_PRODUCT__") 203 } 204 205 // Define __ANDROID_VENDOR_API__ for both product and vendor variants 206 // because they both use the same LLNDK libraries. 207 vendorApiLevel := ctx.Config().VendorApiLevel() 208 if vendorApiLevel == "" { 209 // TODO(b/314036847): This is a fallback for UDC targets. 210 // This must be a build failure when UDC is no longer built 211 // from this source tree. 212 vendorApiLevel = ctx.Config().PlatformSdkVersion().String() 213 } 214 cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel) 215 } 216 217 if ctx.RustModule().InRecovery() { 218 cflags = append(cflags, "-D__ANDROID_RECOVERY__") 219 } 220 221 if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" { 222 cflags = append(cflags, "-D__ANDROID_APEX__") 223 } 224 225 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 226 cflags = append(cflags, "-D__ANDROID_NATIVE_BRIDGE__") 227 } 228 229 // Dependency clang flags and include paths 230 cflags = append(cflags, deps.depClangFlags...) 231 for _, include := range deps.depIncludePaths { 232 cflags = append(cflags, "-I"+include.String()) 233 } 234 for _, include := range deps.depSystemIncludePaths { 235 cflags = append(cflags, "-isystem "+include.String()) 236 } 237 238 esc := proptools.NinjaAndShellEscapeList 239 240 // Filter out invalid cflags 241 cflagsProp := b.ClangProperties.Cflags.GetOrDefault(ctx, nil) 242 for _, flag := range cflagsProp { 243 if flag == "-x c++" || flag == "-xc++" { 244 ctx.PropertyErrorf("cflags", 245 "-x c++ should not be specified in cflags; setting cpp_std specifies this is a C++ header, or change the file extension to '.hpp' or '.hh'") 246 } 247 if strings.HasPrefix(flag, "-std=") { 248 ctx.PropertyErrorf("cflags", 249 "-std should not be specified in cflags; instead use c_std or cpp_std") 250 } 251 } 252 253 // Module defined clang flags and include paths 254 cflags = append(cflags, esc(cflagsProp)...) 255 for _, include := range b.ClangProperties.Local_include_dirs.GetOrDefault(ctx, nil) { 256 cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String()) 257 implicits = append(implicits, android.PathForModuleSrc(ctx, include)) 258 } 259 260 bindgenFlags := defaultBindgenFlags 261 bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...) 262 if Bool(b.Properties.Handle_static_inline) { 263 outputStaticFnsFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".c") 264 implicitOutputs = append(implicitOutputs, outputStaticFnsFile) 265 validations = append(validations, outputStaticFnsFile) 266 bindgenFlags = append(bindgenFlags, []string{"--experimental", "--wrap-static-fns", "--wrap-static-fns-path=" + outputStaticFnsFile.String()}...) 267 } 268 269 // cat reads from stdin if its command line is empty, 270 // so we pass in /dev/null if there are no other flag files 271 bindgenFlagFiles := []string{"/dev/null"} 272 for _, flagFile := range b.Properties.Bindgen_flag_files { 273 bindgenFlagFiles = append(bindgenFlagFiles, android.PathForModuleSrc(ctx, flagFile).String()) 274 implicits = append(implicits, android.PathForModuleSrc(ctx, flagFile)) 275 } 276 277 wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src) 278 if !wrapperFile.Valid() { 279 ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source") 280 } 281 282 // Add C std version flag 283 stdVersion, isCpp := b.getStdVersion(ctx, wrapperFile.Path()) 284 cflags = append(cflags, "-std="+stdVersion) 285 286 // Specify the header source language to avoid ambiguity. 287 if isCpp { 288 cflags = append(cflags, "-x c++") 289 // Add any C++ only flags. 290 cflags = append(cflags, esc(b.ClangProperties.Cppflags.GetOrDefault(ctx, nil))...) 291 } else { 292 cflags = append(cflags, "-x c") 293 } 294 295 // clang-r468909b complains about the -x c in the flags in clang-sys parse_search_paths: 296 // clang: error: '-x c' after last input file has no effect [-Werror,-Wunused-command-line-argument] 297 cflags = append(cflags, "-Wno-unused-command-line-argument") 298 299 // The Clang version used by CXX can be newer than the one used by Bindgen, and uses warning related flags that 300 // it cannot recognize. Turn off unknown warning flags warning. 301 cflags = append(cflags, "-Wno-unknown-warning-option") 302 303 // Suppress warnings while testing a new compiler. 304 if ctx.Config().IsEnvTrue("LLVM_NEXT") { 305 cflags = append(cflags, "-Wno-everything") 306 } 307 308 outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs") 309 310 var cmd, cmdDesc string 311 if b.Properties.Custom_bindgen != "" { 312 cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(android.HostToolProvider).HostToolPath().String() 313 cmdDesc = b.Properties.Custom_bindgen 314 } else { 315 cmd = "$bindgenCmd" 316 cmdDesc = "bindgen" 317 } 318 319 ctx.Build(pctx, android.BuildParams{ 320 Rule: bindgen, 321 Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "), 322 Output: outputFile, 323 Input: wrapperFile.Path(), 324 Implicits: implicits, 325 ImplicitOutputs: implicitOutputs, 326 Validations: validations, 327 Args: map[string]string{ 328 "cmd": cmd, 329 "flags": strings.Join(bindgenFlags, " "), 330 "flagfiles": strings.Join(bindgenFlagFiles, " "), 331 "cflags": strings.Join(cflags, " "), 332 }, 333 }) 334 335 b.BaseSourceProvider.OutputFiles = android.Paths{outputFile} 336 337 // Append any additional implicit outputs after the entry point source. 338 // We append any generated .c file here so it can picked up by cc_library_static modules. 339 // Those CC modules need to be sure not to pass any included .rs files to Clang. 340 // We don't have to worry about the additional .c files for Rust modules as only the entry point 341 // is passed to rustc. 342 b.BaseSourceProvider.OutputFiles = append(b.BaseSourceProvider.OutputFiles, implicitOutputs.Paths()...) 343 344 return outputFile 345} 346 347func (b *bindgenDecorator) SourceProviderProps() []interface{} { 348 return append(b.BaseSourceProvider.SourceProviderProps(), 349 &b.Properties, &b.ClangProperties) 350} 351 352// rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input. 353// Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure 354// the header and generated source is appropriately handled. It is recommended to add it as a dependency in the 355// rlibs or rustlibs property. It may also be added in the srcs property for external crates, using the ":" 356// prefix. 357func RustBindgenFactory() android.Module { 358 module, _ := NewRustBindgen(android.HostAndDeviceSupported) 359 return module.Init() 360} 361 362func RustBindgenHostFactory() android.Module { 363 module, _ := NewRustBindgen(android.HostSupported) 364 return module.Init() 365} 366 367func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) { 368 bindgen := &bindgenDecorator{ 369 BaseSourceProvider: NewSourceProvider(), 370 Properties: BindgenProperties{}, 371 ClangProperties: cc.RustBindgenClangProperties{}, 372 } 373 374 module := NewSourceProviderModule(hod, bindgen, false, false) 375 376 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 377 type stub_props struct { 378 Visibility []string 379 } 380 props := &stub_props{[]string{":__subpackages__"}} 381 ctx.PrependProperties(props) 382 }) 383 384 return module, bindgen 385} 386 387func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps { 388 deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps) 389 if ctx.toolchain().Bionic() && !ctx.RustModule().compiler.noStdlibs() { 390 deps = bionicDeps(ctx, deps, false) 391 } else if ctx.Os() == android.LinuxMusl { 392 deps = muslDeps(ctx, deps, false) 393 } 394 395 if !ctx.RustModule().Source() && b.Properties.Static_inline_library != nil { 396 // This is not the source variant, so add the static inline library as a dependency. 397 // 398 // This is necessary to avoid a circular dependency between the source variant and the 399 // dependent cc module. 400 deps.StaticLibs = append(deps.StaticLibs, String(b.Properties.Static_inline_library)) 401 } 402 403 deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs.GetOrDefault(ctx, nil)...) 404 deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs.GetOrDefault(ctx, nil)...) 405 deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs.GetOrDefault(ctx, nil)...) 406 return deps 407} 408