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-r510928" 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}") 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 202 vendorApiLevel := ctx.Config().VendorApiLevel() 203 if vendorApiLevel == "" { 204 // TODO(b/314036847): This is a fallback for UDC targets. 205 // This must be a build failure when UDC is no longer built 206 // from this source tree. 207 vendorApiLevel = ctx.Config().PlatformSdkVersion().String() 208 } 209 cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel) 210 } else if ctx.RustModule().InProduct() { 211 cflags = append(cflags, "-D__ANDROID_PRODUCT__") 212 } 213 } 214 215 if ctx.RustModule().InRecovery() { 216 cflags = append(cflags, "-D__ANDROID_RECOVERY__") 217 } 218 219 if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" { 220 cflags = append(cflags, "-D__ANDROID_APEX__") 221 } 222 223 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 224 cflags = append(cflags, "-D__ANDROID_NATIVE_BRIDGE__") 225 } 226 227 // Dependency clang flags and include paths 228 cflags = append(cflags, deps.depClangFlags...) 229 for _, include := range deps.depIncludePaths { 230 cflags = append(cflags, "-I"+include.String()) 231 } 232 for _, include := range deps.depSystemIncludePaths { 233 cflags = append(cflags, "-isystem "+include.String()) 234 } 235 236 esc := proptools.NinjaAndShellEscapeList 237 238 // Filter out invalid cflags 239 cflagsProp := b.ClangProperties.Cflags.GetOrDefault(ctx, nil) 240 for _, flag := range cflagsProp { 241 if flag == "-x c++" || flag == "-xc++" { 242 ctx.PropertyErrorf("cflags", 243 "-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'") 244 } 245 if strings.HasPrefix(flag, "-std=") { 246 ctx.PropertyErrorf("cflags", 247 "-std should not be specified in cflags; instead use c_std or cpp_std") 248 } 249 } 250 251 // Module defined clang flags and include paths 252 cflags = append(cflags, esc(cflagsProp)...) 253 for _, include := range b.ClangProperties.Local_include_dirs { 254 cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String()) 255 implicits = append(implicits, android.PathForModuleSrc(ctx, include)) 256 } 257 258 bindgenFlags := defaultBindgenFlags 259 bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...) 260 if Bool(b.Properties.Handle_static_inline) { 261 outputStaticFnsFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".c") 262 implicitOutputs = append(implicitOutputs, outputStaticFnsFile) 263 validations = append(validations, outputStaticFnsFile) 264 bindgenFlags = append(bindgenFlags, []string{"--experimental", "--wrap-static-fns", "--wrap-static-fns-path=" + outputStaticFnsFile.String()}...) 265 } 266 267 // cat reads from stdin if its command line is empty, 268 // so we pass in /dev/null if there are no other flag files 269 bindgenFlagFiles := []string{"/dev/null"} 270 for _, flagFile := range b.Properties.Bindgen_flag_files { 271 bindgenFlagFiles = append(bindgenFlagFiles, android.PathForModuleSrc(ctx, flagFile).String()) 272 implicits = append(implicits, android.PathForModuleSrc(ctx, flagFile)) 273 } 274 275 wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src) 276 if !wrapperFile.Valid() { 277 ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source") 278 } 279 280 // Add C std version flag 281 stdVersion, isCpp := b.getStdVersion(ctx, wrapperFile.Path()) 282 cflags = append(cflags, "-std="+stdVersion) 283 284 // Specify the header source language to avoid ambiguity. 285 if isCpp { 286 cflags = append(cflags, "-x c++") 287 // Add any C++ only flags. 288 cflags = append(cflags, esc(b.ClangProperties.Cppflags)...) 289 } else { 290 cflags = append(cflags, "-x c") 291 } 292 293 // clang-r468909b complains about the -x c in the flags in clang-sys parse_search_paths: 294 // clang: error: '-x c' after last input file has no effect [-Werror,-Wunused-command-line-argument] 295 cflags = append(cflags, "-Wno-unused-command-line-argument") 296 297 // The Clang version used by CXX can be newer than the one used by Bindgen, and uses warning related flags that 298 // it cannot recognize. Turn off unknown warning flags warning. 299 cflags = append(cflags, "-Wno-unknown-warning-option") 300 301 outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs") 302 303 var cmd, cmdDesc string 304 if b.Properties.Custom_bindgen != "" { 305 cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(android.HostToolProvider).HostToolPath().String() 306 cmdDesc = b.Properties.Custom_bindgen 307 } else { 308 cmd = "$bindgenCmd" 309 cmdDesc = "bindgen" 310 } 311 312 ctx.Build(pctx, android.BuildParams{ 313 Rule: bindgen, 314 Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "), 315 Output: outputFile, 316 Input: wrapperFile.Path(), 317 Implicits: implicits, 318 ImplicitOutputs: implicitOutputs, 319 Validations: validations, 320 Args: map[string]string{ 321 "cmd": cmd, 322 "flags": strings.Join(bindgenFlags, " "), 323 "flagfiles": strings.Join(bindgenFlagFiles, " "), 324 "cflags": strings.Join(cflags, " "), 325 }, 326 }) 327 328 b.BaseSourceProvider.OutputFiles = android.Paths{outputFile} 329 330 // Append any additional implicit outputs after the entry point source. 331 // We append any generated .c file here so it can picked up by cc_library_static modules. 332 // Those CC modules need to be sure not to pass any included .rs files to Clang. 333 // We don't have to worry about the additional .c files for Rust modules as only the entry point 334 // is passed to rustc. 335 b.BaseSourceProvider.OutputFiles = append(b.BaseSourceProvider.OutputFiles, implicitOutputs.Paths()...) 336 337 return outputFile 338} 339 340func (b *bindgenDecorator) SourceProviderProps() []interface{} { 341 return append(b.BaseSourceProvider.SourceProviderProps(), 342 &b.Properties, &b.ClangProperties) 343} 344 345// rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input. 346// Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure 347// the header and generated source is appropriately handled. It is recommended to add it as a dependency in the 348// rlibs or rustlibs property. It may also be added in the srcs property for external crates, using the ":" 349// prefix. 350func RustBindgenFactory() android.Module { 351 module, _ := NewRustBindgen(android.HostAndDeviceSupported) 352 return module.Init() 353} 354 355func RustBindgenHostFactory() android.Module { 356 module, _ := NewRustBindgen(android.HostSupported) 357 return module.Init() 358} 359 360func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) { 361 bindgen := &bindgenDecorator{ 362 BaseSourceProvider: NewSourceProvider(), 363 Properties: BindgenProperties{}, 364 ClangProperties: cc.RustBindgenClangProperties{}, 365 } 366 367 module := NewSourceProviderModule(hod, bindgen, false, true) 368 369 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 370 type stub_props struct { 371 Visibility []string 372 } 373 props := &stub_props{[]string{":__subpackages__"}} 374 ctx.PrependProperties(props) 375 }) 376 377 return module, bindgen 378} 379 380func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps { 381 deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps) 382 if ctx.toolchain().Bionic() && !ctx.RustModule().compiler.noStdlibs() { 383 deps = bionicDeps(ctx, deps, false) 384 } else if ctx.Os() == android.LinuxMusl { 385 deps = muslDeps(ctx, deps, false) 386 } 387 388 if !ctx.RustModule().Source() && b.Properties.Static_inline_library != nil { 389 // This is not the source variant, so add the static inline library as a dependency. 390 // 391 // This is necessary to avoid a circular dependency between the source variant and the 392 // dependent cc module. 393 deps.StaticLibs = append(deps.StaticLibs, String(b.Properties.Static_inline_library)) 394 } 395 396 deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...) 397 deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...) 398 deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs...) 399 return deps 400} 401