1// Copyright (C) 2021 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 aidl
16
17import (
18	"android/soong/android"
19	"android/soong/cc"
20	"android/soong/java"
21	"android/soong/rust"
22
23	"fmt"
24	"path/filepath"
25	"strings"
26
27	"github.com/google/blueprint/proptools"
28)
29
30func addLibrary(mctx android.DefaultableHookContext, i *aidlInterface, version string, lang string, notFrozen bool, requireFrozenReason string) string {
31	if lang == langJava {
32		return addJavaLibrary(mctx, i, version, notFrozen, requireFrozenReason)
33	} else if lang == langRust {
34		return addRustLibrary(mctx, i, version, notFrozen, requireFrozenReason)
35	} else if lang == langCppAnalyzer {
36		return addCppAnalyzerLibrary(mctx, i, version, notFrozen, requireFrozenReason)
37	} else if lang == langCpp || lang == langNdk || lang == langNdkPlatform {
38		return addCppLibrary(mctx, i, version, lang, notFrozen, requireFrozenReason)
39	} else {
40		panic(fmt.Errorf("unsupported language backend %q\n", lang))
41	}
42}
43
44func addCppLibrary(mctx android.DefaultableHookContext, i *aidlInterface, version string, lang string, notFrozen bool, requireFrozenReason string) string {
45	cppSourceGen := i.versionedName(version) + "-" + lang + "-source"
46	cppModuleGen := i.versionedName(version) + "-" + lang
47
48	srcs, aidlRoot := i.srcsForVersion(mctx, version)
49	if len(srcs) == 0 {
50		// This can happen when the version is about to be frozen; the version
51		// directory is created but API dump hasn't been copied there.
52		// Don't create a library for the yet-to-be-frozen version.
53		return ""
54	}
55
56	var commonProperties *CommonNativeBackendProperties
57	if lang == langCpp {
58		commonProperties = &i.properties.Backend.Cpp.CommonNativeBackendProperties
59	} else if lang == langNdk || lang == langNdkPlatform {
60		commonProperties = &i.properties.Backend.Ndk.CommonNativeBackendProperties
61	}
62
63	genLog := proptools.Bool(commonProperties.Gen_log)
64	genTrace := i.genTrace(lang)
65	aidlFlags := i.flagsForAidlGenRule(version)
66
67	mctx.CreateModule(aidlGenFactory, &nameProperties{
68		Name: proptools.StringPtr(cppSourceGen),
69	}, &aidlGenProperties{
70		Srcs:                srcs,
71		AidlRoot:            aidlRoot,
72		Imports:             i.getImportsForVersion(version),
73		Headers:             i.properties.Headers,
74		Stability:           i.properties.Stability,
75		Min_sdk_version:     i.minSdkVersion(lang),
76		Lang:                lang,
77		BaseName:            i.ModuleBase.Name(),
78		GenLog:              genLog,
79		Version:             i.versionForInitVersionCompat(version),
80		GenTrace:            genTrace,
81		Unstable:            i.properties.Unstable,
82		NotFrozen:           notFrozen,
83		RequireFrozenReason: requireFrozenReason,
84		Flags:               aidlFlags,
85		UseUnfrozen:         i.useUnfrozen(mctx),
86	})
87
88	importExportDependencies := []string{}
89	sharedLibDependency := commonProperties.Additional_shared_libraries
90	var headerLibs []string
91	var sdkVersion *string
92	var stl *string
93	var cpp_std *string
94	var hostSupported *bool
95	addCflags := commonProperties.Cflags
96	targetProp := ccTargetProperties{}
97
98	if lang == langCpp {
99		importExportDependencies = append(importExportDependencies, "libbinder", "libutils")
100		if genTrace {
101			sharedLibDependency = append(sharedLibDependency, "libcutils")
102		}
103		hostSupported = i.properties.Host_supported
104	} else if lang == langNdk || lang == langNdkPlatform {
105		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
106		nonAppProps := imageProperties{
107			Cflags: []string{"-DBINDER_STABILITY_SUPPORT"},
108		}
109		targetProp.Platform = nonAppProps
110		targetProp.Vendor = nonAppProps
111		targetProp.Product = nonAppProps
112		hostSupported = i.properties.Host_supported
113		if lang == langNdk && i.shouldGenerateAppNdkBackend() {
114			sdkVersion = i.properties.Backend.Ndk.Sdk_version
115			if sdkVersion == nil {
116				sdkVersion = proptools.StringPtr("current")
117			}
118
119			// Don't worry! This maps to libc++.so for the platform variant.
120			stl = proptools.StringPtr("c++_shared")
121		}
122	} else {
123		panic("Unrecognized language: " + lang)
124	}
125
126	vendorAvailable := i.properties.Vendor_available
127	odmAvailable := i.properties.Odm_available
128	productAvailable := i.properties.Product_available
129	recoveryAvailable := i.properties.Recovery_available
130	if lang == langCpp {
131		// Vendor and product modules cannot use the libbinder (cpp) backend of AIDL in a
132		// way that is stable. So, in order to prevent accidental usage of these library by
133		// vendor and product forcibly disabling this version of the library.
134		//
135		// It may be the case in the future that we will want to enable this (if some generic
136		// helper should be used by both libbinder vendor things using /dev/vndbinder as well
137		// as those things using /dev/binder + libbinder_ndk to talk to stable interfaces).
138
139		// As libbinder is not available for the product processes, we must not create
140		// product variant for the aidl_interface
141		productAvailable = nil
142	}
143
144	langProps := &aidlLanguageModuleProperties{}
145	langProps.Aidl_internal_props.Lang = lang
146	langProps.Aidl_internal_props.AidlInterfaceName = i.ModuleBase.Name()
147	langProps.Aidl_internal_props.Version = version
148	langProps.Aidl_internal_props.Imports = i.getImportsForVersion(version)
149
150	mctx.CreateModule(wrapLibraryFactory(aidlCcModuleFactory), langProps, &ccProperties{
151		Name: proptools.StringPtr(cppModuleGen),
152		Enabled: android.CreateSelectOsToBool(map[string]*bool{
153			"":       nil,
154			"darwin": proptools.BoolPtr(false),
155		}),
156		Vendor_available:          vendorAvailable,
157		Odm_available:             odmAvailable,
158		Product_available:         productAvailable,
159		Recovery_available:        recoveryAvailable,
160		Host_supported:            hostSupported,
161		Cmake_snapshot_supported:  i.properties.Cmake_snapshot_supported,
162		Defaults:                  []string{"aidl-cpp-module-defaults"},
163		Double_loadable:           i.properties.Double_loadable,
164		Generated_sources:         []string{cppSourceGen},
165		Generated_headers:         []string{cppSourceGen},
166		Export_generated_headers:  []string{cppSourceGen},
167		Shared_libs:               append(importExportDependencies, sharedLibDependency...),
168		Header_libs:               headerLibs,
169		Export_shared_lib_headers: importExportDependencies,
170		Sdk_version:               sdkVersion,
171		Stl:                       stl,
172		Cpp_std:                   cpp_std,
173		Cflags:                    append(addCflags, "-Wextra", "-Wall", "-Werror", "-Wextra-semi"),
174		Ldflags:                   commonProperties.Ldflags,
175		Apex_available:            commonProperties.Apex_available,
176		Min_sdk_version:           i.minSdkVersion(lang),
177		Target:                    targetProp,
178		Tidy:                      proptools.BoolPtr(true),
179		// Do the tidy check only for the generated headers
180		Tidy_flags: []string{"--header-filter=" + android.PathForOutput(mctx).String() + ".*"},
181		Tidy_checks_as_errors: []string{
182			"*",
183			"-clang-analyzer-deadcode.DeadStores", // b/253079031
184			"-clang-analyzer-cplusplus.NewDeleteLeaks",  // b/253079031
185			"-clang-analyzer-optin.performance.Padding", // b/253079031
186		},
187		Include_build_directory: proptools.BoolPtr(false), // b/254682497
188		AidlInterface: struct {
189			Sources  []string
190			AidlRoot string
191			Lang     string
192			Flags    []string
193		}{
194			Sources:  srcs,
195			AidlRoot: aidlRoot,
196			Lang:     lang,
197			Flags:    aidlFlags,
198		},
199	})
200
201	return cppModuleGen
202}
203
204func addCppAnalyzerLibrary(mctx android.DefaultableHookContext, i *aidlInterface, version string, notFrozen bool, requireFrozenReason string) string {
205	cppAnalyzerSourceGen := i.versionedName("") + "-cpp-analyzer-source"
206	cppAnalyzerModuleGen := i.versionedName("") + "-cpp-analyzer"
207
208	srcs, aidlRoot := i.srcsForVersion(mctx, version)
209	if len(srcs) == 0 {
210		return ""
211	}
212
213	mctx.CreateModule(aidlGenFactory, &nameProperties{
214		Name: proptools.StringPtr(cppAnalyzerSourceGen),
215	}, &aidlGenProperties{
216		Srcs:                srcs,
217		AidlRoot:            aidlRoot,
218		Imports:             i.getImportsForVersion(version),
219		Stability:           i.properties.Stability,
220		Min_sdk_version:     i.minSdkVersion(langCpp),
221		Lang:                langCppAnalyzer,
222		BaseName:            i.ModuleBase.Name(),
223		Version:             i.versionForInitVersionCompat(version),
224		Unstable:            i.properties.Unstable,
225		NotFrozen:           notFrozen,
226		RequireFrozenReason: requireFrozenReason,
227		Flags:               i.flagsForAidlGenRule(version),
228		UseUnfrozen:         i.useUnfrozen(mctx),
229	})
230
231	importExportDependencies := []string{}
232	var hostSupported *bool
233	var addCflags []string // not using cpp backend cflags for now
234	targetProp := ccTargetProperties{}
235
236	importExportDependencies = append(importExportDependencies, "libbinder", "libutils")
237	hostSupported = i.properties.Host_supported
238
239	vendorAvailable := i.properties.Vendor_available
240	odmAvailable := i.properties.Odm_available
241	productAvailable := i.properties.Product_available
242	recoveryAvailable := i.properties.Recovery_available
243	productAvailable = nil
244
245	commonProperties := &i.properties.Backend.Cpp.CommonNativeBackendProperties
246
247	props := &ccProperties{
248		Name: proptools.StringPtr(cppAnalyzerModuleGen),
249		Enabled: android.CreateSelectOsToBool(map[string]*bool{
250			"":       nil,
251			"darwin": proptools.BoolPtr(false),
252		}),
253		Vendor_available:          vendorAvailable,
254		Odm_available:             odmAvailable,
255		Product_available:         productAvailable,
256		Recovery_available:        recoveryAvailable,
257		Host_supported:            hostSupported,
258		Defaults:                  []string{"aidl-cpp-module-defaults"},
259		Double_loadable:           i.properties.Double_loadable,
260		Installable:               proptools.BoolPtr(true),
261		Generated_sources:         []string{cppAnalyzerSourceGen},
262		Generated_headers:         []string{cppAnalyzerSourceGen},
263		Export_generated_headers:  []string{cppAnalyzerSourceGen},
264		Shared_libs:               append(append(importExportDependencies, i.versionedName(version)+"-"+langCpp), commonProperties.Additional_shared_libraries...),
265		Static_libs:               []string{"aidl-analyzer-main"},
266		Export_shared_lib_headers: importExportDependencies,
267		Cflags:                    append(addCflags, "-Wextra", "-Wall", "-Werror", "-Wextra-semi"),
268		Ldflags:                   commonProperties.Ldflags,
269		Min_sdk_version:           i.minSdkVersion(langCpp),
270		Target:                    targetProp,
271		Tidy:                      proptools.BoolPtr(true),
272		// Do the tidy check only for the generated headers
273		Tidy_flags: []string{"--header-filter=" + android.PathForOutput(mctx).String() + ".*"},
274		Tidy_checks_as_errors: []string{
275			"*",
276			"-clang-diagnostic-deprecated-declarations", // b/253081572
277			"-clang-analyzer-deadcode.DeadStores",       // b/253079031
278			"-clang-analyzer-cplusplus.NewDeleteLeaks",  // b/253079031
279			"-clang-analyzer-optin.performance.Padding", // b/253079031
280		},
281	}
282
283	mctx.CreateModule(wrapLibraryFactory(cc.BinaryFactory), props)
284	return cppAnalyzerModuleGen
285}
286
287func addJavaLibrary(mctx android.DefaultableHookContext, i *aidlInterface, version string, notFrozen bool, requireFrozenReason string) string {
288	javaSourceGen := i.versionedName(version) + "-java-source"
289	javaModuleGen := i.versionedName(version) + "-java"
290	srcs, aidlRoot := i.srcsForVersion(mctx, version)
291	if len(srcs) == 0 {
292		// This can happen when the version is about to be frozen; the version
293		// directory is created but API dump hasn't been copied there.
294		// Don't create a library for the yet-to-be-frozen version.
295		return ""
296	}
297	minSdkVersion := i.minSdkVersion(langJava)
298	sdkVersion := i.properties.Backend.Java.Sdk_version
299	if !proptools.Bool(i.properties.Backend.Java.Platform_apis) && sdkVersion == nil {
300		// platform apis requires no default
301		sdkVersion = proptools.StringPtr("system_current")
302	}
303	// use sdkVersion if minSdkVersion is not set
304	if sdkVersion != nil && minSdkVersion == nil {
305		minSdkVersion = proptools.StringPtr(android.SdkSpecFrom(mctx, *sdkVersion).ApiLevel.String())
306	}
307
308	mctx.CreateModule(aidlGenFactory, &nameProperties{
309		Name: proptools.StringPtr(javaSourceGen),
310	}, &aidlGenProperties{
311		Srcs:                srcs,
312		AidlRoot:            aidlRoot,
313		Imports:             i.getImportsForVersion(version),
314		Headers:             i.properties.Headers,
315		Stability:           i.properties.Stability,
316		Min_sdk_version:     minSdkVersion,
317		Platform_apis:       proptools.Bool(i.properties.Backend.Java.Platform_apis),
318		Lang:                langJava,
319		BaseName:            i.ModuleBase.Name(),
320		Version:             version,
321		GenRpc:              proptools.Bool(i.properties.Backend.Java.Gen_rpc),
322		GenTrace:            i.genTrace(langJava),
323		Unstable:            i.properties.Unstable,
324		NotFrozen:           notFrozen,
325		RequireFrozenReason: requireFrozenReason,
326		Flags:               i.flagsForAidlGenRule(version),
327		UseUnfrozen:         i.useUnfrozen(mctx),
328	})
329
330	langProps := &aidlLanguageModuleProperties{}
331	langProps.Aidl_internal_props.Lang = langJava
332	langProps.Aidl_internal_props.AidlInterfaceName = i.ModuleBase.Name()
333	langProps.Aidl_internal_props.Version = version
334	langProps.Aidl_internal_props.Imports = i.getImportsForVersion(version)
335
336	mctx.CreateModule(wrapLibraryFactory(aidlJavaModuleFactory), &javaProperties{
337		Name:            proptools.StringPtr(javaModuleGen),
338		Installable:     proptools.BoolPtr(true),
339		Defaults:        []string{"aidl-java-module-defaults"},
340		Sdk_version:     sdkVersion,
341		Srcs:            []string{":" + javaSourceGen},
342		Apex_available:  i.properties.Backend.Java.Apex_available,
343		Min_sdk_version: i.minSdkVersion(langJava),
344		Static_libs:     i.properties.Backend.Java.Additional_libs,
345		Is_stubs_module: proptools.BoolPtr(true),
346	},
347		&i.properties.Backend.Java.LintProperties,
348		langProps,
349	)
350
351	return javaModuleGen
352}
353
354func addRustLibrary(mctx android.DefaultableHookContext, i *aidlInterface, version string, notFrozen bool, requireFrozenReason string) string {
355	rustSourceGen := i.versionedName(version) + "-rust-source"
356	rustModuleGen := i.versionedName(version) + "-rust"
357	srcs, aidlRoot := i.srcsForVersion(mctx, version)
358	if len(srcs) == 0 {
359		// This can happen when the version is about to be frozen; the version
360		// directory is created but API dump hasn't been copied there.
361		// Don't create a library for the yet-to-be-frozen version.
362		return ""
363	}
364
365	mctx.CreateModule(aidlGenFactory, &nameProperties{
366		Name: proptools.StringPtr(rustSourceGen),
367	}, &aidlGenProperties{
368		Srcs:                srcs,
369		AidlRoot:            aidlRoot,
370		Imports:             i.getImportsForVersion(version),
371		Headers:             i.properties.Headers,
372		Stability:           i.properties.Stability,
373		Min_sdk_version:     i.minSdkVersion(langRust),
374		Lang:                langRust,
375		BaseName:            i.ModuleBase.Name(),
376		Version:             i.versionForInitVersionCompat(version),
377		Unstable:            i.properties.Unstable,
378		NotFrozen:           notFrozen,
379		RequireFrozenReason: requireFrozenReason,
380		Flags:               i.flagsForAidlGenRule(version),
381		UseUnfrozen:         i.useUnfrozen(mctx),
382		GenMockall:          proptools.Bool(i.properties.Backend.Rust.Gen_mockall),
383	})
384
385	versionedRustName := fixRustName(i.versionedName(version))
386	rustCrateName := fixRustName(i.ModuleBase.Name())
387
388	mctx.CreateModule(wrapLibraryFactory(aidlRustLibraryFactory), &rustProperties{
389		Name: proptools.StringPtr(rustModuleGen),
390		Enabled: android.CreateSelectOsToBool(map[string]*bool{
391			"darwin": proptools.BoolPtr(false),
392			"":       nil,
393		}),
394		Crate_name:        rustCrateName,
395		Stem:              proptools.StringPtr("lib" + versionedRustName),
396		Defaults:          []string{"aidl-rust-module-defaults"},
397		Host_supported:    i.properties.Host_supported,
398		Vendor_available:  i.properties.Vendor_available,
399		Product_available: i.properties.Product_available,
400		Apex_available:    i.properties.Backend.Rust.Apex_available,
401		Min_sdk_version:   i.minSdkVersion(langRust),
402		Rustlibs:          i.properties.Backend.Rust.Additional_rustlibs,
403	}, &rust.SourceProviderProperties{
404		Source_stem: proptools.StringPtr(versionedRustName),
405	}, &aidlRustSourceProviderProperties{
406		SourceGen:         rustSourceGen,
407		Imports:           i.getImportsForVersion(version),
408		Version:           version,
409		AidlInterfaceName: i.ModuleBase.Name(),
410	})
411
412	return rustModuleGen
413}
414
415// This function returns module name with version. Assume that there is foo of which latest version is 2
416// Version -> Module name
417// "1"->foo-V1
418// "2"->foo-V2
419// "3"->foo-V3
420// And assume that there is 'bar' which is an 'unstable' interface.
421// ""->bar
422func (i *aidlInterface) versionedName(version string) string {
423	name := i.ModuleBase.Name()
424	if version == "" {
425		return name
426	}
427	return name + "-V" + version
428}
429
430func (i *aidlInterface) srcsForVersion(mctx android.EarlyModuleContext, version string) (srcs []string, aidlRoot string) {
431	if version == i.nextVersion() {
432		return i.properties.Srcs, i.properties.Local_include_dir
433	} else {
434		aidlRoot = filepath.Join(aidlApiDir, i.ModuleBase.Name(), version)
435		full_paths, err := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), aidlRoot, "**/*.aidl"), nil)
436		if err != nil {
437			panic(err)
438		}
439		for _, path := range full_paths {
440			// Here, we need path local to the module
441			srcs = append(srcs, strings.TrimPrefix(path, mctx.ModuleDir()+"/"))
442		}
443		return srcs, aidlRoot
444	}
445}
446
447// For certain backend, avoid a difference between the initial version of a versioned
448// interface and an unversioned interface. This ensures that prebuilts can't prevent
449// an interface from switching from unversioned to versioned.
450func (i *aidlInterface) versionForInitVersionCompat(version string) string {
451	if !i.hasVersion() {
452		return ""
453	}
454	return version
455}
456
457func (i *aidlInterface) flagsForAidlGenRule(version string) (flags []string) {
458	// For the latest unfrozen version of an interface we turn on all warnings and use
459	// all flags supplied by the 'flags' field in the aidl_interface module
460	if version == i.nextVersion() && !i.isFrozen() {
461		flags = append(flags, "-Weverything -Wno-missing-permission-annotation")
462		flags = append(flags, i.properties.Flags...)
463	}
464	return
465}
466
467// importing aidl_interface's version  | imported aidl_interface | imported aidl_interface's version
468// --------------------------------------------------------------------------------------------------
469// whatever                            | unstable                | unstable version
470// ToT version(including unstable)     | whatever                | ToT version(unstable if unstable)
471// otherwise                           | whatever                | the latest stable version
472// In the case that import specifies the version which it wants to use, use that version.
473func (i *aidlInterface) getImportWithVersion(version string, anImport string, other *aidlInterface) string {
474	if hasVersionSuffix(anImport) {
475		return anImport
476	}
477	if proptools.Bool(other.properties.Unstable) {
478		return anImport
479	}
480	if version == i.nextVersion() || !other.hasVersion() {
481		return other.versionedName(other.nextVersion())
482	}
483	return other.versionedName(other.latestVersion())
484}
485
486// Assuming that the context module has deps to its original aidl_interface and imported
487// aidl_interface modules with interfaceDepTag and importInterfaceDepTag, returns the list of
488// imported interfaces with versions.
489func getImportsWithVersion(ctx android.BaseModuleContext, interfaceName, version string) []string {
490	// We're using VisitDirectDepsWithTag instead of GetDirectDepWithTag because GetDirectDepWithTag
491	// has weird behavior: if you're using a ModuleContext, it will find a dep based off the
492	// ModuleBase name, but if you're using a BaseModuleContext, it will find a dep based off of
493	// the outer module's name. We need the behavior to be consistent because we call this method
494	// with both types of contexts.
495	var i *aidlInterface
496	ctx.VisitDirectDepsWithTag(interfaceDep, func(visited android.Module) {
497		if i == nil && visited.Name() == interfaceName+aidlInterfaceSuffix {
498			i = visited.(*aidlInterface)
499		}
500	})
501	if i == nil {
502		ctx.ModuleErrorf("expected to find the aidl interface via an interfaceDep, but did not.")
503		return nil
504	}
505	var imports []string
506	ctx.VisitDirectDeps(func(dep android.Module) {
507		if tag, ok := ctx.OtherModuleDependencyTag(dep).(importInterfaceDepTag); ok {
508			other := dep.(*aidlInterface)
509			imports = append(imports, i.getImportWithVersion(version, tag.anImport, other))
510		}
511	})
512	return imports
513}
514
515func aidlCcModuleFactory() android.Module {
516	m := cc.LibraryFactory()
517	m.AddProperties(&aidlLanguageModuleProperties{})
518	return m
519}
520
521func aidlJavaModuleFactory() android.Module {
522	m := java.LibraryFactory()
523	m.AddProperties(&aidlLanguageModuleProperties{})
524	return m
525}
526
527type aidlLanguageModuleProperties struct {
528	// Because we add this property struct to regular cc/java modules, move all the props under an
529	// "Aidl_internal_props" struct so we're sure they don't accidentally share the same name as a
530	// core cc/java property.
531	Aidl_internal_props struct {
532		Lang              string
533		AidlInterfaceName string
534		Version           string
535		Imports           []string
536	}
537}
538