• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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