• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/genrule"
20
21	"path/filepath"
22	"strings"
23
24	"github.com/google/blueprint"
25	"github.com/google/blueprint/pathtools"
26	"github.com/google/blueprint/proptools"
27)
28
29var (
30	aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{
31		Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
32			`touch ${out} # ${in}`,
33		Description: "create ${out}",
34	}, "outDir")
35
36	aidlCppRule = pctx.StaticRule("aidlCppRule", blueprint.RuleParams{
37		Command: `mkdir -p "${headerDir}" && ` +
38			`${aidlCmd} --lang=${lang} ${optionalFlags} --structured --ninja -d ${out}.d ` +
39			`-h ${headerDir} -o ${outDir} ${imports} ${in}`,
40		Depfile:     "${out}.d",
41		Deps:        blueprint.DepsGCC,
42		CommandDeps: []string{"${aidlCmd}"},
43		Description: "AIDL ${lang} ${in}",
44	}, "imports", "lang", "headerDir", "outDir", "optionalFlags")
45
46	aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{
47		Command: `${aidlCmd} --lang=java ${optionalFlags} --structured --ninja -d ${out}.d ` +
48			`-o ${outDir} ${imports} ${in}`,
49		Depfile:     "${out}.d",
50		Deps:        blueprint.DepsGCC,
51		CommandDeps: []string{"${aidlCmd}"},
52		Description: "AIDL Java ${in}",
53	}, "imports", "outDir", "optionalFlags")
54
55	aidlRustRule = pctx.StaticRule("aidlRustRule", blueprint.RuleParams{
56		Command: `${aidlCmd} --lang=rust ${optionalFlags} --structured --ninja -d ${out}.d ` +
57			`-o ${outDir} ${imports} ${in}`,
58		Depfile:     "${out}.d",
59		Deps:        blueprint.DepsGCC,
60		CommandDeps: []string{"${aidlCmd}"},
61		Description: "AIDL Rust ${in}",
62	}, "imports", "outDir", "optionalFlags")
63)
64
65type aidlGenProperties struct {
66	Srcs                  []string `android:"path"`
67	AidlRoot              string   // base directory for the input aidl file
68	IsToT                 bool
69	ImportsWithoutVersion []string
70	Stability             *string
71	Lang                  string // target language [java|cpp|ndk|rust]
72	BaseName              string
73	GenLog                bool
74	Version               string
75	GenTrace              bool
76	Unstable              *bool
77	Visibility            []string
78	Flags                 []string
79}
80
81type aidlGenRule struct {
82	android.ModuleBase
83
84	properties aidlGenProperties
85
86	implicitInputs android.Paths
87	importFlags    string
88
89	// TODO(b/149952131): always have a hash file
90	hashFile android.Path
91
92	genOutDir     android.ModuleGenPath
93	genHeaderDir  android.ModuleGenPath
94	genHeaderDeps android.Paths
95	genOutputs    android.WritablePaths
96}
97
98var _ android.SourceFileProducer = (*aidlGenRule)(nil)
99var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil)
100
101func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
102	srcs, imports := getPaths(ctx, g.properties.Srcs, g.properties.AidlRoot)
103
104	if ctx.Failed() {
105		return
106	}
107
108	genDirTimestamp := android.PathForModuleGen(ctx, "timestamp") // $out/gen/timestamp
109	g.implicitInputs = append(g.implicitInputs, genDirTimestamp)
110
111	importPaths, implicits := getImportsFromDeps(ctx, g.properties.IsToT)
112	imports = append(imports, importPaths...)
113
114	g.importFlags = strings.Join(wrap("-I", imports, ""), " ")
115
116	g.genOutDir = android.PathForModuleGen(ctx)
117	g.genHeaderDir = android.PathForModuleGen(ctx, "include")
118	for _, src := range srcs {
119		outFile, headers := g.generateBuildActionsForSingleAidl(ctx, src)
120		g.genOutputs = append(g.genOutputs, outFile)
121		g.genHeaderDeps = append(g.genHeaderDeps, headers...)
122	}
123
124	// This is to clean genOutDir before generating any file
125	ctx.Build(pctx, android.BuildParams{
126		Rule:      aidlDirPrepareRule,
127		Implicits: implicits,
128		Inputs:    srcs,
129		Output:    genDirTimestamp,
130		Args: map[string]string{
131			"outDir": g.genOutDir.String(),
132		},
133	})
134
135	// This is to trigger genrule alone
136	ctx.Build(pctx, android.BuildParams{
137		Rule:   android.Phony,
138		Output: android.PathForModuleOut(ctx, "timestamp"), // $out/timestamp
139		Inputs: g.genOutputs.Paths(),
140	})
141}
142
143func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) {
144	baseDir := getBaseDir(ctx, src, android.PathForModuleSrc(ctx, g.properties.AidlRoot))
145
146	var ext string
147	if g.properties.Lang == langJava {
148		ext = "java"
149	} else if g.properties.Lang == langRust {
150		ext = "rs"
151	} else {
152		ext = "cpp"
153	}
154	relPath, _ := filepath.Rel(baseDir, src.String())
155	outFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension(relPath, ext))
156	implicits := g.implicitInputs
157
158	optionalFlags := append([]string{}, g.properties.Flags...)
159	if g.properties.Version != "" {
160		optionalFlags = append(optionalFlags, "--version "+g.properties.Version)
161
162		hash := "notfrozen"
163		if !strings.HasPrefix(baseDir, ctx.Config().BuildDir()) {
164			hashFile := android.ExistentPathForSource(ctx, baseDir, ".hash")
165			if hashFile.Valid() {
166				hash = "$$(read -r <" + hashFile.Path().String() + " hash extra; printf '%s' \"$$hash\")"
167				implicits = append(implicits, hashFile.Path())
168
169				g.hashFile = hashFile.Path()
170			}
171		}
172		optionalFlags = append(optionalFlags, "--hash "+hash)
173	}
174	if g.properties.GenTrace {
175		optionalFlags = append(optionalFlags, "-t")
176	}
177	if g.properties.Stability != nil {
178		optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability)
179	}
180
181	var headers android.WritablePaths
182	if g.properties.Lang == langJava {
183		ctx.Build(pctx, android.BuildParams{
184			Rule:      aidlJavaRule,
185			Input:     src,
186			Implicits: implicits,
187			Output:    outFile,
188			Args: map[string]string{
189				"imports":       g.importFlags,
190				"outDir":        g.genOutDir.String(),
191				"optionalFlags": strings.Join(optionalFlags, " "),
192			},
193		})
194	} else if g.properties.Lang == langRust {
195		ctx.Build(pctx, android.BuildParams{
196			Rule:      aidlRustRule,
197			Input:     src,
198			Implicits: implicits,
199			Output:    outFile,
200			Args: map[string]string{
201				"imports":       g.importFlags,
202				"outDir":        g.genOutDir.String(),
203				"optionalFlags": strings.Join(optionalFlags, " "),
204			},
205		})
206	} else {
207		typeName := strings.TrimSuffix(filepath.Base(relPath), ".aidl")
208		packagePath := filepath.Dir(relPath)
209		baseName := typeName
210		// TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if
211		//   an interface name has a leading I. Those same heuristics have been
212		//   moved here.
213		if len(baseName) >= 2 && baseName[0] == 'I' &&
214			strings.ToUpper(baseName)[1] == baseName[1] {
215			baseName = strings.TrimPrefix(typeName, "I")
216		}
217
218		prefix := ""
219		if g.properties.Lang == langNdk || g.properties.Lang == langNdkPlatform {
220			prefix = "aidl"
221		}
222
223		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
224			typeName+".h"))
225		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
226			"Bp"+baseName+".h"))
227		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
228			"Bn"+baseName+".h"))
229
230		if g.properties.GenLog {
231			optionalFlags = append(optionalFlags, "--log")
232		}
233
234		aidlLang := g.properties.Lang
235		if aidlLang == langNdkPlatform {
236			aidlLang = "ndk"
237		}
238
239		ctx.Build(pctx, android.BuildParams{
240			Rule:            aidlCppRule,
241			Input:           src,
242			Implicits:       implicits,
243			Output:          outFile,
244			ImplicitOutputs: headers,
245			Args: map[string]string{
246				"imports":       g.importFlags,
247				"lang":          aidlLang,
248				"headerDir":     g.genHeaderDir.String(),
249				"outDir":        g.genOutDir.String(),
250				"optionalFlags": strings.Join(optionalFlags, " "),
251			},
252		})
253	}
254
255	return outFile, headers.Paths()
256}
257
258func (g *aidlGenRule) GeneratedSourceFiles() android.Paths {
259	return g.genOutputs.Paths()
260}
261
262func (g *aidlGenRule) Srcs() android.Paths {
263	return g.genOutputs.Paths()
264}
265
266func (g *aidlGenRule) GeneratedDeps() android.Paths {
267	return g.genHeaderDeps
268}
269
270func (g *aidlGenRule) GeneratedHeaderDirs() android.Paths {
271	return android.Paths{g.genHeaderDir}
272}
273
274func (g *aidlGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
275	// original interface
276	ctx.AddDependency(ctx.Module(), interfaceDep, g.properties.BaseName+aidlInterfaceSuffix)
277
278	if !proptools.Bool(g.properties.Unstable) {
279		// for checkapi timestamps
280		ctx.AddDependency(ctx.Module(), apiDep, g.properties.BaseName+aidlApiSuffix)
281	}
282
283	// imported interfaces
284	ctx.AddDependency(ctx.Module(), importInterfaceDep, wrap("", g.properties.ImportsWithoutVersion, aidlInterfaceSuffix)...)
285
286	ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName)
287}
288
289func aidlGenFactory() android.Module {
290	g := &aidlGenRule{}
291	g.AddProperties(&g.properties)
292	android.InitAndroidModule(g)
293	return g
294}
295