• 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	"strconv"
21
22	"path/filepath"
23	"strings"
24
25	"github.com/google/blueprint"
26	"github.com/google/blueprint/pathtools"
27	"github.com/google/blueprint/proptools"
28)
29
30var (
31	aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{
32		Command:     `mkdir -p "${outDir}" && touch ${out} # ${in}`,
33		Description: "create ${out}",
34	}, "outDir")
35
36	aidlCppRule = pctx.StaticRule("aidlCppRule", blueprint.RuleParams{
37		Command: `mkdir -p "${headerDir}" && ` +
38			`mkdir -p "${outDir}/staging" && ` +
39			`mkdir -p "${headerDir}/staging" && ` +
40			`${aidlCmd} --lang=${lang} ${optionalFlags} --ninja -d ${outStagingFile}.d ` +
41			`-h ${headerDir}/staging -o ${outDir}/staging ${imports} ${nextImports} ${in} && ` +
42			`rsync --checksum ${outStagingFile}.d ${out}.d && ` +
43			`rsync --checksum ${outStagingFile} ${out} && ` +
44			`( [ -z "${stagingHeaders}" ] || rsync --checksum ${stagingHeaders} ${fullHeaderDir} ) && ` +
45			`sed -i 's/\/gen\/staging\//\/gen\//g' ${out}.d && ` +
46			`rm ${outStagingFile} ${outStagingFile}.d ${stagingHeaders}`,
47		Depfile:     "${out}.d",
48		Deps:        blueprint.DepsGCC,
49		CommandDeps: []string{"${aidlCmd}"},
50		Restat:      true,
51		Description: "AIDL ${lang} ${in}",
52	}, "imports", "nextImports", "lang", "headerDir", "outDir", "optionalFlags", "stagingHeaders", "outStagingFile",
53		"fullHeaderDir")
54
55	aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{
56		Command: `${aidlCmd} --lang=java ${optionalFlags} --ninja -d ${out}.d ` +
57			`-o ${outDir} ${imports} ${nextImports} ${in}`,
58		Depfile:     "${out}.d",
59		Deps:        blueprint.DepsGCC,
60		CommandDeps: []string{"${aidlCmd}"},
61		Restat:      true,
62		Description: "AIDL Java ${in}",
63	}, "imports", "nextImports", "outDir", "optionalFlags")
64
65	aidlRustRule = pctx.StaticRule("aidlRustRule", blueprint.RuleParams{
66		Command: `${aidlCmd} --lang=rust ${optionalFlags} --ninja -d ${out}.d ` +
67			`-o ${outDir} ${imports} ${nextImports} ${in}`,
68		Depfile:     "${out}.d",
69		Deps:        blueprint.DepsGCC,
70		CommandDeps: []string{"${aidlCmd}"},
71		Restat:      true,
72		Description: "AIDL Rust ${in}",
73	}, "imports", "nextImports", "outDir", "optionalFlags")
74)
75
76type aidlGenProperties struct {
77	Srcs                []string `android:"path"`
78	AidlRoot            string   // base directory for the input aidl file
79	Imports             []string
80	Headers             []string
81	Stability           *string
82	Min_sdk_version     *string
83	Platform_apis       bool
84	Lang                string // target language [java|cpp|ndk|rust]
85	BaseName            string
86	GenLog              bool
87	Version             string
88	GenRpc              bool
89	GenTrace            bool
90	Unstable            *bool
91	NotFrozen           bool
92	RequireFrozenReason string
93	Visibility          []string
94	Flags               []string
95	UseUnfrozen         bool
96}
97
98type aidlGenRule struct {
99	android.ModuleBase
100
101	properties aidlGenProperties
102
103	deps            deps
104	implicitInputs  android.Paths
105	importFlags     string
106	nextImportFlags string
107
108	// A frozen aidl_interface always have a hash file
109	hashFile android.Path
110
111	genOutDir     android.ModuleGenPath
112	genHeaderDir  android.ModuleGenPath
113	genHeaderDeps android.Paths
114	genOutputs    android.WritablePaths
115}
116
117var _ android.SourceFileProducer = (*aidlGenRule)(nil)
118var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil)
119
120func (g *aidlGenRule) aidlInterface(ctx android.BaseModuleContext) *aidlInterface {
121	return ctx.GetDirectDepWithTag(g.properties.BaseName, interfaceDep).(*aidlInterface)
122}
123
124func (g *aidlGenRule) getImports(ctx android.ModuleContext) map[string]string {
125	iface := g.aidlInterface(ctx)
126	return iface.getImports(g.properties.Version)
127}
128
129func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
130	srcs, nextImports := getPaths(ctx, g.properties.Srcs, g.properties.AidlRoot)
131
132	g.deps = getDeps(ctx, g.getImports(ctx))
133
134	if ctx.Failed() {
135		return
136	}
137
138	genDirTimestamp := android.PathForModuleGen(ctx, "timestamp") // $out/gen/timestamp
139	g.implicitInputs = append(g.implicitInputs, genDirTimestamp)
140	g.implicitInputs = append(g.implicitInputs, g.deps.implicits...)
141	g.implicitInputs = append(g.implicitInputs, g.deps.preprocessed...)
142
143	g.nextImportFlags = strings.Join(wrap("-N", nextImports, ""), " ")
144	g.importFlags = strings.Join(wrap("-I", g.deps.imports, ""), " ")
145
146	g.genOutDir = android.PathForModuleGen(ctx)
147	g.genHeaderDir = android.PathForModuleGen(ctx, "include")
148	for _, src := range srcs {
149		outFile, headers := g.generateBuildActionsForSingleAidl(ctx, src)
150		g.genOutputs = append(g.genOutputs, outFile)
151		g.genHeaderDeps = append(g.genHeaderDeps, headers...)
152	}
153
154	// This is to clean genOutDir before generating any file
155	ctx.Build(pctx, android.BuildParams{
156		Rule:   aidlDirPrepareRule,
157		Inputs: srcs,
158		Output: genDirTimestamp,
159		Args: map[string]string{
160			"outDir": g.genOutDir.String(),
161		},
162	})
163
164	// This is to trigger genrule alone
165	ctx.Build(pctx, android.BuildParams{
166		Rule:   android.Phony,
167		Output: android.PathForModuleOut(ctx, "timestamp"), // $out/timestamp
168		Inputs: g.genOutputs.Paths(),
169	})
170}
171
172func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) {
173	relPath := src.Rel()
174	baseDir := strings.TrimSuffix(strings.TrimSuffix(src.String(), relPath), "/")
175
176	var ext string
177	if g.properties.Lang == langJava {
178		ext = "java"
179	} else if g.properties.Lang == langRust {
180		ext = "rs"
181	} else {
182		ext = "cpp"
183	}
184	outFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension(relPath, ext))
185	outStagingFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension("staging/"+relPath, ext))
186	implicits := g.implicitInputs
187
188	// default version is 1 for any stable interface
189	version := "1"
190	previousVersion := ""
191	previousApiDir := ""
192	if g.properties.Version != "" {
193		version = g.properties.Version
194	}
195	versionInt, err := strconv.Atoi(version)
196	if err != nil && g.properties.Version != "" {
197		ctx.PropertyErrorf(g.properties.Version, "Invalid Version string: %s", g.properties.Version)
198	} else if err == nil && versionInt > 1 {
199		previousVersion = strconv.Itoa(versionInt - 1)
200		previousApiDir = filepath.Join(ctx.ModuleDir(), aidlApiDir, g.properties.BaseName, previousVersion)
201	}
202
203	optionalFlags := append([]string{}, g.properties.Flags...)
204	if proptools.Bool(g.properties.Unstable) != true {
205		optionalFlags = append(optionalFlags, "--structured")
206		optionalFlags = append(optionalFlags, "--version "+version)
207		hash := "notfrozen"
208		if !strings.HasPrefix(baseDir, ctx.Config().SoongOutDir()) {
209			hashFile := android.ExistentPathForSource(ctx, baseDir, ".hash")
210			if hashFile.Valid() {
211				hash = "$$(tail -1 '" + hashFile.Path().String() + "')"
212				implicits = append(implicits, hashFile.Path())
213
214				g.hashFile = hashFile.Path()
215			}
216		}
217		optionalFlags = append(optionalFlags, "--hash "+hash)
218	}
219	if g.properties.GenRpc {
220		optionalFlags = append(optionalFlags, "--rpc")
221	}
222	if g.properties.GenTrace {
223		optionalFlags = append(optionalFlags, "-t")
224	}
225	if g.properties.Stability != nil {
226		optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability)
227	}
228	if g.properties.Platform_apis {
229		optionalFlags = append(optionalFlags, "--min_sdk_version platform_apis")
230	} else {
231		minSdkVer := proptools.StringDefault(g.properties.Min_sdk_version, "current")
232		optionalFlags = append(optionalFlags, "--min_sdk_version "+minSdkVer)
233	}
234	optionalFlags = append(optionalFlags, wrap("-p", g.deps.preprocessed.Strings(), "")...)
235
236	// If this is an unfrozen version of a previously frozen interface, we want (1) the location
237	// of the previously frozen source and (2) the previously frozen hash so the generated
238	// library can behave like both versions at run time.
239	if !g.properties.UseUnfrozen && previousVersion != "" &&
240		!proptools.Bool(g.properties.Unstable) && g.hashFile == nil {
241		apiDirPath := android.ExistentPathForSource(ctx, previousApiDir)
242		if apiDirPath.Valid() {
243			optionalFlags = append(optionalFlags, "--previous_api_dir="+apiDirPath.Path().String())
244		} else {
245			ctx.PropertyErrorf("--previous_api_dir is invalid: %s", apiDirPath.Path().String())
246		}
247		hashFile := android.ExistentPathForSource(ctx, previousApiDir, ".hash")
248		if hashFile.Valid() {
249			previousHash := "$$(tail -1 '" + hashFile.Path().String() + "')"
250			implicits = append(implicits, hashFile.Path())
251			optionalFlags = append(optionalFlags, "--previous_hash "+previousHash)
252		} else {
253			ctx.ModuleErrorf("Failed to find previous version's hash file in %s", previousApiDir)
254		}
255	}
256
257	var headers android.WritablePaths
258	if g.properties.Lang == langJava {
259		ctx.Build(pctx, android.BuildParams{
260			Rule:      aidlJavaRule,
261			Input:     src,
262			Implicits: implicits,
263			Output:    outFile,
264			Args: map[string]string{
265				"imports":       g.importFlags,
266				"nextImports":   g.nextImportFlags,
267				"outDir":        g.genOutDir.String(),
268				"optionalFlags": strings.Join(optionalFlags, " "),
269			},
270		})
271	} else if g.properties.Lang == langRust {
272		ctx.Build(pctx, android.BuildParams{
273			Rule:      aidlRustRule,
274			Input:     src,
275			Implicits: implicits,
276			Output:    outFile,
277			Args: map[string]string{
278				"imports":       g.importFlags,
279				"nextImports":   g.nextImportFlags,
280				"outDir":        g.genOutDir.String(),
281				"optionalFlags": strings.Join(optionalFlags, " "),
282			},
283		})
284	} else {
285		typeName := strings.TrimSuffix(filepath.Base(relPath), ".aidl")
286		packagePath := filepath.Dir(relPath)
287		baseName := typeName
288		// TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if
289		//   an interface name has a leading I. Those same heuristics have been
290		//   moved here.
291		if len(baseName) >= 2 && baseName[0] == 'I' &&
292			strings.ToUpper(baseName)[1] == baseName[1] {
293			baseName = strings.TrimPrefix(typeName, "I")
294		}
295
296		prefix := ""
297		if g.properties.Lang == langNdk || g.properties.Lang == langNdkPlatform {
298			prefix = "aidl"
299		}
300
301		var stagingHeaders []string
302		var fullHeaderDir = g.genHeaderDir.Join(ctx, prefix, packagePath)
303		if g.properties.Lang != langCppAnalyzer {
304			headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, typeName+".h"))
305			stagingHeaders = append(stagingHeaders, g.genHeaderDir.Join(ctx, "staging/"+prefix, packagePath, typeName+".h").String())
306			headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, "Bp"+baseName+".h"))
307			stagingHeaders = append(stagingHeaders, g.genHeaderDir.Join(ctx, "staging/"+prefix, packagePath, "Bp"+baseName+".h").String())
308			headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath, "Bn"+baseName+".h"))
309			stagingHeaders = append(stagingHeaders, g.genHeaderDir.Join(ctx, "staging/"+prefix, packagePath, "Bn"+baseName+".h").String())
310		}
311
312		if g.properties.GenLog {
313			optionalFlags = append(optionalFlags, "--log")
314		}
315
316		aidlLang := g.properties.Lang
317		if aidlLang == langNdkPlatform {
318			aidlLang = "ndk"
319		}
320
321		ctx.Build(pctx, android.BuildParams{
322			Rule:            aidlCppRule,
323			Input:           src,
324			Implicits:       implicits,
325			Output:          outFile,
326			ImplicitOutputs: headers,
327			Args: map[string]string{
328				"imports":        g.importFlags,
329				"nextImports":    g.nextImportFlags,
330				"lang":           aidlLang,
331				"headerDir":      g.genHeaderDir.String(),
332				"fullHeaderDir":  fullHeaderDir.String(),
333				"outDir":         g.genOutDir.String(),
334				"outStagingFile": outStagingFile.String(),
335				"optionalFlags":  strings.Join(optionalFlags, " "),
336				"stagingHeaders": strings.Join(stagingHeaders, " "),
337			},
338		})
339	}
340
341	return outFile, headers.Paths()
342}
343
344func (g *aidlGenRule) GeneratedSourceFiles() android.Paths {
345	return g.genOutputs.Paths()
346}
347
348func (g *aidlGenRule) Srcs() android.Paths {
349	return g.genOutputs.Paths()
350}
351
352func (g *aidlGenRule) GeneratedDeps() android.Paths {
353	return g.genHeaderDeps
354}
355
356func (g *aidlGenRule) GeneratedHeaderDirs() android.Paths {
357	return android.Paths{g.genHeaderDir}
358}
359
360func (g *aidlGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
361	ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName)
362}
363func aidlGenFactory() android.Module {
364	g := &aidlGenRule{}
365	g.AddProperties(&g.properties)
366	android.InitAndroidModule(g)
367	return g
368}
369