Use versioned preprocessed.aidl

Generates versioned preprocessed.aidl files and use them for "versioned
imports".

For example, when a module 'foo' has two versions: [1,2], three
preprocessed.aidl files are generated: [1,2,3(tot)].

This ensures that a module with versioned imports can't refer newer
symbols(types or consts).

Bug: 189288369
Bug: 189728905
Test: m (w/ soong tests)
Change-Id: Ie9e82b47ee0ffc979dd316638c19a5b44726fadf
diff --git a/build/aidl_api.go b/build/aidl_api.go
index 6ae212f..832b5c6 100644
--- a/build/aidl_api.go
+++ b/build/aidl_api.go
@@ -90,11 +90,17 @@
 }
 
 type apiDump struct {
+	version  string
 	dir      android.Path
 	files    android.Paths
 	hashFile android.OptionalPath
 }
 
+func (m *aidlApi) getImports(ctx android.ModuleContext, version string) map[string]string {
+	iface := ctx.GetDirectDepWithTag(m.properties.BaseName, interfaceDep).(*aidlInterface)
+	return iface.getImports(version)
+}
+
 func (m *aidlApi) createApiDumpFromSource(ctx android.ModuleContext) apiDump {
 	srcs, imports := getPaths(ctx, m.properties.Srcs, m.properties.AidlRoot)
 
@@ -102,7 +108,8 @@
 		return apiDump{}
 	}
 
-	deps := getDeps(ctx)
+	// dumpapi uses imports for ToT("") version
+	deps := getDeps(ctx, m.getImports(ctx, m.nextVersion()))
 	imports = append(imports, deps.imports...)
 
 	var apiDir android.WritablePath
@@ -125,6 +132,7 @@
 	}
 	optionalFlags = append(optionalFlags, wrap("-p", deps.preprocessed.Strings(), "")...)
 
+	version := nextVersion(m.properties.Versions)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      aidlDumpApiRule,
 		Outputs:   append(apiFiles, hashFile),
@@ -135,10 +143,10 @@
 			"imports":       strings.Join(wrap("-I", imports, ""), " "),
 			"outDir":        apiDir.String(),
 			"hashFile":      hashFile.String(),
-			"latestVersion": versionForHashGen(nextVersion(m.properties.Versions)),
+			"latestVersion": versionForHashGen(version),
 		},
 	})
-	return apiDump{apiDir, apiFiles.Paths(), android.OptionalPathForPath(hashFile)}
+	return apiDump{version, apiDir, apiFiles.Paths(), android.OptionalPathForPath(hashFile)}
 }
 
 func (m *aidlApi) makeApiDumpAsVersion(ctx android.ModuleContext, dump apiDump, version string, latestVersionDump *apiDump) android.WritablePath {
@@ -155,6 +163,7 @@
 		// (i.e. `has_development` file contains "1").
 		rb.Command().
 			Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then").
+			Text("mkdir -p " + targetDir + " && ").
 			Text("cp -rf " + dump.dir.String() + "/. " + targetDir).Implicits(dump.files).
 			Text("; fi")
 
@@ -191,13 +200,18 @@
 // calculates import flags(-I) from deps.
 // When the target is ToT, use ToT of imported interfaces. If not, we use "current" snapshot of
 // imported interfaces.
-func getDeps(ctx android.ModuleContext) deps {
+func getDeps(ctx android.ModuleContext, versionedImports map[string]string) deps {
 	var deps deps
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		switch ctx.OtherModuleDependencyTag(dep) {
 		case importInterfaceDep:
 			iface := dep.(*aidlInterface)
-			deps.preprocessed = append(deps.preprocessed, iface.preprocessed)
+			if version, ok := versionedImports[iface.BaseModuleName()]; ok {
+				if iface.preprocessed[version] == nil {
+					ctx.ModuleErrorf("can't import %v's preprocessed(version=%v)", iface.BaseModuleName(), version)
+				}
+				deps.preprocessed = append(deps.preprocessed, iface.preprocessed[version])
+			}
 		case interfaceDep:
 			iface := dep.(*aidlInterface)
 			deps.imports = append(deps.imports, iface.properties.Include_dirs...)
@@ -215,7 +229,8 @@
 	newVersion := newDump.dir.Base()
 	timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
 
-	deps := getDeps(ctx)
+	// --checkapi(old,new) should use imports for "new"
+	deps := getDeps(ctx, m.getImports(ctx, newDump.version))
 	var implicits android.Paths
 	implicits = append(implicits, deps.implicits...)
 	implicits = append(implicits, deps.preprocessed...)
@@ -301,7 +316,8 @@
 		if m.properties.Stability != nil {
 			hasDevCommand.FlagWithArg("--stability ", *m.properties.Stability)
 		}
-		deps := getDeps(ctx)
+		// checkapi(latest, tot) should use imports for nextVersion(=tot)
+		deps := getDeps(ctx, m.getImports(ctx, m.nextVersion()))
 		hasDevCommand.
 			FlagForEachArg("-I", deps.imports).Implicits(deps.implicits).
 			FlagForEachInput("-p", deps.preprocessed).
@@ -325,6 +341,7 @@
 	var currentApiDump apiDump
 	if currentApiDir.Valid() {
 		currentApiDump = apiDump{
+			version:  nextVersion(m.properties.Versions),
 			dir:      currentApiDir.Path(),
 			files:    ctx.Glob(filepath.Join(currentApiDir.Path().String(), "**/*.aidl"), nil),
 			hashFile: android.ExistentPathForSource(ctx, ctx.ModuleDir(), m.apiDir(), currentVersion, ".hash"),
@@ -355,6 +372,7 @@
 		if apiDirPath.Valid() {
 			hashFilePath := filepath.Join(apiDir, ".hash")
 			dump := apiDump{
+				version:  ver,
 				dir:      apiDirPath.Path(),
 				files:    ctx.Glob(filepath.Join(apiDirPath.String(), "**/*.aidl"), nil),
 				hashFile: android.ExistentPathForSource(ctx, hashFilePath),