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-r487747c" 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 bindgen = pctx.AndroidStaticRule("bindgen", 65 blueprint.RuleParams{ 66 Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " + 67 "$cmd $flags $in -o $out -- -MD -MF $out.d $cflags", 68 CommandDeps: []string{"$cmd"}, 69 Deps: blueprint.DepsGCC, 70 Depfile: "$out.d", 71 }, 72 "cmd", "flags", "cflags") 73) 74 75func init() { 76 android.RegisterModuleType("rust_bindgen", RustBindgenFactory) 77 android.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory) 78} 79 80var _ SourceProvider = (*bindgenDecorator)(nil) 81 82type BindgenProperties struct { 83 // The wrapper header file. By default this is assumed to be a C header unless the extension is ".hh" or ".hpp". 84 // This is used to specify how to interpret the header and determines which '-std' flag to use by default. 85 // 86 // If your C++ header must have some other extension, then the default behavior can be overridden by setting the 87 // cpp_std property. 88 Wrapper_src *string `android:"path,arch_variant"` 89 90 // list of bindgen-specific flags and options 91 Bindgen_flags []string `android:"arch_variant"` 92 93 // module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom 94 // binary must expect arguments in a similar fashion to bindgen, e.g. 95 // 96 // "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]" 97 Custom_bindgen string 98} 99 100type bindgenDecorator struct { 101 *BaseSourceProvider 102 103 Properties BindgenProperties 104 ClangProperties cc.RustBindgenClangProperties 105} 106 107func (b *bindgenDecorator) getStdVersion(ctx ModuleContext, src android.Path) (string, bool) { 108 // Assume headers are C headers 109 isCpp := false 110 stdVersion := "" 111 112 switch src.Ext() { 113 case ".hpp", ".hh": 114 isCpp = true 115 } 116 117 if String(b.ClangProperties.Cpp_std) != "" && String(b.ClangProperties.C_std) != "" { 118 ctx.PropertyErrorf("c_std", "c_std and cpp_std cannot both be defined at the same time.") 119 } 120 121 if String(b.ClangProperties.Cpp_std) != "" { 122 if String(b.ClangProperties.Cpp_std) == "experimental" { 123 stdVersion = cc_config.ExperimentalCppStdVersion 124 } else if String(b.ClangProperties.Cpp_std) == "default" { 125 stdVersion = cc_config.CppStdVersion 126 } else { 127 stdVersion = String(b.ClangProperties.Cpp_std) 128 } 129 } else if b.ClangProperties.C_std != nil { 130 if String(b.ClangProperties.C_std) == "experimental" { 131 stdVersion = cc_config.ExperimentalCStdVersion 132 } else if String(b.ClangProperties.C_std) == "default" { 133 stdVersion = cc_config.CStdVersion 134 } else { 135 stdVersion = String(b.ClangProperties.C_std) 136 } 137 } else if isCpp { 138 stdVersion = cc_config.CppStdVersion 139 } else { 140 stdVersion = cc_config.CStdVersion 141 } 142 143 return stdVersion, isCpp 144} 145 146func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path { 147 ccToolchain := ctx.RustModule().ccToolchain(ctx) 148 149 var cflags []string 150 var implicits android.Paths 151 152 implicits = append(implicits, deps.depGeneratedHeaders...) 153 154 // Default clang flags 155 cflags = append(cflags, "${cc_config.CommonGlobalCflags}") 156 if ctx.Device() { 157 cflags = append(cflags, "${cc_config.DeviceGlobalCflags}") 158 } 159 160 // Toolchain clang flags 161 cflags = append(cflags, "-target "+ccToolchain.ClangTriple()) 162 cflags = append(cflags, strings.ReplaceAll(ccToolchain.Cflags(), "${config.", "${cc_config.")) 163 cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainCflags(), "${config.", "${cc_config.")) 164 165 if ctx.RustModule().UseVndk() { 166 cflags = append(cflags, "-D__ANDROID_VNDK__") 167 if ctx.RustModule().InVendor() { 168 cflags = append(cflags, "-D__ANDROID_VENDOR__") 169 } else if ctx.RustModule().InProduct() { 170 cflags = append(cflags, "-D__ANDROID_PRODUCT__") 171 } 172 } 173 174 if ctx.RustModule().InRecovery() { 175 cflags = append(cflags, "-D__ANDROID_RECOVERY__") 176 } 177 178 if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" { 179 cflags = append(cflags, "-D__ANDROID_APEX__") 180 } 181 182 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 183 cflags = append(cflags, "-D__ANDROID_NATIVE_BRIDGE__") 184 } 185 186 // Dependency clang flags and include paths 187 cflags = append(cflags, deps.depClangFlags...) 188 for _, include := range deps.depIncludePaths { 189 cflags = append(cflags, "-I"+include.String()) 190 } 191 for _, include := range deps.depSystemIncludePaths { 192 cflags = append(cflags, "-isystem "+include.String()) 193 } 194 195 esc := proptools.NinjaAndShellEscapeList 196 197 // Filter out invalid cflags 198 for _, flag := range b.ClangProperties.Cflags { 199 if flag == "-x c++" || flag == "-xc++" { 200 ctx.PropertyErrorf("cflags", 201 "-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'") 202 } 203 if strings.HasPrefix(flag, "-std=") { 204 ctx.PropertyErrorf("cflags", 205 "-std should not be specified in cflags; instead use c_std or cpp_std") 206 } 207 } 208 209 // Module defined clang flags and include paths 210 cflags = append(cflags, esc(b.ClangProperties.Cflags)...) 211 for _, include := range b.ClangProperties.Local_include_dirs { 212 cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String()) 213 implicits = append(implicits, android.PathForModuleSrc(ctx, include)) 214 } 215 216 bindgenFlags := defaultBindgenFlags 217 bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...) 218 219 wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src) 220 if !wrapperFile.Valid() { 221 ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source") 222 } 223 224 // Add C std version flag 225 stdVersion, isCpp := b.getStdVersion(ctx, wrapperFile.Path()) 226 cflags = append(cflags, "-std="+stdVersion) 227 228 // Specify the header source language to avoid ambiguity. 229 if isCpp { 230 cflags = append(cflags, "-x c++") 231 // Add any C++ only flags. 232 cflags = append(cflags, esc(b.ClangProperties.Cppflags)...) 233 } else { 234 cflags = append(cflags, "-x c") 235 } 236 237 // clang-r468909b complains about the -x c in the flags in clang-sys parse_search_paths: 238 // clang: error: '-x c' after last input file has no effect [-Werror,-Wunused-command-line-argument] 239 cflags = append(cflags, "-Wno-unused-command-line-argument") 240 241 // LLVM_NEXT may contain flags that bindgen doesn't recognise. Turn off unknown flags warning. 242 if ctx.Config().IsEnvTrue("LLVM_NEXT") { 243 cflags = append(cflags, "-Wno-unknown-warning-option") 244 } 245 246 outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs") 247 248 var cmd, cmdDesc string 249 if b.Properties.Custom_bindgen != "" { 250 cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(*Module).HostToolPath().String() 251 cmdDesc = b.Properties.Custom_bindgen 252 } else { 253 cmd = "$bindgenCmd" 254 cmdDesc = "bindgen" 255 } 256 257 ctx.Build(pctx, android.BuildParams{ 258 Rule: bindgen, 259 Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "), 260 Output: outputFile, 261 Input: wrapperFile.Path(), 262 Implicits: implicits, 263 Args: map[string]string{ 264 "cmd": cmd, 265 "flags": strings.Join(bindgenFlags, " "), 266 "cflags": strings.Join(cflags, " "), 267 }, 268 }) 269 270 b.BaseSourceProvider.OutputFiles = android.Paths{outputFile} 271 return outputFile 272} 273 274func (b *bindgenDecorator) SourceProviderProps() []interface{} { 275 return append(b.BaseSourceProvider.SourceProviderProps(), 276 &b.Properties, &b.ClangProperties) 277} 278 279// rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input. 280// Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure 281// the header and generated source is appropriately handled. It is recommended to add it as a dependency in the 282// rlibs, dylibs or rustlibs property. It may also be added in the srcs property for external crates, using the ":" 283// prefix. 284func RustBindgenFactory() android.Module { 285 module, _ := NewRustBindgen(android.HostAndDeviceSupported) 286 return module.Init() 287} 288 289func RustBindgenHostFactory() android.Module { 290 module, _ := NewRustBindgen(android.HostSupported) 291 return module.Init() 292} 293 294func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) { 295 bindgen := &bindgenDecorator{ 296 BaseSourceProvider: NewSourceProvider(), 297 Properties: BindgenProperties{}, 298 ClangProperties: cc.RustBindgenClangProperties{}, 299 } 300 301 module := NewSourceProviderModule(hod, bindgen, false, true) 302 303 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 304 type stub_props struct { 305 Visibility []string 306 } 307 props := &stub_props{[]string{":__subpackages__"}} 308 ctx.PrependProperties(props) 309 }) 310 311 return module, bindgen 312} 313 314func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps { 315 deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps) 316 if ctx.toolchain().Bionic() && !ctx.RustModule().compiler.noStdlibs() { 317 deps = bionicDeps(ctx, deps, false) 318 } else if ctx.Os() == android.LinuxMusl { 319 deps = muslDeps(ctx, deps, false) 320 } 321 322 deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...) 323 deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...) 324 deps.HeaderLibs = append(deps.StaticLibs, b.ClangProperties.Header_libs...) 325 return deps 326} 327