xref: /aosp_15_r20/build/soong/rust/bindgen.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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