blob: 18d2d5390ac635dab2aba8bf6d9ae904123c760b [file] [log] [blame]
Jeongik Chada36d5a2021-01-20 00:43:34 +09001// 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
20 "fmt"
21 "io"
22 "path/filepath"
23 "strconv"
24 "strings"
25
26 "github.com/google/blueprint"
27 "github.com/google/blueprint/proptools"
28)
29
30var (
31 aidlDumpApiRule = pctx.StaticRule("aidlDumpApiRule", blueprint.RuleParams{
32 Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
33 `${aidlCmd} --dumpapi --structured ${imports} ${optionalFlags} --out ${outDir} ${in} && ` +
Jeongik Cha6cc16f32021-05-18 11:23:55 +090034 `${aidlHashGen} ${outDir} ${latestVersion} ${hashFile}`,
35 CommandDeps: []string{"${aidlCmd}", "${aidlHashGen}"},
Jeongik Chada36d5a2021-01-20 00:43:34 +090036 }, "optionalFlags", "imports", "outDir", "hashFile", "latestVersion")
37
38 aidlCheckApiRule = pctx.StaticRule("aidlCheckApiRule", blueprint.RuleParams{
Jooyung Han8e3b72c2021-05-22 02:54:37 +090039 Command: `(${aidlCmd} ${optionalFlags} --checkapi=${checkApiLevel} ${imports} ${old} ${new} && touch ${out}) || ` +
Jeongik Chada36d5a2021-01-20 00:43:34 +090040 `(cat ${messageFile} && exit 1)`,
41 CommandDeps: []string{"${aidlCmd}"},
42 Description: "AIDL CHECK API: ${new} against ${old}",
Jooyung Han8e3b72c2021-05-22 02:54:37 +090043 }, "optionalFlags", "imports", "old", "new", "messageFile", "checkApiLevel")
Jeongik Chada36d5a2021-01-20 00:43:34 +090044
45 aidlVerifyHashRule = pctx.StaticRule("aidlVerifyHashRule", blueprint.RuleParams{
46 Command: `if [ $$(cd '${apiDir}' && { find ./ -name "*.aidl" -print0 | LC_ALL=C sort -z | xargs -0 sha1sum && echo ${version}; } | sha1sum | cut -d " " -f 1) = $$(read -r <'${hashFile}' hash extra; printf %s $$hash) ]; then ` +
47 `touch ${out}; else cat '${messageFile}' && exit 1; fi`,
48 Description: "Verify ${apiDir} files have not been modified",
49 }, "apiDir", "version", "messageFile", "hashFile")
50)
51
52type aidlApiProperties struct {
Jeongik Chaa01447d2021-03-17 17:43:12 +090053 BaseName string
54 Srcs []string `android:"path"`
55 AidlRoot string // base directory for the input aidl file
56 Stability *string
57 ImportsWithoutVersion []string
58 Versions []string
59 Dumpapi DumpApiProperties
Jeongik Chada36d5a2021-01-20 00:43:34 +090060}
61
62type aidlApi struct {
63 android.ModuleBase
64
65 properties aidlApiProperties
66
67 // for triggering api check for version X against version X-1
68 checkApiTimestamps android.WritablePaths
69
70 // for triggering updating current API
71 updateApiTimestamp android.WritablePath
72
73 // for triggering check that files have not been modified
74 checkHashTimestamps android.WritablePaths
75
76 // for triggering freezing API as the new version
77 freezeApiTimestamp android.WritablePath
Devin Moore15b8ebe2021-05-21 09:36:37 -070078
79 // for checking for active development on unfrozen version
80 hasDevelopment android.WritablePath
Jeongik Chada36d5a2021-01-20 00:43:34 +090081}
82
83func (m *aidlApi) apiDir() string {
84 return filepath.Join(aidlApiDir, m.properties.BaseName)
85}
86
87// `m <iface>-freeze-api` will freeze ToT as this version
88func (m *aidlApi) nextVersion() string {
Jeongik Cha52e98022021-01-20 18:37:20 +090089 return nextVersion(m.properties.Versions)
Jeongik Chada36d5a2021-01-20 00:43:34 +090090}
91
92type apiDump struct {
Jooyung Handf94f0f2021-06-07 18:57:10 +090093 version string
Jeongik Chada36d5a2021-01-20 00:43:34 +090094 dir android.Path
95 files android.Paths
96 hashFile android.OptionalPath
97}
98
Jooyung Handf94f0f2021-06-07 18:57:10 +090099func (m *aidlApi) getImports(ctx android.ModuleContext, version string) map[string]string {
100 iface := ctx.GetDirectDepWithTag(m.properties.BaseName, interfaceDep).(*aidlInterface)
101 return iface.getImports(version)
102}
103
Jeongik Chada36d5a2021-01-20 00:43:34 +0900104func (m *aidlApi) createApiDumpFromSource(ctx android.ModuleContext) apiDump {
Jooyung Han60699b52021-05-26 22:20:42 +0900105 srcs, imports := getPaths(ctx, m.properties.Srcs, m.properties.AidlRoot)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900106
107 if ctx.Failed() {
108 return apiDump{}
109 }
110
Jooyung Handf94f0f2021-06-07 18:57:10 +0900111 // dumpapi uses imports for ToT("") version
112 deps := getDeps(ctx, m.getImports(ctx, m.nextVersion()))
Jooyung Han42c5f292021-06-04 19:49:34 +0900113 imports = append(imports, deps.imports...)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900114
115 var apiDir android.WritablePath
116 var apiFiles android.WritablePaths
117 var hashFile android.WritablePath
118
119 apiDir = android.PathForModuleOut(ctx, "dump")
Jeongik Chada36d5a2021-01-20 00:43:34 +0900120 for _, src := range srcs {
Jooyung Hanaf45dbb2021-06-03 13:56:24 +0900121 outFile := android.PathForModuleOut(ctx, "dump", src.Rel())
Jeongik Chada36d5a2021-01-20 00:43:34 +0900122 apiFiles = append(apiFiles, outFile)
123 }
124 hashFile = android.PathForModuleOut(ctx, "dump", ".hash")
Jeongik Chada36d5a2021-01-20 00:43:34 +0900125
126 var optionalFlags []string
127 if m.properties.Stability != nil {
128 optionalFlags = append(optionalFlags, "--stability", *m.properties.Stability)
129 }
Jooyung Han252657e2021-02-27 02:51:39 +0900130 if proptools.Bool(m.properties.Dumpapi.No_license) {
131 optionalFlags = append(optionalFlags, "--no_license")
132 }
Jooyung Han42c5f292021-06-04 19:49:34 +0900133 optionalFlags = append(optionalFlags, wrap("-p", deps.preprocessed.Strings(), "")...)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900134
Jooyung Handf94f0f2021-06-07 18:57:10 +0900135 version := nextVersion(m.properties.Versions)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900136 ctx.Build(pctx, android.BuildParams{
Jooyung Han42c5f292021-06-04 19:49:34 +0900137 Rule: aidlDumpApiRule,
138 Outputs: append(apiFiles, hashFile),
139 Inputs: srcs,
140 Implicits: deps.preprocessed,
Jeongik Chada36d5a2021-01-20 00:43:34 +0900141 Args: map[string]string{
142 "optionalFlags": strings.Join(optionalFlags, " "),
Jooyung Han60699b52021-05-26 22:20:42 +0900143 "imports": strings.Join(wrap("-I", imports, ""), " "),
Jeongik Chada36d5a2021-01-20 00:43:34 +0900144 "outDir": apiDir.String(),
145 "hashFile": hashFile.String(),
Jooyung Handf94f0f2021-06-07 18:57:10 +0900146 "latestVersion": versionForHashGen(version),
Jeongik Chada36d5a2021-01-20 00:43:34 +0900147 },
148 })
Jooyung Handf94f0f2021-06-07 18:57:10 +0900149 return apiDump{version, apiDir, apiFiles.Paths(), android.OptionalPathForPath(hashFile)}
Jeongik Chada36d5a2021-01-20 00:43:34 +0900150}
151
Jiyong Park72e08932021-05-21 00:18:20 +0900152func (m *aidlApi) makeApiDumpAsVersion(ctx android.ModuleContext, dump apiDump, version string, latestVersionDump *apiDump) android.WritablePath {
153 creatingNewVersion := version != currentVersion
154 moduleDir := android.PathForModuleSrc(ctx).String()
155 targetDir := filepath.Join(moduleDir, m.apiDir(), version)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900156 rb := android.NewRuleBuilder(pctx, ctx)
Jiyong Park72e08932021-05-21 00:18:20 +0900157
158 if creatingNewVersion {
159 // We are asked to create a new version. But before doing that, check if the given
160 // dump is the same as the latest version. If so, don't create a new version,
Devin Moore15b8ebe2021-05-21 09:36:37 -0700161 // otherwise we will be unnecessarily creating many versions.
Jiyong Park72e08932021-05-21 00:18:20 +0900162 // Copy the given dump to the target directory only when the equality check failed
Devin Moore15b8ebe2021-05-21 09:36:37 -0700163 // (i.e. `has_development` file contains "1").
Jiyong Park72e08932021-05-21 00:18:20 +0900164 rb.Command().
Devin Moore15b8ebe2021-05-21 09:36:37 -0700165 Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then").
Jooyung Handf94f0f2021-06-07 18:57:10 +0900166 Text("mkdir -p " + targetDir + " && ").
Jiyong Park72e08932021-05-21 00:18:20 +0900167 Text("cp -rf " + dump.dir.String() + "/. " + targetDir).Implicits(dump.files).
168 Text("; fi")
169
170 // Also modify Android.bp file to add the new version to the 'versions' property.
171 rb.Command().
Devin Moore15b8ebe2021-05-21 09:36:37 -0700172 Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then").
Jiyong Park72e08932021-05-21 00:18:20 +0900173 BuiltTool("bpmodify").
Jeongik Chada36d5a2021-01-20 00:43:34 +0900174 Text("-w -m " + m.properties.BaseName).
175 Text("-parameter versions -a " + version).
Jiyong Park72e08932021-05-21 00:18:20 +0900176 Text(android.PathForModuleSrc(ctx, "Android.bp").String()).
177 Text("; fi")
178
Jeongik Chada36d5a2021-01-20 00:43:34 +0900179 } else {
Jiyong Park72e08932021-05-21 00:18:20 +0900180 // We are updating the current version. Don't copy .hash to the current dump
181 rb.Command().Text("mkdir -p " + targetDir)
182 rb.Command().Text("rm -rf " + targetDir + "/*")
Jeongik Chada36d5a2021-01-20 00:43:34 +0900183 rb.Command().Text("cp -rf " + dump.dir.String() + "/* " + targetDir).Implicits(dump.files)
184 }
Jiyong Park72e08932021-05-21 00:18:20 +0900185
186 timestampFile := android.PathForModuleOut(ctx, "updateapi_"+version+".timestamp")
Jeongik Chada36d5a2021-01-20 00:43:34 +0900187 rb.Command().Text("touch").Output(timestampFile)
188
189 rb.Build("dump_aidl_api"+m.properties.BaseName+"_"+version,
190 "Making AIDL API of "+m.properties.BaseName+" as version "+version)
191 return timestampFile
192}
193
Jooyung Han42c5f292021-06-04 19:49:34 +0900194type deps struct {
195 preprocessed android.Paths
196 implicits android.Paths
197 imports []string
198}
199
Jooyung Han60699b52021-05-26 22:20:42 +0900200// calculates import flags(-I) from deps.
201// When the target is ToT, use ToT of imported interfaces. If not, we use "current" snapshot of
202// imported interfaces.
Jooyung Handf94f0f2021-06-07 18:57:10 +0900203func getDeps(ctx android.ModuleContext, versionedImports map[string]string) deps {
Jooyung Han42c5f292021-06-04 19:49:34 +0900204 var deps deps
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900205 ctx.VisitDirectDeps(func(dep android.Module) {
Jooyung Han60699b52021-05-26 22:20:42 +0900206 switch ctx.OtherModuleDependencyTag(dep) {
207 case importInterfaceDep:
208 iface := dep.(*aidlInterface)
Jooyung Handf94f0f2021-06-07 18:57:10 +0900209 if version, ok := versionedImports[iface.BaseModuleName()]; ok {
210 if iface.preprocessed[version] == nil {
211 ctx.ModuleErrorf("can't import %v's preprocessed(version=%v)", iface.BaseModuleName(), version)
212 }
213 deps.preprocessed = append(deps.preprocessed, iface.preprocessed[version])
214 }
Jooyung Han60699b52021-05-26 22:20:42 +0900215 case interfaceDep:
216 iface := dep.(*aidlInterface)
Jooyung Han42c5f292021-06-04 19:49:34 +0900217 deps.imports = append(deps.imports, iface.properties.Include_dirs...)
Jooyung Han60699b52021-05-26 22:20:42 +0900218 case importApiDep, apiDep:
219 api := dep.(*aidlApi)
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900220 // add imported module's checkapiTimestamps as implicits to make sure that imported apiDump is up-to-date
Jooyung Han42c5f292021-06-04 19:49:34 +0900221 deps.implicits = append(deps.implicits, api.checkApiTimestamps.Paths()...)
222 deps.implicits = append(deps.implicits, api.checkHashTimestamps.Paths()...)
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900223 }
224 })
Jooyung Han42c5f292021-06-04 19:49:34 +0900225 return deps
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900226}
227
Jooyung Hanb8a97772021-01-19 01:27:38 +0900228func (m *aidlApi) checkApi(ctx android.ModuleContext, oldDump, newDump apiDump, checkApiLevel string, messageFile android.Path) android.WritablePath {
Jeongik Chada36d5a2021-01-20 00:43:34 +0900229 newVersion := newDump.dir.Base()
230 timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
Jeongik Chada36d5a2021-01-20 00:43:34 +0900231
Jooyung Handf94f0f2021-06-07 18:57:10 +0900232 // --checkapi(old,new) should use imports for "new"
233 deps := getDeps(ctx, m.getImports(ctx, newDump.version))
Jooyung Han42c5f292021-06-04 19:49:34 +0900234 var implicits android.Paths
235 implicits = append(implicits, deps.implicits...)
236 implicits = append(implicits, deps.preprocessed...)
237 implicits = append(implicits, oldDump.files...)
238 implicits = append(implicits, newDump.files...)
239 implicits = append(implicits, messageFile)
240
Jeongik Chada36d5a2021-01-20 00:43:34 +0900241 var optionalFlags []string
242 if m.properties.Stability != nil {
243 optionalFlags = append(optionalFlags, "--stability", *m.properties.Stability)
244 }
Jooyung Han42c5f292021-06-04 19:49:34 +0900245 optionalFlags = append(optionalFlags, wrap("-p", deps.preprocessed.Strings(), "")...)
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900246
Jeongik Chada36d5a2021-01-20 00:43:34 +0900247 ctx.Build(pctx, android.BuildParams{
248 Rule: aidlCheckApiRule,
249 Implicits: implicits,
250 Output: timestampFile,
251 Args: map[string]string{
252 "optionalFlags": strings.Join(optionalFlags, " "),
Jooyung Han42c5f292021-06-04 19:49:34 +0900253 "imports": strings.Join(wrap("-I", deps.imports, ""), " "),
Jeongik Chada36d5a2021-01-20 00:43:34 +0900254 "old": oldDump.dir.String(),
255 "new": newDump.dir.String(),
256 "messageFile": messageFile.String(),
Jooyung Hanb8a97772021-01-19 01:27:38 +0900257 "checkApiLevel": checkApiLevel,
Jeongik Chada36d5a2021-01-20 00:43:34 +0900258 },
259 })
260 return timestampFile
261}
262
Jooyung Hanb8a97772021-01-19 01:27:38 +0900263func (m *aidlApi) checkCompatibility(ctx android.ModuleContext, oldDump, newDump apiDump) android.WritablePath {
264 messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_compatibility.txt")
265 return m.checkApi(ctx, oldDump, newDump, "compatible", messageFile)
266}
Jeongik Chada36d5a2021-01-20 00:43:34 +0900267
Jooyung Hanb8a97772021-01-19 01:27:38 +0900268func (m *aidlApi) checkEquality(ctx android.ModuleContext, oldDump apiDump, newDump apiDump) android.WritablePath {
Jeongik Chada36d5a2021-01-20 00:43:34 +0900269 // Use different messages depending on whether platform SDK is finalized or not.
270 // In case when it is finalized, we should never allow updating the already frozen API.
271 // If it's not finalized, we let users to update the current version by invoking
272 // `m <name>-update-api`.
273 messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_equality.txt")
274 sdkIsFinal := !ctx.Config().DefaultAppTargetSdk(ctx).IsPreview()
275 if sdkIsFinal {
276 messageFile = android.PathForSource(ctx, "system/tools/aidl/build/message_check_equality_release.txt")
277 }
278 formattedMessageFile := android.PathForModuleOut(ctx, "message_check_equality.txt")
279 rb := android.NewRuleBuilder(pctx, ctx)
280 rb.Command().Text("sed").Flag(" s/%s/" + m.properties.BaseName + "/g ").Input(messageFile).Text(" > ").Output(formattedMessageFile)
281 rb.Build("format_message_"+m.properties.BaseName, "")
282
Jooyung Hanb8a97772021-01-19 01:27:38 +0900283 return m.checkApi(ctx, oldDump, newDump, "equal", formattedMessageFile)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900284}
285
286func (m *aidlApi) checkIntegrity(ctx android.ModuleContext, dump apiDump) android.WritablePath {
287 version := dump.dir.Base()
288 timestampFile := android.PathForModuleOut(ctx, "checkhash_"+version+".timestamp")
289 messageFile := android.PathForSource(ctx, "system/tools/aidl/build/message_check_integrity.txt")
290
Jeongik Chada36d5a2021-01-20 00:43:34 +0900291 var implicits android.Paths
292 implicits = append(implicits, dump.files...)
293 implicits = append(implicits, dump.hashFile.Path())
294 implicits = append(implicits, messageFile)
295 ctx.Build(pctx, android.BuildParams{
296 Rule: aidlVerifyHashRule,
297 Implicits: implicits,
298 Output: timestampFile,
299 Args: map[string]string{
300 "apiDir": dump.dir.String(),
Jeongik Cha6cc16f32021-05-18 11:23:55 +0900301 "version": versionForHashGen(version),
Jeongik Chada36d5a2021-01-20 00:43:34 +0900302 "hashFile": dump.hashFile.Path().String(),
303 "messageFile": messageFile.String(),
304 },
305 })
306 return timestampFile
307}
308
Devin Moore15b8ebe2021-05-21 09:36:37 -0700309func (m *aidlApi) checkForDevelopment(ctx android.ModuleContext, latestVersionDump *apiDump, totDump apiDump) android.WritablePath {
310 hasDevPath := android.PathForModuleOut(ctx, "has_development")
311 rb := android.NewRuleBuilder(pctx, ctx)
312 rb.Command().Text("rm -f " + hasDevPath.String())
313 if latestVersionDump != nil {
314 hasDevCommand := rb.Command()
315 hasDevCommand.BuiltTool("aidl").FlagWithArg("--checkapi=", "equal")
316 if m.properties.Stability != nil {
317 hasDevCommand.FlagWithArg("--stability ", *m.properties.Stability)
318 }
Jooyung Handf94f0f2021-06-07 18:57:10 +0900319 // checkapi(latest, tot) should use imports for nextVersion(=tot)
320 deps := getDeps(ctx, m.getImports(ctx, m.nextVersion()))
Devin Moore15b8ebe2021-05-21 09:36:37 -0700321 hasDevCommand.
Jooyung Han42c5f292021-06-04 19:49:34 +0900322 FlagForEachArg("-I", deps.imports).Implicits(deps.implicits).
323 FlagForEachInput("-p", deps.preprocessed).
Devin Moore15b8ebe2021-05-21 09:36:37 -0700324 Text(latestVersionDump.dir.String()).Implicits(latestVersionDump.files).
325 Text(totDump.dir.String()).Implicits(totDump.files).
Devin Moore45e93ca2021-06-04 13:57:38 -0700326 Text("2> /dev/null; echo $? >").Output(hasDevPath)
Devin Moore15b8ebe2021-05-21 09:36:37 -0700327 } else {
328 rb.Command().Text("echo 1 >").Output(hasDevPath)
329 }
330 rb.Build("check_for_development", "")
331 return hasDevPath
332}
333
Jeongik Chada36d5a2021-01-20 00:43:34 +0900334func (m *aidlApi) GenerateAndroidBuildActions(ctx android.ModuleContext) {
335 // An API dump is created from source and it is compared against the API dump of the
336 // 'current' (yet-to-be-finalized) version. By checking this we enforce that any change in
337 // the AIDL interface is gated by the AIDL API review even before the interface is frozen as
338 // a new version.
339 totApiDump := m.createApiDumpFromSource(ctx)
340 currentApiDir := android.ExistentPathForSource(ctx, ctx.ModuleDir(), m.apiDir(), currentVersion)
341 var currentApiDump apiDump
342 if currentApiDir.Valid() {
343 currentApiDump = apiDump{
Jooyung Handf94f0f2021-06-07 18:57:10 +0900344 version: nextVersion(m.properties.Versions),
Jeongik Chada36d5a2021-01-20 00:43:34 +0900345 dir: currentApiDir.Path(),
346 files: ctx.Glob(filepath.Join(currentApiDir.Path().String(), "**/*.aidl"), nil),
347 hashFile: android.ExistentPathForSource(ctx, ctx.ModuleDir(), m.apiDir(), currentVersion, ".hash"),
348 }
349 checked := m.checkEquality(ctx, currentApiDump, totApiDump)
350 m.checkApiTimestamps = append(m.checkApiTimestamps, checked)
351 } else {
352 // The "current" directory might not exist, in case when the interface is first created.
353 // Instruct user to create one by executing `m <name>-update-api`.
354 rb := android.NewRuleBuilder(pctx, ctx)
355 ifaceName := m.properties.BaseName
356 rb.Command().Text(fmt.Sprintf(`echo "API dump for the current version of AIDL interface %s does not exist."`, ifaceName))
357 rb.Command().Text(fmt.Sprintf(`echo Run "m %s-update-api", or add "unstable: true" to the build rule `+
358 `for the interface if it does not need to be versioned`, ifaceName))
359 // This file will never be created. Otherwise, the build will pass simply by running 'm; m'.
360 alwaysChecked := android.PathForModuleOut(ctx, "checkapi_current.timestamp")
361 rb.Command().Text("false").ImplicitOutput(alwaysChecked)
362 rb.Build("check_current_aidl_api", "")
363 m.checkApiTimestamps = append(m.checkApiTimestamps, alwaysChecked)
364 }
365
366 // Also check that version X is backwards compatible with version X-1.
367 // "current" is checked against the latest version.
368 var dumps []apiDump
369 for _, ver := range m.properties.Versions {
370 apiDir := filepath.Join(ctx.ModuleDir(), m.apiDir(), ver)
371 apiDirPath := android.ExistentPathForSource(ctx, apiDir)
372 if apiDirPath.Valid() {
Jeongik Cha6cc16f32021-05-18 11:23:55 +0900373 hashFilePath := filepath.Join(apiDir, ".hash")
374 dump := apiDump{
Jooyung Handf94f0f2021-06-07 18:57:10 +0900375 version: ver,
Jeongik Chada36d5a2021-01-20 00:43:34 +0900376 dir: apiDirPath.Path(),
377 files: ctx.Glob(filepath.Join(apiDirPath.String(), "**/*.aidl"), nil),
Jeongik Cha6cc16f32021-05-18 11:23:55 +0900378 hashFile: android.ExistentPathForSource(ctx, hashFilePath),
379 }
380 if !dump.hashFile.Valid() {
Jeongik Chac6442172021-05-25 10:46:56 +0900381 // We should show the source path of hash_gen because aidl_hash_gen cannot be built due to build error.
382 cmd := fmt.Sprintf(`(croot && system/tools/aidl/build/hash_gen.sh %s %s %s)`, apiDir, versionForHashGen(ver), hashFilePath)
Jeongik Cha6cc16f32021-05-18 11:23:55 +0900383 ctx.ModuleErrorf("A frozen aidl_interface must have '.hash' file, but %s-V%s doesn't have it. Use the command below to generate hash.\n%s\n",
384 m.properties.BaseName, ver, cmd)
385 }
386 dumps = append(dumps, dump)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900387 } else if ctx.Config().AllowMissingDependencies() {
388 ctx.AddMissingDependencies([]string{apiDir})
389 } else {
390 ctx.ModuleErrorf("API version %s path %s does not exist", ver, apiDir)
391 }
392 }
Jiyong Park72e08932021-05-21 00:18:20 +0900393 var latestVersionDump *apiDump
394 if len(dumps) >= 1 {
395 latestVersionDump = &dumps[len(dumps)-1]
396 }
Jeongik Chada36d5a2021-01-20 00:43:34 +0900397 if currentApiDir.Valid() {
398 dumps = append(dumps, currentApiDump)
399 }
400 for i, _ := range dumps {
401 if dumps[i].hashFile.Valid() {
402 checkHashTimestamp := m.checkIntegrity(ctx, dumps[i])
403 m.checkHashTimestamps = append(m.checkHashTimestamps, checkHashTimestamp)
404 }
405
406 if i == 0 {
407 continue
408 }
409 checked := m.checkCompatibility(ctx, dumps[i-1], dumps[i])
410 m.checkApiTimestamps = append(m.checkApiTimestamps, checked)
411 }
412
Devin Moore15b8ebe2021-05-21 09:36:37 -0700413 // Check for active development on the unfrozen version
414 m.hasDevelopment = m.checkForDevelopment(ctx, latestVersionDump, totApiDump)
415
Jeongik Chada36d5a2021-01-20 00:43:34 +0900416 // API dump from source is updated to the 'current' version. Triggered by `m <name>-update-api`
Jiyong Park72e08932021-05-21 00:18:20 +0900417 m.updateApiTimestamp = m.makeApiDumpAsVersion(ctx, totApiDump, currentVersion, nil)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900418
419 // API dump from source is frozen as the next stable version. Triggered by `m <name>-freeze-api`
420 nextVersion := m.nextVersion()
Jiyong Park72e08932021-05-21 00:18:20 +0900421 m.freezeApiTimestamp = m.makeApiDumpAsVersion(ctx, totApiDump, nextVersion, latestVersionDump)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900422}
423
424func (m *aidlApi) AndroidMk() android.AndroidMkData {
425 return android.AndroidMkData{
426 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
427 android.WriteAndroidMkData(w, data)
428 targetName := m.properties.BaseName + "-freeze-api"
429 fmt.Fprintln(w, ".PHONY:", targetName)
430 fmt.Fprintln(w, targetName+":", m.freezeApiTimestamp.String())
431
432 targetName = m.properties.BaseName + "-update-api"
433 fmt.Fprintln(w, ".PHONY:", targetName)
434 fmt.Fprintln(w, targetName+":", m.updateApiTimestamp.String())
435 },
436 }
437}
438
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900439type depTag struct {
440 blueprint.BaseDependencyTag
441 name string
442}
443
444var (
Jooyung Han60699b52021-05-26 22:20:42 +0900445 apiDep = depTag{name: "api"}
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900446 interfaceDep = depTag{name: "interface"}
Jooyung Han60699b52021-05-26 22:20:42 +0900447
448 importApiDep = depTag{name: "imported-api"}
449 importInterfaceDep = depTag{name: "imported-interface"}
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900450)
451
Jeongik Chada36d5a2021-01-20 00:43:34 +0900452func (m *aidlApi) DepsMutator(ctx android.BottomUpMutatorContext) {
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900453 ctx.AddDependency(ctx.Module(), interfaceDep, m.properties.BaseName+aidlInterfaceSuffix)
Jooyung Han42c5f292021-06-04 19:49:34 +0900454 ctx.AddDependency(ctx.Module(), importInterfaceDep, wrap("", m.properties.ImportsWithoutVersion, aidlInterfaceSuffix)...)
455 ctx.AddDependency(ctx.Module(), importApiDep, wrap("", m.properties.ImportsWithoutVersion, aidlApiSuffix)...)
Devin Moore15b8ebe2021-05-21 09:36:37 -0700456 ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName)
Jeongik Chada36d5a2021-01-20 00:43:34 +0900457}
458
459func aidlApiFactory() android.Module {
460 m := &aidlApi{}
461 m.AddProperties(&m.properties)
462 android.InitAndroidModule(m)
463 return m
464}
465
466func addApiModule(mctx android.LoadHookContext, i *aidlInterface) string {
467 apiModule := i.ModuleBase.Name() + aidlApiSuffix
Jeongik Cha52e98022021-01-20 18:37:20 +0900468 srcs, aidlRoot := i.srcsForVersion(mctx, i.nextVersion())
Jeongik Chada36d5a2021-01-20 00:43:34 +0900469 mctx.CreateModule(aidlApiFactory, &nameProperties{
470 Name: proptools.StringPtr(apiModule),
471 }, &aidlApiProperties{
Jeongik Chaa01447d2021-03-17 17:43:12 +0900472 BaseName: i.ModuleBase.Name(),
473 Srcs: srcs,
474 AidlRoot: aidlRoot,
475 Stability: i.properties.Stability,
Jooyung Han8e3b72c2021-05-22 02:54:37 +0900476 ImportsWithoutVersion: i.properties.ImportsWithoutVersion,
Jeongik Chaa01447d2021-03-17 17:43:12 +0900477 Versions: i.properties.Versions,
478 Dumpapi: i.properties.Dumpapi,
Jeongik Chada36d5a2021-01-20 00:43:34 +0900479 })
480 return apiModule
481}
Jeongik Cha6cc16f32021-05-18 11:23:55 +0900482
483func versionForHashGen(ver string) string {
484 // aidlHashGen uses the version before current version. If it has never been frozen, return 'latest-version'.
485 verInt, _ := strconv.Atoi(ver)
486 if verInt > 1 {
487 return strconv.Itoa(verInt - 1)
488 }
489 return "latest-version"
490}
Jiyong Parke38dab42021-05-20 14:25:34 +0900491
492func init() {
493 android.RegisterSingletonType("aidl-freeze-api", freezeApiSingletonFactory)
494}
495
496func freezeApiSingletonFactory() android.Singleton {
497 return &freezeApiSingleton{}
498}
499
500type freezeApiSingleton struct{}
501
502func (f *freezeApiSingleton) GenerateBuildActions(ctx android.SingletonContext) {
503 var files android.Paths
504 ctx.VisitAllModules(func(module android.Module) {
505 if !module.Enabled() {
506 return
507 }
508 if m, ok := module.(*aidlApi); ok {
Devin Moore91d92492021-06-24 08:50:55 -0700509 ownersToFreeze := strings.Fields(ctx.Config().Getenv("AIDL_FREEZE_OWNERS"))
510 var shouldBeFrozen bool
511 if len(ownersToFreeze) > 0 {
512 shouldBeFrozen = android.InList(m.Owner(), ownersToFreeze)
513 } else {
514 shouldBeFrozen = m.Owner() == ""
515 }
516 if shouldBeFrozen {
517 files = append(files, m.freezeApiTimestamp)
518 }
Jiyong Parke38dab42021-05-20 14:25:34 +0900519 }
520 })
521 ctx.Phony("aidl-freeze-api", files...)
522}