• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 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/genrule"
21	"android/soong/java"
22	"android/soong/phony"
23	"fmt"
24	"io"
25	"path/filepath"
26	"strconv"
27	"strings"
28	"sync"
29
30	"github.com/google/blueprint"
31	"github.com/google/blueprint/pathtools"
32	"github.com/google/blueprint/proptools"
33)
34
35var (
36	aidlInterfaceSuffix = "_interface"
37	aidlApiSuffix       = "-api"
38	langCpp             = "cpp"
39	langJava            = "java"
40	langNdk             = "ndk"
41	langNdkPlatform     = "ndk_platform"
42	futureVersion       = "10000"
43
44	pctx = android.NewPackageContext("android/aidl")
45
46	aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{
47		Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
48			`touch ${out}`,
49		Description: "create ${out}",
50	}, "outDir")
51
52	aidlCppRule = pctx.StaticRule("aidlCppRule", blueprint.RuleParams{
53		Command: `mkdir -p "${headerDir}" && ` +
54			`${aidlCmd} --lang=${lang} ${optionalFlags} --structured --ninja -d ${out}.d ` +
55			`-h ${headerDir} -o ${outDir} ${imports} ${in}`,
56		Depfile:     "${out}.d",
57		Deps:        blueprint.DepsGCC,
58		CommandDeps: []string{"${aidlCmd}"},
59		Description: "AIDL ${lang} ${in}",
60	}, "imports", "lang", "headerDir", "outDir", "optionalFlags")
61
62	aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{
63		Command: `${aidlCmd} --lang=java ${optionalFlags} --structured --ninja -d ${out}.d ` +
64			`-o ${outDir} ${imports} ${in}`,
65		Depfile:     "${out}.d",
66		Deps:        blueprint.DepsGCC,
67		CommandDeps: []string{"${aidlCmd}"},
68		Description: "AIDL Java ${in}",
69	}, "imports", "outDir", "optionalFlags")
70
71	aidlDumpApiRule = pctx.StaticRule("aidlDumpApiRule", blueprint.RuleParams{
72		Command: `rm -rf "${out}" && mkdir -p "${out}" && ` +
73			`${aidlCmd} --dumpapi --structured ${imports} --out ${out} ${in}`,
74		CommandDeps: []string{"${aidlCmd}"},
75	}, "imports")
76
77	aidlDumpMappingsRule = pctx.StaticRule("aidlDumpMappingsRule", blueprint.RuleParams{
78		Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
79			`${aidlCmd} --apimapping ${outDir}/intermediate.txt ${in} ${imports} && ` +
80			`${aidlToJniCmd} ${outDir}/intermediate.txt ${out}`,
81		CommandDeps: []string{"${aidlCmd}"},
82	}, "imports", "outDir")
83
84	aidlFreezeApiRule = pctx.AndroidStaticRule("aidlFreezeApiRule",
85		blueprint.RuleParams{
86			Command: `mkdir -p ${to} && rm -rf ${to}/* && ` +
87				`${bpmodifyCmd} -w -m ${name} -parameter versions -a ${version} ${bp} && ` +
88				`cp -rf ${in}/* ${to} && ` +
89				`find ${to} -type f -exec bash -c ` +
90				`"cat ${apiPreamble} {} > {}.temp; mv {}.temp {}" \; && ` +
91				`touch ${out}`,
92			CommandDeps: []string{"${bpmodifyCmd}"},
93		}, "to", "name", "version", "bp", "apiPreamble")
94
95	aidlCheckApiRule = pctx.StaticRule("aidlCheckApiRule", blueprint.RuleParams{
96		Command: `(${aidlCmd} --checkapi ${old} ${new} && touch ${out}) || ` +
97			`(cat ${messageFile} && exit 1)`,
98		CommandDeps: []string{"${aidlCmd}"},
99		Description: "AIDL CHECK API: ${new} against ${old}",
100	}, "old", "new", "messageFile")
101
102	aidlDiffApiRule = pctx.StaticRule("aidlDiffApiRule", blueprint.RuleParams{
103		Command: `(diff -r -B -I '//.*' ${old} ${new} && touch ${out}) || ` +
104			`(cat ${messageFile} && exit 1)`,
105		Description: "Check equality of ${new} and ${old}",
106	}, "old", "new", "messageFile")
107)
108
109func init() {
110	pctx.HostBinToolVariable("aidlCmd", "aidl")
111	pctx.HostBinToolVariable("bpmodifyCmd", "bpmodify")
112	pctx.SourcePathVariable("aidlToJniCmd", "system/tools/aidl/build/aidl_to_jni.py")
113	android.RegisterModuleType("aidl_interface", aidlInterfaceFactory)
114	android.RegisterModuleType("aidl_mapping", aidlMappingFactory)
115}
116
117// wrap(p, a, s) = [p + v + s for v in a]
118func wrap(prefix string, strs []string, suffix string) []string {
119	ret := make([]string, len(strs))
120	for i, v := range strs {
121		ret[i] = prefix + v + suffix
122	}
123	return ret
124}
125
126// concat(a...) = sum((i for i in a), [])
127func concat(sstrs ...[]string) []string {
128	var ret []string
129	for _, v := range sstrs {
130		ret = append(ret, v...)
131	}
132	return ret
133}
134
135func isRelativePath(path string) bool {
136	if path == "" {
137		return true
138	}
139	return filepath.Clean(path) == path && path != ".." &&
140		!strings.HasPrefix(path, "../") && !strings.HasPrefix(path, "/")
141}
142
143type aidlGenProperties struct {
144	Srcs     []string
145	AidlRoot string // base directory for the input aidl file
146	Imports  []string
147	Lang     string // target language [java|cpp|ndk]
148	BaseName string
149	GenLog   bool
150	Version  string
151}
152
153type aidlGenRule struct {
154	android.ModuleBase
155
156	properties aidlGenProperties
157
158	implicitInputs android.Paths
159	importFlags    string
160
161	genOutDir    android.ModuleGenPath
162	genHeaderDir android.ModuleGenPath
163	genOutputs   android.WritablePaths
164}
165
166var _ android.SourceFileProducer = (*aidlGenRule)(nil)
167var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil)
168
169func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
170	genDirTimestamp := android.PathForModuleGen(ctx, "timestamp")
171	g.implicitInputs = append(g.implicitInputs, genDirTimestamp)
172
173	var importPaths []string
174	ctx.VisitDirectDeps(func(dep android.Module) {
175		if importedAidl, ok := dep.(*aidlInterface); ok {
176			importPaths = append(importPaths, importedAidl.properties.Full_import_paths...)
177		} else if api, ok := dep.(*aidlApi); ok {
178			// When compiling an AIDL interface, also make sure that each
179			// version of the interface is compatible with its previous version
180			for _, path := range api.checkApiTimestamps {
181				g.implicitInputs = append(g.implicitInputs, path)
182			}
183		}
184	})
185	g.importFlags = strings.Join(wrap("-I", importPaths, ""), " ")
186
187	srcs := android.PathsWithModuleSrcSubDir(ctx, android.PathsForModuleSrc(ctx, g.properties.Srcs), g.properties.AidlRoot)
188
189	g.genOutDir = android.PathForModuleGen(ctx)
190	g.genHeaderDir = android.PathForModuleGen(ctx, "include")
191	for _, src := range srcs {
192		g.genOutputs = append(g.genOutputs, g.generateBuildActionsForSingleAidl(ctx, src))
193	}
194
195	// This is to clean genOutDir before generating any file
196	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
197		Rule:      aidlDirPrepareRule,
198		Implicits: srcs,
199		Output:    genDirTimestamp,
200		Args: map[string]string{
201			"outDir": g.genOutDir.String(),
202		},
203	})
204}
205
206func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) android.WritablePath {
207	var outFile android.WritablePath
208	if g.properties.Lang == langJava {
209		outFile = android.PathForModuleGen(ctx, pathtools.ReplaceExtension(src.Rel(), "java"))
210	} else {
211		outFile = android.PathForModuleGen(ctx, pathtools.ReplaceExtension(src.Rel(), "cpp"))
212	}
213
214	var optionalFlags []string
215	if g.properties.Version != "" {
216		optionalFlags = append(optionalFlags, "--version "+g.properties.Version)
217	}
218
219	if g.properties.Lang == langJava {
220		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
221			Rule:      aidlJavaRule,
222			Input:     src,
223			Implicits: g.implicitInputs,
224			Output:    outFile,
225			Args: map[string]string{
226				"imports":       g.importFlags,
227				"outDir":        g.genOutDir.String(),
228				"optionalFlags": strings.Join(optionalFlags, " "),
229			},
230		})
231	} else {
232		typeName := strings.TrimSuffix(filepath.Base(src.Rel()), ".aidl")
233		packagePath := filepath.Dir(src.Rel())
234		baseName := typeName
235		// TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if
236		//   an interface name has a leading I. Those same heuristics have been
237		//   moved here.
238		if len(baseName) >= 2 && baseName[0] == 'I' &&
239			strings.ToUpper(baseName)[1] == baseName[1] {
240			baseName = strings.TrimPrefix(typeName, "I")
241		}
242
243		prefix := ""
244		if g.properties.Lang == langNdk || g.properties.Lang == langNdkPlatform {
245			prefix = "aidl"
246		}
247
248		var headers android.WritablePaths
249		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
250			typeName+".h"))
251		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
252			"Bp"+baseName+".h"))
253		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
254			"Bn"+baseName+".h"))
255
256		if g.properties.GenLog {
257			optionalFlags = append(optionalFlags, "--log")
258		}
259
260		aidlLang := g.properties.Lang
261		if aidlLang == langNdkPlatform {
262			aidlLang = "ndk"
263		}
264
265		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
266			Rule:            aidlCppRule,
267			Input:           src,
268			Implicits:       g.implicitInputs,
269			Output:          outFile,
270			ImplicitOutputs: headers,
271			Args: map[string]string{
272				"imports":       g.importFlags,
273				"lang":          aidlLang,
274				"headerDir":     g.genHeaderDir.String(),
275				"outDir":        g.genOutDir.String(),
276				"optionalFlags": strings.Join(optionalFlags, " "),
277			},
278		})
279	}
280
281	return outFile
282}
283
284func (g *aidlGenRule) GeneratedSourceFiles() android.Paths {
285	return g.genOutputs.Paths()
286}
287
288func (g *aidlGenRule) Srcs() android.Paths {
289	return g.genOutputs.Paths()
290}
291
292func (g *aidlGenRule) GeneratedDeps() android.Paths {
293	return g.genOutputs.Paths()
294}
295
296func (g *aidlGenRule) GeneratedHeaderDirs() android.Paths {
297	return android.Paths{g.genHeaderDir}
298}
299
300func (g *aidlGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
301	ctx.AddDependency(ctx.Module(), nil, wrap("", g.properties.Imports, aidlInterfaceSuffix)...)
302	ctx.AddDependency(ctx.Module(), nil, g.properties.BaseName+aidlApiSuffix)
303}
304
305func aidlGenFactory() android.Module {
306	g := &aidlGenRule{}
307	g.AddProperties(&g.properties)
308	android.InitAndroidModule(g)
309	return g
310}
311
312type aidlApiProperties struct {
313	BaseName string
314	Inputs   []string
315	Imports  []string
316	Api_dir  *string
317	Versions []string
318	AidlRoot string // base directory for the input aidl file
319}
320
321type aidlApi struct {
322	android.ModuleBase
323
324	properties aidlApiProperties
325
326	// for triggering api check for version X against version X-1
327	checkApiTimestamps android.WritablePaths
328
329	// for triggering freezing API as the new version
330	freezeApiTimestamp android.WritablePath
331}
332
333func (m *aidlApi) apiDir() string {
334	if m.properties.Api_dir != nil {
335		return *(m.properties.Api_dir)
336	} else {
337		return "api"
338	}
339}
340
341// Version of the interface at ToT if it is frozen
342func (m *aidlApi) validateCurrentVersion(ctx android.ModuleContext) string {
343	if len(m.properties.Versions) == 0 {
344		return "1"
345	} else {
346		latestVersion := m.properties.Versions[len(m.properties.Versions)-1]
347
348		i, err := strconv.ParseInt(latestVersion, 10, 64)
349		if err != nil {
350			ctx.PropertyErrorf("versions", "must be integers")
351			return ""
352		}
353
354		return strconv.FormatInt(i+1, 10)
355	}
356}
357
358func (m *aidlApi) createApiDumpFromSource(ctx android.ModuleContext) (apiDir android.WritablePath, apiFiles android.WritablePaths) {
359	var importPaths []string
360	ctx.VisitDirectDeps(func(dep android.Module) {
361		if importedAidl, ok := dep.(*aidlInterface); ok {
362			importPaths = append(importPaths, importedAidl.properties.Full_import_paths...)
363		}
364	})
365
366	var srcs android.Paths
367	for _, input := range m.properties.Inputs {
368		path := android.PathForModuleSrc(ctx, input)
369		path = android.PathWithModuleSrcSubDir(ctx, path, m.properties.AidlRoot)
370		srcs = append(srcs, path)
371	}
372
373	apiDir = android.PathForModuleOut(ctx, "dump")
374	for _, src := range srcs {
375		apiFiles = append(apiFiles, android.PathForModuleOut(ctx, "dump", src.Rel()))
376	}
377	imports := strings.Join(wrap("-I", importPaths, ""), " ")
378	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
379		Rule:            aidlDumpApiRule,
380		Inputs:          srcs,
381		Output:          apiDir,
382		ImplicitOutputs: apiFiles,
383		Args: map[string]string{
384			"imports": imports,
385		},
386	})
387	return apiDir, apiFiles
388}
389
390func (m *aidlApi) freezeApiDumpAsVersion(ctx android.ModuleContext, apiDumpDir android.Path, apiFiles android.Paths, version string) android.WritablePath {
391	timestampFile := android.PathForModuleOut(ctx, "freezeapi_"+version+".timestamp")
392
393	modulePath := android.PathForModuleSrc(ctx).String()
394
395	var implicits android.Paths
396	implicits = append(implicits, apiFiles...)
397
398	apiPreamble := android.PathForSource(ctx, "system/tools/aidl/build/api_preamble.txt")
399	implicits = append(implicits, apiPreamble)
400
401	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
402		Rule:        aidlFreezeApiRule,
403		Description: "Freezing AIDL API of " + m.properties.BaseName + " as version " + version,
404		Implicits:   implicits,
405		Output:      timestampFile,
406		Args: map[string]string{
407			"to":          filepath.Join(modulePath, m.apiDir(), version),
408			"name":        m.properties.BaseName,
409			"version":     version,
410			"bp":          android.PathForModuleSrc(ctx, "Android.bp").String(),
411			"apiPreamble": apiPreamble.String(),
412		},
413	})
414	return timestampFile
415}
416
417func (m *aidlApi) checkCompatibility(ctx android.ModuleContext, oldApiDir android.Path, oldApiFiles android.Paths, newApiDir android.Path, newApiFiles android.Paths) android.WritablePath {
418	newVersion := newApiDir.Base()
419	timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
420	messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_compatibility.txt")
421	var implicits android.Paths
422	implicits = append(implicits, oldApiFiles...)
423	implicits = append(implicits, newApiFiles...)
424	implicits = append(implicits, messageFile)
425	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
426		Rule:      aidlCheckApiRule,
427		Implicits: implicits,
428		Output:    timestampFile,
429		Args: map[string]string{
430			"old":         oldApiDir.String(),
431			"new":         newApiDir.String(),
432			"messageFile": messageFile.String(),
433		},
434	})
435	return timestampFile
436}
437
438func (m *aidlApi) checkEquality(ctx android.ModuleContext, oldApiDir android.Path, oldApiFiles android.Paths, newApiDir android.Path, newApiFiles android.Paths) android.WritablePath {
439	newVersion := newApiDir.Base()
440	timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
441	messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_equality.txt")
442	var implicits android.Paths
443	implicits = append(implicits, oldApiFiles...)
444	implicits = append(implicits, newApiFiles...)
445	implicits = append(implicits, messageFile)
446	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
447		Rule:      aidlDiffApiRule,
448		Implicits: implicits,
449		Output:    timestampFile,
450		Args: map[string]string{
451			"old":         oldApiDir.String(),
452			"new":         newApiDir.String(),
453			"messageFile": messageFile.String(),
454		},
455	})
456	return timestampFile
457}
458
459func (m *aidlApi) GenerateAndroidBuildActions(ctx android.ModuleContext) {
460	currentVersion := m.validateCurrentVersion(ctx)
461
462	if ctx.Failed() {
463		return
464	}
465
466	currentDumpDir, currentApiFiles := m.createApiDumpFromSource(ctx)
467	m.freezeApiTimestamp = m.freezeApiDumpAsVersion(ctx, currentDumpDir, currentApiFiles.Paths(), currentVersion)
468
469	apiDirs := make(map[string]android.Path)
470	apiFiles := make(map[string]android.Paths)
471	for _, ver := range m.properties.Versions {
472		apiDir := android.PathForModuleSrc(ctx, m.apiDir(), ver)
473		apiDirs[ver] = apiDir
474		apiFiles[ver] = ctx.Glob(filepath.Join(apiDir.String(), "**/*.aidl"), nil)
475	}
476	apiDirs[currentVersion] = currentDumpDir
477	apiFiles[currentVersion] = currentApiFiles.Paths()
478
479	// Check that version X is backward compatible with version X-1
480	for i, newVersion := range m.properties.Versions {
481		if i != 0 {
482			oldVersion := m.properties.Versions[i-1]
483			checkApiTimestamp := m.checkCompatibility(ctx, apiDirs[oldVersion], apiFiles[oldVersion], apiDirs[newVersion], apiFiles[newVersion])
484			m.checkApiTimestamps = append(m.checkApiTimestamps, checkApiTimestamp)
485		}
486	}
487
488	// ... and that the currentVersion (ToT) is backwards compatible with or
489	// equal to the latest frozen version
490	if len(m.properties.Versions) >= 1 {
491		latestVersion := m.properties.Versions[len(m.properties.Versions)-1]
492		var checkApiTimestamp android.WritablePath
493		if ctx.Config().DefaultAppTargetSdkInt() != android.FutureApiLevel {
494			// If API is frozen, don't allow any change to the API
495			checkApiTimestamp = m.checkEquality(ctx, apiDirs[latestVersion], apiFiles[latestVersion], apiDirs[currentVersion], apiFiles[currentVersion])
496		} else {
497			// If not, allow backwards compatible changes to the API
498			checkApiTimestamp = m.checkCompatibility(ctx, apiDirs[latestVersion], apiFiles[latestVersion], apiDirs[currentVersion], apiFiles[currentVersion])
499		}
500		m.checkApiTimestamps = append(m.checkApiTimestamps, checkApiTimestamp)
501	}
502}
503
504func (m *aidlApi) AndroidMk() android.AndroidMkData {
505	return android.AndroidMkData{
506		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
507			android.WriteAndroidMkData(w, data)
508			targetName := m.properties.BaseName + "-freeze-api"
509			fmt.Fprintln(w, ".PHONY:", targetName)
510			fmt.Fprintln(w, targetName+":", m.freezeApiTimestamp.String())
511		},
512	}
513}
514
515func (m *aidlApi) DepsMutator(ctx android.BottomUpMutatorContext) {
516	ctx.AddDependency(ctx.Module(), nil, wrap("", m.properties.Imports, aidlInterfaceSuffix)...)
517}
518
519func aidlApiFactory() android.Module {
520	m := &aidlApi{}
521	m.AddProperties(&m.properties)
522	android.InitAndroidModule(m)
523	return m
524}
525
526type aidlInterfaceProperties struct {
527	// Vndk properties for interface library only.
528	cc.VndkProperties
529
530	// Whether the library can be installed on the vendor image.
531	Vendor_available *bool
532	// Top level directories for includes.
533	// TODO(b/128940869): remove it if aidl_interface can depend on framework.aidl
534	Include_dirs []string
535	// Relative path for includes. By default assumes AIDL path is relative to current directory.
536	// TODO(b/111117220): automatically compute by letting AIDL parse multiple files simultaneously
537	Local_include_dir string
538
539	// The owner of the module
540	Owner *string
541
542	// List of .aidl files which compose this interface. These may be globbed.
543	Srcs []string
544
545	Imports []string
546
547	// Used by gen dependency to fill out aidl include path
548	Full_import_paths []string `blueprint:"mutated"`
549
550	// Directory where API dumps are. Default is "api".
551	Api_dir *string
552
553	// Previous API versions that are now frozen. The version that is last in
554	// the list is considered as the most recent version.
555	Versions []string
556
557	Backend struct {
558		Java struct {
559			// Whether to generate Java code using Java binder APIs
560			// Default: true
561			Enabled *bool
562			// Set to the version of the sdk to compile against
563			// Default: system_current
564			Sdk_version *string
565		}
566		Cpp struct {
567			// Whether to generate C++ code using C++ binder APIs
568			// Default: true
569			Enabled *bool
570			// Whether to generate additional code for gathering information
571			// about the transactions
572			// Default: false
573			Gen_log *bool
574		}
575		Ndk struct {
576			// Whether to generate C++ code using NDK binder APIs
577			// Default: true
578			Enabled *bool
579			// Whether to generate additional code for gathering information
580			// about the transactions
581			// Default: false
582			Gen_log *bool
583		}
584	}
585}
586
587type aidlInterface struct {
588	android.ModuleBase
589
590	properties aidlInterfaceProperties
591
592	// Unglobbed sources
593	rawSrcs []string
594}
595
596func (i *aidlInterface) shouldGenerateJavaBackend() bool {
597	// explicitly true if not specified to give early warning to devs
598	return i.properties.Backend.Java.Enabled == nil || *i.properties.Backend.Java.Enabled
599}
600
601func (i *aidlInterface) shouldGenerateCppBackend() bool {
602	// explicitly true if not specified to give early warning to devs
603	return i.properties.Backend.Cpp.Enabled == nil || *i.properties.Backend.Cpp.Enabled
604}
605
606func (i *aidlInterface) shouldGenerateNdkBackend() bool {
607	// explicitly true if not specified to give early warning to devs
608	return i.properties.Backend.Ndk.Enabled == nil || *i.properties.Backend.Ndk.Enabled
609}
610
611func (i *aidlInterface) checkAndUpdateSources(mctx android.LoadHookContext) {
612	prefix := mctx.ModuleDir()
613	for _, source := range i.properties.Srcs {
614		if pathtools.IsGlob(source) {
615			globbedSrcFiles, err := mctx.GlobWithDeps(filepath.Join(prefix, source), nil)
616			if err != nil {
617				mctx.ModuleErrorf("glob: %s", err.Error())
618			}
619			for _, globbedSrc := range globbedSrcFiles {
620				relativeGlobbedSrc, err := filepath.Rel(prefix, globbedSrc)
621				if err != nil {
622					panic(err)
623				}
624
625				i.rawSrcs = append(i.rawSrcs, relativeGlobbedSrc)
626			}
627		} else {
628			i.rawSrcs = append(i.rawSrcs, source)
629		}
630	}
631
632	if len(i.rawSrcs) == 0 {
633		mctx.PropertyErrorf("srcs", "No sources provided.")
634	}
635
636	for _, source := range i.rawSrcs {
637		if !strings.HasSuffix(source, ".aidl") {
638			mctx.PropertyErrorf("srcs", "Source must be a .aidl file: "+source)
639			continue
640		}
641
642		relativePath, err := filepath.Rel(i.properties.Local_include_dir, source)
643		if err != nil || !isRelativePath(relativePath) {
644			mctx.PropertyErrorf("srcs", "Source is not in local_include_dir: "+source)
645		}
646	}
647}
648
649func (i *aidlInterface) checkImports(mctx android.LoadHookContext) {
650	for _, anImport := range i.properties.Imports {
651		other := lookupInterface(anImport)
652
653		if other == nil {
654			mctx.PropertyErrorf("imports", "Import does not exist: "+anImport)
655		}
656
657		if i.shouldGenerateCppBackend() && !other.shouldGenerateCppBackend() {
658			mctx.PropertyErrorf("backend.cpp.enabled",
659				"C++ backend not enabled in the imported AIDL interface %q", anImport)
660		}
661
662		if i.shouldGenerateNdkBackend() && !other.shouldGenerateNdkBackend() {
663			mctx.PropertyErrorf("backend.ndk.enabled",
664				"NDK backend not enabled in the imported AIDL interface %q", anImport)
665		}
666	}
667}
668
669func (i *aidlInterface) versionedName(version string) string {
670	name := i.ModuleBase.Name()
671	if version != futureVersion && version != "" {
672		name = name + "-V" + version
673	}
674	return name
675}
676
677func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, base string) {
678	if version == futureVersion || version == "" {
679		return i.rawSrcs, i.properties.Local_include_dir
680	} else {
681		var apiDir string
682		if i.properties.Api_dir != nil {
683			apiDir = *(i.properties.Api_dir)
684		} else {
685			apiDir = "api"
686		}
687		base = filepath.Join(apiDir, version)
688		full_paths, err := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), base, "**/*.aidl"), nil)
689		if err != nil {
690			panic(err)
691		}
692		for _, path := range full_paths {
693			// Here, we need path local to the module
694			srcs = append(srcs, strings.TrimPrefix(path, mctx.ModuleDir()+"/"))
695		}
696		return srcs, base
697	}
698}
699
700func aidlInterfaceHook(mctx android.LoadHookContext, i *aidlInterface) {
701	if !isRelativePath(i.properties.Local_include_dir) {
702		mctx.PropertyErrorf("local_include_dir", "must be relative path: "+i.properties.Local_include_dir)
703	}
704	var importPaths []string
705	importPaths = append(importPaths, filepath.Join(mctx.ModuleDir(), i.properties.Local_include_dir))
706	importPaths = append(importPaths, i.properties.Include_dirs...)
707
708	i.properties.Full_import_paths = importPaths
709
710	i.checkAndUpdateSources(mctx)
711	i.checkImports(mctx)
712
713	if mctx.Failed() {
714		return
715	}
716
717	var libs []string
718
719	currentVersion := ""
720	if len(i.properties.Versions) > 0 {
721		currentVersion = futureVersion
722	}
723
724	if i.shouldGenerateCppBackend() {
725		libs = append(libs, addCppLibrary(mctx, i, currentVersion, langCpp))
726		for _, version := range i.properties.Versions {
727			addCppLibrary(mctx, i, version, langCpp)
728		}
729	}
730
731	if i.shouldGenerateNdkBackend() {
732		// TODO(b/119771576): inherit properties and export 'is vendor' computation from cc.go
733		if !proptools.Bool(i.properties.Vendor_available) {
734			libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdk))
735			for _, version := range i.properties.Versions {
736				addCppLibrary(mctx, i, version, langNdk)
737			}
738		}
739		// TODO(b/121157555): combine with '-ndk' variant
740		libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdkPlatform))
741		for _, version := range i.properties.Versions {
742			addCppLibrary(mctx, i, version, langNdkPlatform)
743		}
744	}
745
746	libs = append(libs, addJavaLibrary(mctx, i, currentVersion))
747	for _, version := range i.properties.Versions {
748		addJavaLibrary(mctx, i, version)
749	}
750
751	addApiModule(mctx, i)
752
753	// Reserve this module name for future use
754	mctx.CreateModule(android.ModuleFactoryAdaptor(phony.PhonyFactory), &phonyProperties{
755		Name:     proptools.StringPtr(i.ModuleBase.Name()),
756		Required: libs,
757	})
758}
759
760func addCppLibrary(mctx android.LoadHookContext, i *aidlInterface, version string, lang string) string {
761	cppSourceGen := i.versionedName(version) + "-" + lang + "-source"
762	cppModuleGen := i.versionedName(version) + "-" + lang
763
764	srcs, base := i.srcsForVersion(mctx, version)
765	if len(srcs) == 0 {
766		// This can happen when the version is about to be frozen; the version
767		// directory is created but API dump hasn't been copied there.
768		// Don't create a library for the yet-to-be-frozen version.
769		return ""
770	}
771
772	genLog := false
773	if lang == langCpp {
774		genLog = proptools.Bool(i.properties.Backend.Cpp.Gen_log)
775	} else if lang == langNdk || lang == langNdkPlatform {
776		genLog = proptools.Bool(i.properties.Backend.Ndk.Gen_log)
777	}
778
779	mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
780		Name: proptools.StringPtr(cppSourceGen),
781	}, &aidlGenProperties{
782		Srcs:     srcs,
783		AidlRoot: base,
784		Imports:  concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
785		Lang:     lang,
786		BaseName: i.ModuleBase.Name(),
787		GenLog:   genLog,
788		Version:  version,
789	})
790
791	importExportDependencies := wrap("", i.properties.Imports, "-"+lang)
792	var libJSONCppDependency []string
793	var staticLibDependency []string
794	var sdkVersion *string
795	var stl *string
796	var cpp_std *string
797	if lang == langCpp {
798		importExportDependencies = append(importExportDependencies, "libbinder", "libutils")
799		if genLog {
800			libJSONCppDependency = []string{"libjsoncpp"}
801		}
802		sdkVersion = nil
803		stl = nil
804		cpp_std = nil
805	} else if lang == langNdk {
806		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
807		if genLog {
808			staticLibDependency = []string{"libjsoncpp_ndk"}
809		}
810		sdkVersion = proptools.StringPtr("current")
811		stl = proptools.StringPtr("c++_shared")
812	} else if lang == langNdkPlatform {
813		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
814		if genLog {
815			libJSONCppDependency = []string{"libjsoncpp"}
816		}
817	} else {
818		panic("Unrecognized language: " + lang)
819	}
820
821	mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
822		Name:                      proptools.StringPtr(cppModuleGen),
823		Owner:                     i.properties.Owner,
824		Vendor_available:          i.properties.Vendor_available,
825		Defaults:                  []string{"aidl-cpp-module-defaults"},
826		Generated_sources:         []string{cppSourceGen},
827		Generated_headers:         []string{cppSourceGen},
828		Export_generated_headers:  []string{cppSourceGen},
829		Static:                    staticLib{Whole_static_libs: libJSONCppDependency},
830		Shared:                    sharedLib{Shared_libs: libJSONCppDependency, Export_shared_lib_headers: libJSONCppDependency},
831		Static_libs:               staticLibDependency,
832		Shared_libs:               importExportDependencies,
833		Export_shared_lib_headers: importExportDependencies,
834		Sdk_version:               sdkVersion,
835		Stl:                       stl,
836		Cpp_std:                   cpp_std,
837		Cflags:                    []string{"-Wextra", "-Wall", "-Werror"},
838	}, &i.properties.VndkProperties)
839
840	return cppModuleGen
841}
842
843func addJavaLibrary(mctx android.LoadHookContext, i *aidlInterface, version string) string {
844	javaSourceGen := i.versionedName(version) + "-java-source"
845	javaModuleGen := i.versionedName(version) + "-java"
846
847	srcs, base := i.srcsForVersion(mctx, version)
848	if len(srcs) == 0 {
849		// This can happen when the version is about to be frozen; the version
850		// directory is created but API dump hasn't been copied there.
851		// Don't create a library for the yet-to-be-frozen version.
852		return ""
853	}
854
855	sdkVersion := proptools.StringDefault(i.properties.Backend.Java.Sdk_version, "system_current")
856
857	mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
858		Name: proptools.StringPtr(javaSourceGen),
859	}, &aidlGenProperties{
860		Srcs:     srcs,
861		AidlRoot: base,
862		Imports:  concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
863		Lang:     langJava,
864		BaseName: i.ModuleBase.Name(),
865		Version:  version,
866	})
867
868	mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory), &javaProperties{
869		Name:              proptools.StringPtr(javaModuleGen),
870		Owner:             i.properties.Owner,
871		Installable:       proptools.BoolPtr(true),
872		Defaults:          []string{"aidl-java-module-defaults"},
873		No_framework_libs: proptools.BoolPtr(true),
874		Sdk_version:       proptools.StringPtr(sdkVersion),
875		Static_libs:       wrap("", i.properties.Imports, "-java"),
876		Srcs:              []string{":" + javaSourceGen},
877	})
878
879	return javaModuleGen
880}
881
882func addApiModule(mctx android.LoadHookContext, i *aidlInterface) string {
883	apiModule := i.ModuleBase.Name() + aidlApiSuffix
884	mctx.CreateModule(android.ModuleFactoryAdaptor(aidlApiFactory), &nameProperties{
885		Name: proptools.StringPtr(apiModule),
886	}, &aidlApiProperties{
887		BaseName: i.ModuleBase.Name(),
888		Inputs:   i.rawSrcs,
889		Imports:  concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
890		Api_dir:  i.properties.Api_dir,
891		AidlRoot: i.properties.Local_include_dir,
892		Versions: i.properties.Versions,
893	})
894	return apiModule
895}
896
897func (i *aidlInterface) Name() string {
898	return i.ModuleBase.Name() + aidlInterfaceSuffix
899}
900func (i *aidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
901}
902func (i *aidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) {
903}
904
905var aidlInterfaceMutex sync.Mutex
906var aidlInterfaces []*aidlInterface
907
908func aidlInterfaceFactory() android.Module {
909	i := &aidlInterface{}
910	i.AddProperties(&i.properties)
911	android.InitAndroidModule(i)
912	android.AddLoadHook(i, func(ctx android.LoadHookContext) { aidlInterfaceHook(ctx, i) })
913
914	aidlInterfaceMutex.Lock()
915	aidlInterfaces = append(aidlInterfaces, i)
916	aidlInterfaceMutex.Unlock()
917
918	return i
919}
920
921func lookupInterface(name string) *aidlInterface {
922	for _, i := range aidlInterfaces {
923		if i.ModuleBase.Name() == name {
924			return i
925		}
926	}
927	return nil
928}
929
930type aidlMappingProperties struct {
931	// Source file of this prebuilt.
932	Srcs   []string `android:"arch_variant"`
933	Output string
934}
935
936type aidlMapping struct {
937	android.ModuleBase
938	properties     aidlMappingProperties
939	outputFilePath android.WritablePath
940}
941
942func (s *aidlMapping) DepsMutator(ctx android.BottomUpMutatorContext) {
943	android.ExtractSourcesDeps(ctx, s.properties.Srcs)
944}
945
946func addItemsToMap(dest map[string]bool, src []string) {
947	for _, item := range src {
948		dest[item] = true
949	}
950}
951
952func (s *aidlMapping) GenerateAndroidBuildActions(ctx android.ModuleContext) {
953	var srcs android.Paths
954	var all_import_dirs map[string]bool = make(map[string]bool)
955
956	ctx.VisitDirectDeps(func(module android.Module) {
957		for _, property := range module.GetProperties() {
958			if jproperty, ok := property.(*java.CompilerProperties); ok {
959				for _, src := range jproperty.Srcs {
960					if strings.HasSuffix(src, ".aidl") {
961						full_path := android.PathForModuleSrc(ctx, src)
962						srcs = append(srcs, full_path)
963						all_import_dirs[filepath.Dir(full_path.String())] = true
964					} else if pathtools.IsGlob(src) {
965						globbedSrcFiles, err := ctx.GlobWithDeps(src, nil)
966						if err == nil {
967							for _, globbedSrc := range globbedSrcFiles {
968								full_path := android.PathForModuleSrc(ctx, globbedSrc)
969								all_import_dirs[full_path.String()] = true
970							}
971						}
972					}
973				}
974			} else if jproperty, ok := property.(*java.CompilerDeviceProperties); ok {
975				addItemsToMap(all_import_dirs, jproperty.Aidl.Include_dirs)
976				for _, include_dir := range jproperty.Aidl.Export_include_dirs {
977					var full_path = filepath.Join(ctx.ModuleDir(), include_dir)
978					all_import_dirs[full_path] = true
979				}
980				for _, include_dir := range jproperty.Aidl.Local_include_dirs {
981					var full_path = filepath.Join(ctx.ModuleSubDir(), include_dir)
982					all_import_dirs[full_path] = true
983				}
984			}
985		}
986	})
987
988	var import_dirs []string
989	for dir := range all_import_dirs {
990		import_dirs = append(import_dirs, dir)
991	}
992	imports := strings.Join(wrap("-I", import_dirs, ""), " ")
993	s.outputFilePath = android.PathForModuleOut(ctx, s.properties.Output)
994	outDir := android.PathForModuleGen(ctx)
995	ctx.Build(pctx, android.BuildParams{
996		Rule:   aidlDumpMappingsRule,
997		Inputs: srcs,
998		Output: s.outputFilePath,
999		Args: map[string]string{
1000			"imports": imports,
1001			"outDir":  outDir.String(),
1002		},
1003	})
1004}
1005
1006func InitAidlMappingModule(s *aidlMapping) {
1007	s.AddProperties(&s.properties)
1008}
1009
1010func aidlMappingFactory() android.Module {
1011	module := &aidlMapping{}
1012	InitAidlMappingModule(module)
1013	android.InitAndroidModule(module)
1014	return module
1015}
1016
1017func (m *aidlMapping) AndroidMk() android.AndroidMkData {
1018	return android.AndroidMkData{
1019		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
1020			android.WriteAndroidMkData(w, data)
1021			targetName := m.Name()
1022			fmt.Fprintln(w, ".PHONY:", targetName)
1023			fmt.Fprintln(w, targetName+":", m.outputFilePath.String())
1024		},
1025	}
1026}
1027