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