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 "fmt" 19 "strings" 20 21 "github.com/google/blueprint" 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25 "android/soong/cc" 26 cc_config "android/soong/cc/config" 27) 28 29var ( 30 defaultBindgenFlags = []string{""} 31 32 // bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures. 33 bindgenClangVersion = "clang-r450784d" 34 35 _ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string { 36 if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" { 37 return override 38 } 39 return bindgenClangVersion 40 }) 41 42 //TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen. 43 _ = pctx.HostBinToolVariable("bindgenCmd", "bindgen") 44 _ = pctx.SourcePathVariable("bindgenClang", 45 "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/bin/clang") 46 _ = pctx.SourcePathVariable("bindgenLibClang", 47 "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/lib64/") 48 49 //TODO(ivanlozano) Switch this to RuleBuilder 50 bindgen = pctx.AndroidStaticRule("bindgen", 51 blueprint.RuleParams{ 52 Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " + 53 "$cmd $flags $in -o $out -- -MD -MF $out.d $cflags", 54 CommandDeps: []string{"$cmd"}, 55 Deps: blueprint.DepsGCC, 56 Depfile: "$out.d", 57 }, 58 "cmd", "flags", "cflags") 59) 60 61func init() { 62 android.RegisterModuleType("rust_bindgen", RustBindgenFactory) 63 android.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory) 64} 65 66var _ SourceProvider = (*bindgenDecorator)(nil) 67 68type BindgenProperties struct { 69 // The wrapper header file. By default this is assumed to be a C header unless the extension is ".hh" or ".hpp". 70 // This is used to specify how to interpret the header and determines which '-std' flag to use by default. 71 // 72 // If your C++ header must have some other extension, then the default behavior can be overridden by setting the 73 // cpp_std property. 74 Wrapper_src *string `android:"path,arch_variant"` 75 76 // list of bindgen-specific flags and options 77 Bindgen_flags []string `android:"arch_variant"` 78 79 // module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom 80 // binary must expect arguments in a similar fashion to bindgen, e.g. 81 // 82 // "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]" 83 Custom_bindgen string 84} 85 86type bindgenDecorator struct { 87 *BaseSourceProvider 88 89 Properties BindgenProperties 90 ClangProperties cc.RustBindgenClangProperties 91} 92 93func (b *bindgenDecorator) getStdVersion(ctx ModuleContext, src android.Path) (string, bool) { 94 // Assume headers are C headers 95 isCpp := false 96 stdVersion := "" 97 98 switch src.Ext() { 99 case ".hpp", ".hh": 100 isCpp = true 101 } 102 103 if String(b.ClangProperties.Cpp_std) != "" && String(b.ClangProperties.C_std) != "" { 104 ctx.PropertyErrorf("c_std", "c_std and cpp_std cannot both be defined at the same time.") 105 } 106 107 if String(b.ClangProperties.Cpp_std) != "" { 108 if String(b.ClangProperties.Cpp_std) == "experimental" { 109 stdVersion = cc_config.ExperimentalCppStdVersion 110 } else if String(b.ClangProperties.Cpp_std) == "default" { 111 stdVersion = cc_config.CppStdVersion 112 } else { 113 stdVersion = String(b.ClangProperties.Cpp_std) 114 } 115 } else if b.ClangProperties.C_std != nil { 116 if String(b.ClangProperties.C_std) == "experimental" { 117 stdVersion = cc_config.ExperimentalCStdVersion 118 } else if String(b.ClangProperties.C_std) == "default" { 119 stdVersion = cc_config.CStdVersion 120 } else { 121 stdVersion = String(b.ClangProperties.C_std) 122 } 123 } else if isCpp { 124 stdVersion = cc_config.CppStdVersion 125 } else { 126 stdVersion = cc_config.CStdVersion 127 } 128 129 return stdVersion, isCpp 130} 131 132func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path { 133 ccToolchain := ctx.RustModule().ccToolchain(ctx) 134 135 var cflags []string 136 var implicits android.Paths 137 138 implicits = append(implicits, deps.depGeneratedHeaders...) 139 140 // Default clang flags 141 cflags = append(cflags, "${cc_config.CommonGlobalCflags}") 142 if ctx.Device() { 143 cflags = append(cflags, "${cc_config.DeviceGlobalCflags}") 144 } 145 146 // Toolchain clang flags 147 cflags = append(cflags, "-target "+ccToolchain.ClangTriple()) 148 cflags = append(cflags, strings.ReplaceAll(ccToolchain.Cflags(), "${config.", "${cc_config.")) 149 cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainCflags(), "${config.", "${cc_config.")) 150 151 if ctx.RustModule().UseVndk() { 152 cflags = append(cflags, "-D__ANDROID_VNDK__") 153 if ctx.RustModule().InVendor() { 154 cflags = append(cflags, "-D__ANDROID_VENDOR__") 155 } else if ctx.RustModule().InProduct() { 156 cflags = append(cflags, "-D__ANDROID_PRODUCT__") 157 } 158 } 159 160 if ctx.RustModule().InRecovery() { 161 cflags = append(cflags, "-D__ANDROID_RECOVERY__") 162 } 163 164 if mctx, ok := ctx.(*moduleContext); ok && mctx.apexVariationName() != "" { 165 cflags = append(cflags, "-D__ANDROID_APEX__") 166 if ctx.Device() { 167 cflags = append(cflags, fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d", 168 ctx.RustModule().apexSdkVersion.FinalOrFutureInt())) 169 } 170 } 171 172 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 173 cflags = append(cflags, "-D__ANDROID_NATIVE_BRIDGE__") 174 } 175 176 // Dependency clang flags and include paths 177 cflags = append(cflags, deps.depClangFlags...) 178 for _, include := range deps.depIncludePaths { 179 cflags = append(cflags, "-I"+include.String()) 180 } 181 for _, include := range deps.depSystemIncludePaths { 182 cflags = append(cflags, "-isystem "+include.String()) 183 } 184 185 esc := proptools.NinjaAndShellEscapeList 186 187 // Filter out invalid cflags 188 for _, flag := range b.ClangProperties.Cflags { 189 if flag == "-x c++" || flag == "-xc++" { 190 ctx.PropertyErrorf("cflags", 191 "-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'") 192 } 193 if strings.HasPrefix(flag, "-std=") { 194 ctx.PropertyErrorf("cflags", 195 "-std should not be specified in cflags; instead use c_std or cpp_std") 196 } 197 } 198 199 // Module defined clang flags and include paths 200 cflags = append(cflags, esc(b.ClangProperties.Cflags)...) 201 for _, include := range b.ClangProperties.Local_include_dirs { 202 cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String()) 203 implicits = append(implicits, android.PathForModuleSrc(ctx, include)) 204 } 205 206 bindgenFlags := defaultBindgenFlags 207 bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...) 208 209 wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src) 210 if !wrapperFile.Valid() { 211 ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source") 212 } 213 214 // Add C std version flag 215 stdVersion, isCpp := b.getStdVersion(ctx, wrapperFile.Path()) 216 cflags = append(cflags, "-std="+stdVersion) 217 218 // Specify the header source language to avoid ambiguity. 219 if isCpp { 220 cflags = append(cflags, "-x c++") 221 // Add any C++ only flags. 222 cflags = append(cflags, esc(b.ClangProperties.Cppflags)...) 223 } else { 224 cflags = append(cflags, "-x c") 225 } 226 227 outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs") 228 229 var cmd, cmdDesc string 230 if b.Properties.Custom_bindgen != "" { 231 cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(*Module).HostToolPath().String() 232 cmdDesc = b.Properties.Custom_bindgen 233 } else { 234 cmd = "$bindgenCmd" 235 cmdDesc = "bindgen" 236 } 237 238 ctx.Build(pctx, android.BuildParams{ 239 Rule: bindgen, 240 Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "), 241 Output: outputFile, 242 Input: wrapperFile.Path(), 243 Implicits: implicits, 244 Args: map[string]string{ 245 "cmd": cmd, 246 "flags": strings.Join(bindgenFlags, " "), 247 "cflags": strings.Join(cflags, " "), 248 }, 249 }) 250 251 b.BaseSourceProvider.OutputFiles = android.Paths{outputFile} 252 return outputFile 253} 254 255func (b *bindgenDecorator) SourceProviderProps() []interface{} { 256 return append(b.BaseSourceProvider.SourceProviderProps(), 257 &b.Properties, &b.ClangProperties) 258} 259 260// rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input. 261// Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure 262// the header and generated source is appropriately handled. It is recommended to add it as a dependency in the 263// rlibs, dylibs or rustlibs property. It may also be added in the srcs property for external crates, using the ":" 264// prefix. 265func RustBindgenFactory() android.Module { 266 module, _ := NewRustBindgen(android.HostAndDeviceSupported) 267 return module.Init() 268} 269 270func RustBindgenHostFactory() android.Module { 271 module, _ := NewRustBindgen(android.HostSupported) 272 return module.Init() 273} 274 275func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) { 276 bindgen := &bindgenDecorator{ 277 BaseSourceProvider: NewSourceProvider(), 278 Properties: BindgenProperties{}, 279 ClangProperties: cc.RustBindgenClangProperties{}, 280 } 281 282 module := NewSourceProviderModule(hod, bindgen, false) 283 284 return module, bindgen 285} 286 287func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps { 288 deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps) 289 if ctx.toolchain().Bionic() { 290 deps = bionicDeps(ctx, deps, false) 291 } else if ctx.Os() == android.LinuxMusl { 292 deps = muslDeps(ctx, deps, false) 293 } 294 295 deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...) 296 deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...) 297 deps.HeaderLibs = append(deps.StaticLibs, b.ClangProperties.Header_libs...) 298 return deps 299} 300