*-freeze-api does nothing when current == latest stable version

This change fixes a bug that freezing an AIDL interface always create a
new version even when the current version is exactly the same as the
latest stable version.

Bug: 188713899
Test: m aidl-freeze-api

Change-Id: I506062ca8ec8ee1ef0744784af2419dfd8b9471e
diff --git a/build/aidl_api.go b/build/aidl_api.go
index 61fa334..8f17fe8 100644
--- a/build/aidl_api.go
+++ b/build/aidl_api.go
@@ -144,28 +144,64 @@
 	return apiDump{apiDir, apiFiles.Paths(), android.OptionalPathForPath(hashFile)}
 }
 
-func (m *aidlApi) makeApiDumpAsVersion(ctx android.ModuleContext, dump apiDump, version string) android.WritablePath {
-	timestampFile := android.PathForModuleOut(ctx, "updateapi_"+version+".timestamp")
-
-	modulePath := android.PathForModuleSrc(ctx).String()
-
-	targetDir := filepath.Join(modulePath, m.apiDir(), version)
+func (m *aidlApi) makeApiDumpAsVersion(ctx android.ModuleContext, dump apiDump, version string, latestVersionDump *apiDump) android.WritablePath {
+	creatingNewVersion := version != currentVersion
+	moduleDir := android.PathForModuleSrc(ctx).String()
+	targetDir := filepath.Join(moduleDir, m.apiDir(), version)
 	rb := android.NewRuleBuilder(pctx, ctx)
-	// Wipe the target directory and then copy the API dump into the directory
-	rb.Command().Text("mkdir -p " + targetDir)
-	rb.Command().Text("rm -rf " + targetDir + "/*")
-	if version != currentVersion {
-		rb.Command().Text("cp -rf " + dump.dir.String() + "/. " + targetDir).Implicits(dump.files)
-		// If this is making a new frozen (i.e. non-current) version of the interface,
-		// modify Android.bp file to add the new version to the 'versions' property.
-		rb.Command().BuiltTool("bpmodify").
+
+	if creatingNewVersion {
+		// We are asked to create a new version. But before doing that, check if the given
+		// dump is the same as the latest version. If so, don't create a new version,
+		// otherwise we will be unnecessarily creating many versions. `newVersionNeededFile`
+		// is created when the equality check fails.
+		newVersionNeededFile := android.PathForModuleOut(ctx, "updateapi_"+version+".needed")
+		rb.Command().Text("rm -f " + newVersionNeededFile.String())
+
+		if latestVersionDump != nil {
+			equalityCheckCommand := rb.Command()
+			equalityCheckCommand.BuiltTool("aidl").
+				FlagWithArg("--checkapi=", "equal")
+			if m.properties.Stability != nil {
+				equalityCheckCommand.FlagWithArg("--stability ", *m.properties.Stability)
+			}
+			equalityCheckCommand.
+				Text(latestVersionDump.dir.String()).Implicits(latestVersionDump.files).
+				Text(dump.dir.String()).Implicits(dump.files).
+				Text("&> /dev/null")
+			equalityCheckCommand.
+				Text("|| touch").
+				Text(newVersionNeededFile.String())
+		} else {
+			// If there is no latest version (i.e. we are creating the initial version)
+			// create the new version unconditionally
+			rb.Command().Text("touch").Text(newVersionNeededFile.String())
+		}
+
+		// Copy the given dump to the target directory only when the equality check failed
+		// (i.e. `newVersionNeededFile` exists).
+		rb.Command().
+			Text("if [ -f " + newVersionNeededFile.String() + " ]; then").
+			Text("cp -rf " + dump.dir.String() + "/. " + targetDir).Implicits(dump.files).
+			Text("; fi")
+
+		// Also modify Android.bp file to add the new version to the 'versions' property.
+		rb.Command().
+			Text("if [ -f " + newVersionNeededFile.String() + " ]; then").
+			BuiltTool("bpmodify").
 			Text("-w -m " + m.properties.BaseName).
 			Text("-parameter versions -a " + version).
-			Text(android.PathForModuleSrc(ctx, "Android.bp").String())
+			Text(android.PathForModuleSrc(ctx, "Android.bp").String()).
+			Text("; fi")
+
 	} else {
-		// In this case (unfrozen interface), don't copy .hash
+		// We are updating the current version. Don't copy .hash to the current dump
+		rb.Command().Text("mkdir -p " + targetDir)
+		rb.Command().Text("rm -rf " + targetDir + "/*")
 		rb.Command().Text("cp -rf " + dump.dir.String() + "/* " + targetDir).Implicits(dump.files)
 	}
+
+	timestampFile := android.PathForModuleOut(ctx, "updateapi_"+version+".timestamp")
 	rb.Command().Text("touch").Output(timestampFile)
 
 	rb.Build("dump_aidl_api"+m.properties.BaseName+"_"+version,
@@ -307,6 +343,10 @@
 			ctx.ModuleErrorf("API version %s path %s does not exist", ver, apiDir)
 		}
 	}
+	var latestVersionDump *apiDump
+	if len(dumps) >= 1 {
+		latestVersionDump = &dumps[len(dumps)-1]
+	}
 	if currentApiDir.Valid() {
 		dumps = append(dumps, currentApiDump)
 	}
@@ -324,11 +364,11 @@
 	}
 
 	// API dump from source is updated to the 'current' version. Triggered by `m <name>-update-api`
-	m.updateApiTimestamp = m.makeApiDumpAsVersion(ctx, totApiDump, currentVersion)
+	m.updateApiTimestamp = m.makeApiDumpAsVersion(ctx, totApiDump, currentVersion, nil)
 
 	// API dump from source is frozen as the next stable version. Triggered by `m <name>-freeze-api`
 	nextVersion := m.nextVersion()
-	m.freezeApiTimestamp = m.makeApiDumpAsVersion(ctx, totApiDump, nextVersion)
+	m.freezeApiTimestamp = m.makeApiDumpAsVersion(ctx, totApiDump, nextVersion, latestVersionDump)
 }
 
 func (m *aidlApi) AndroidMk() android.AndroidMkData {