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
22	"android/soong/android"
23	ccConfig "android/soong/cc/config"
24)
25
26var (
27	defaultBindgenFlags = []string{""}
28
29	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
30	bindgenClangVersion  = "clang-r383902c"
31	bindgenLibClangSoGit = "11git"
32
33	//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
34	_ = pctx.SourcePathVariable("bindgenCmd", "out/host/${config.HostPrebuiltTag}/bin/bindgen")
35	_ = pctx.SourcePathVariable("bindgenClang",
36		"${ccConfig.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
37	_ = pctx.SourcePathVariable("bindgenLibClang",
38		"${ccConfig.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/lib64/libclang.so."+bindgenLibClangSoGit)
39
40	//TODO(ivanlozano) Switch this to RuleBuilder
41	bindgen = pctx.AndroidStaticRule("bindgen",
42		blueprint.RuleParams{
43			Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " +
44				"$bindgenCmd $flags $in -o $out -- -MD -MF $out.d $cflags",
45			CommandDeps: []string{"$bindgenCmd"},
46			Deps:        blueprint.DepsGCC,
47			Depfile:     "$out.d",
48		},
49		"flags", "cflags")
50)
51
52func init() {
53	android.RegisterModuleType("rust_bindgen", RustBindgenFactory)
54	android.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory)
55}
56
57var _ SourceProvider = (*bindgenDecorator)(nil)
58
59type BindgenProperties struct {
60	// The wrapper header file
61	Wrapper_src *string `android:"path,arch_variant"`
62
63	// list of bindgen-specific flags and options
64	Flags []string `android:"arch_variant"`
65
66	// list of clang flags required to correctly interpret the headers.
67	Cflags []string `android:"arch_variant"`
68
69	// list of directories relative to the Blueprints file that will
70	// be added to the include path using -I
71	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
72
73	// list of static libraries that provide headers for this binding.
74	Static_libs []string `android:"arch_variant,variant_prepend"`
75
76	// list of shared libraries that provide headers for this binding.
77	Shared_libs []string `android:"arch_variant"`
78
79	//TODO(b/161141999) Add support for headers from cc_library_header modules.
80}
81
82type bindgenDecorator struct {
83	*baseSourceProvider
84
85	Properties BindgenProperties
86}
87
88func (b *bindgenDecorator) generateSource(ctx android.ModuleContext, deps PathDeps) android.Path {
89	ccToolchain := ccConfig.FindToolchain(ctx.Os(), ctx.Arch())
90
91	var cflags []string
92	var implicits android.Paths
93
94	implicits = append(implicits, deps.depIncludePaths...)
95	implicits = append(implicits, deps.depSystemIncludePaths...)
96
97	// Default clang flags
98	cflags = append(cflags, "${ccConfig.CommonClangGlobalCflags}")
99	if ctx.Device() {
100		cflags = append(cflags, "${ccConfig.DeviceClangGlobalCflags}")
101	}
102
103	// Toolchain clang flags
104	cflags = append(cflags, "-target "+ccToolchain.ClangTriple())
105	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainClangCflags(), "${config.", "${ccConfig."))
106
107	// Dependency clang flags and include paths
108	cflags = append(cflags, deps.depClangFlags...)
109	for _, include := range deps.depIncludePaths {
110		cflags = append(cflags, "-I"+include.String())
111	}
112	for _, include := range deps.depSystemIncludePaths {
113		cflags = append(cflags, "-isystem "+include.String())
114	}
115
116	// Module defined clang flags and include paths
117	cflags = append(cflags, b.Properties.Cflags...)
118	for _, include := range b.Properties.Local_include_dirs {
119		cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
120		implicits = append(implicits, android.PathForModuleSrc(ctx, include))
121	}
122
123	bindgenFlags := defaultBindgenFlags
124	bindgenFlags = append(bindgenFlags, strings.Join(b.Properties.Flags, " "))
125
126	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
127	if !wrapperFile.Valid() {
128		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
129	}
130
131	outputFile := android.PathForModuleOut(ctx, b.baseSourceProvider.getStem(ctx)+".rs")
132
133	ctx.Build(pctx, android.BuildParams{
134		Rule:        bindgen,
135		Description: "bindgen " + wrapperFile.Path().Rel(),
136		Output:      outputFile,
137		Input:       wrapperFile.Path(),
138		Implicits:   implicits,
139		Args: map[string]string{
140			"flags":  strings.Join(bindgenFlags, " "),
141			"cflags": strings.Join(cflags, " "),
142		},
143	})
144	b.baseSourceProvider.outputFile = outputFile
145	return outputFile
146}
147
148func (b *bindgenDecorator) sourceProviderProps() []interface{} {
149	return append(b.baseSourceProvider.sourceProviderProps(),
150		&b.Properties)
151}
152
153// rust_bindgen generates Rust FFI bindings to C libraries using bindgen given a wrapper header as the primary input.
154// Bindgen has a number of flags to control the generated source, and additional flags can be passed to clang to ensure
155// the header and generated source is appropriately handled.
156func RustBindgenFactory() android.Module {
157	module, _ := NewRustBindgen(android.HostAndDeviceSupported)
158	return module.Init()
159}
160
161func RustBindgenHostFactory() android.Module {
162	module, _ := NewRustBindgen(android.HostSupported)
163	return module.Init()
164}
165
166func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorator) {
167	module := newModule(hod, android.MultilibBoth)
168
169	bindgen := &bindgenDecorator{
170		baseSourceProvider: NewSourceProvider(),
171		Properties:         BindgenProperties{},
172	}
173	module.sourceProvider = bindgen
174
175	return module, bindgen
176}
177
178func (b *bindgenDecorator) sourceProviderDeps(ctx DepsContext, deps Deps) Deps {
179	deps = b.baseSourceProvider.sourceProviderDeps(ctx, deps)
180	if ctx.toolchain().Bionic() {
181		deps = bionicDeps(deps)
182	}
183
184	deps.SharedLibs = append(deps.SharedLibs, b.Properties.Shared_libs...)
185	deps.StaticLibs = append(deps.StaticLibs, b.Properties.Static_libs...)
186	return deps
187}
188