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