Replace android.WriteFile rule with android.WriteFileRule

The android.WriteFile rule takes careful escaping to produce the
right contents.  Wrap it in an android.WriteFileRule that handles
the escaping.

Test: compare all android.WriteFile outputs
Change-Id: If71a5843af47a37ca61714e1a1ebb32d08536c31
This commit is contained in:
Colin Cross 2020-11-13 11:48:42 -08:00
parent 1d26d3033a
commit cf371cc1f7
13 changed files with 90 additions and 114 deletions

View file

@ -598,36 +598,22 @@ func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion
var fullContent strings.Builder
var flatContent strings.Builder
fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\\n", ctx.ModuleName(), minSdkVersion)
fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\n", ctx.ModuleName(), minSdkVersion)
for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
info := depInfos[key]
toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion)
if info.IsExternal {
toName = toName + " (external)"
}
fmt.Fprintf(&fullContent, " %s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
fmt.Fprintf(&flatContent, "%s\\n", toName)
fmt.Fprintf(&fullContent, " %s <- %s\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
fmt.Fprintf(&flatContent, "%s\n", toName)
}
d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath
ctx.Build(pctx, BuildParams{
Rule: WriteFile,
Description: "Full Dependency Info",
Output: d.fullListPath,
Args: map[string]string{
"content": fullContent.String(),
},
})
WriteFileRule(ctx, d.fullListPath, fullContent.String())
d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath
ctx.Build(pctx, BuildParams{
Rule: WriteFile,
Description: "Flat Dependency Info",
Output: d.flatListPath,
Args: map[string]string{
"content": flatContent.String(),
},
})
WriteFileRule(ctx, d.flatListPath, flatContent.String())
}
// TODO(b/158059172): remove minSdkVersion allowlist

View file

@ -225,14 +225,7 @@ func createApiLevelsJson(ctx SingletonContext, file WritablePath,
ctx.Errorf(err.Error())
}
ctx.Build(pctx, BuildParams{
Rule: WriteFile,
Description: "generate " + file.Base(),
Output: file,
Args: map[string]string{
"content": string(jsonStr[:]),
},
})
WriteFileRule(ctx, file, string(jsonStr))
}
func GetApiLevelsJson(ctx PathContext) WritablePath {

View file

@ -15,8 +15,12 @@
package android
import (
"strings"
"testing"
"github.com/google/blueprint"
_ "github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
)
var (
@ -91,9 +95,9 @@ var (
// ubuntu 14.04 offcially use dash for /bin/sh, and its builtin echo command
// doesn't support -e option. Therefore we force to use /bin/bash when writing out
// content to file.
WriteFile = pctx.AndroidStaticRule("WriteFile",
writeFile = pctx.AndroidStaticRule("writeFile",
blueprint.RuleParams{
Command: "/bin/bash -c 'echo -e $$0 > $out' '$content'",
Command: `/bin/bash -c 'echo -e "$$0" > $out' $content`,
Description: "writing file $out",
},
"content")
@ -111,3 +115,64 @@ var (
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
}
var (
// echoEscaper escapes a string such that passing it to "echo -e" will produce the input value.
echoEscaper = strings.NewReplacer(
`\`, `\\`, // First escape existing backslashes so they aren't interpreted by `echo -e`.
"\n", `\n`, // Then replace newlines with \n
)
// echoEscaper reverses echoEscaper.
echoUnescaper = strings.NewReplacer(
`\n`, "\n",
`\\`, `\`,
)
// shellUnescaper reverses the replacer in proptools.ShellEscape
shellUnescaper = strings.NewReplacer(`'\''`, `'`)
)
// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped
// so that the file contains exactly the contents passed to the function, plus a trailing newline.
func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
content = echoEscaper.Replace(content)
content = proptools.ShellEscape(content)
if content == "" {
content = "''"
}
ctx.Build(pctx, BuildParams{
Rule: writeFile,
Output: outputFile,
Description: "write " + outputFile.Base(),
Args: map[string]string{
"content": content,
},
})
}
// shellUnescape reverses proptools.ShellEscape
func shellUnescape(s string) string {
// Remove leading and trailing quotes if present
if len(s) >= 2 && s[0] == '\'' {
s = s[1 : len(s)-1]
}
s = shellUnescaper.Replace(s)
return s
}
// ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
// in tests.
func ContentFromFileRuleForTests(t *testing.T, params TestingBuildParams) string {
t.Helper()
if g, w := params.Rule, writeFile; g != w {
t.Errorf("expected params.Rule to be %q, was %q", w, g)
return ""
}
content := params.Args["content"]
content = shellUnescape(content)
content = echoUnescaper.Replace(content)
return content
}

View file

@ -5574,7 +5574,7 @@ func TestAppBundle(t *testing.T) {
}
`, withManifestPackageNameOverrides([]string{"AppFoo:com.android.foo"}))
bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Description("Bundle Config")
bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("bundle_config.json")
content := bundleConfigRule.Args["content"]
ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
@ -5600,7 +5600,7 @@ func TestAppSetBundle(t *testing.T) {
set: "AppSet.apks",
}`)
mod := ctx.ModuleForTests("myapex", "android_common_myapex_image")
bundleConfigRule := mod.Description("Bundle Config")
bundleConfigRule := mod.Output("bundle_config.json")
content := bundleConfigRule.Args["content"]
ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
s := mod.Rule("apexRule").Args["copy_commands"]

View file

@ -373,14 +373,7 @@ func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.Output
panic(fmt.Errorf("error while marshalling to %q: %#v", output, err))
}
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: output,
Description: "Bundle Config " + output.String(),
Args: map[string]string{
"content": string(j),
},
})
android.WriteFileRule(ctx, output, string(j))
return output.OutputPath
}

View file

@ -116,7 +116,7 @@ func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) {
partition string
}
toString := func(e apexKeyEntry) string {
format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\\n"
format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\n"
if e.presigned {
return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition)
} else {
@ -173,17 +173,9 @@ func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) {
var filecontent strings.Builder
for _, name := range moduleNames {
fmt.Fprintf(&filecontent, "%s", toString(apexKeyMap[name]))
filecontent.WriteString(toString(apexKeyMap[name]))
}
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Description: "apexkeys.txt",
Output: s.output,
Args: map[string]string{
"content": filecontent.String(),
},
})
android.WriteFileRule(ctx, s.output, filecontent.String())
}
func apexKeysTextFactory() android.Singleton {

View file

@ -296,8 +296,8 @@ func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton andr
func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
t.Helper()
assertString(t, params.Rule.String(), android.WriteFile.String())
actual := strings.FieldsFunc(strings.ReplaceAll(params.Args["content"], "\\n", "\n"), func(r rune) bool { return r == '\n' })
content := android.ContentFromFileRuleForTests(t, params)
actual := strings.FieldsFunc(content, func(r rune) bool { return r == '\n' })
assertArrayString(t, actual, expected)
}

View file

@ -278,14 +278,7 @@ func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
if fuzz.Properties.Fuzz_config != nil {
configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Description: "fuzzer infrastructure configuration",
Output: configPath,
Args: map[string]string{
"content": fuzz.Properties.Fuzz_config.String(),
},
})
android.WriteFileRule(ctx, configPath, fuzz.Properties.Fuzz_config.String())
fuzz.config = configPath
}

View file

@ -93,13 +93,6 @@ func combineNotices(ctx android.SingletonContext, paths android.Paths, out strin
func writeStringToFile(ctx android.SingletonContext, content, out string) android.OutputPath {
outPath := android.PathForOutput(ctx, out)
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: outPath,
Description: "WriteFile " + out,
Args: map[string]string{
"content": content,
},
})
android.WriteFileRule(ctx, outPath, content)
return outPath
}

View file

@ -487,14 +487,7 @@ func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleConte
}
txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: txt.outputFile,
Description: "Writing " + txt.outputFile.String(),
Args: map[string]string{
"content": strings.Join(list, "\\n"),
},
})
android.WriteFileRule(ctx, txt.outputFile, strings.Join(list, "\n"))
installPath := android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(installPath, filename, txt.outputFile)
@ -825,14 +818,7 @@ func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.Singleton
merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: c.vndkLibrariesFile,
Description: "Writing " + c.vndkLibrariesFile.String(),
Args: map[string]string{
"content": strings.Join(merged, "\\n"),
},
})
android.WriteFileRule(ctx, c.vndkLibrariesFile, strings.Join(merged, "\n"))
}
func (c *vndkSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {

View file

@ -488,13 +488,7 @@ func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonC
return
}
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: android.PathForOutput(ctx, "dexpreopt_soong.config"),
Args: map[string]string{
"content": string(data),
},
})
android.WriteFileRule(ctx, android.PathForOutput(ctx, "dexpreopt_soong.config"), string(data))
}
func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {

View file

@ -572,14 +572,7 @@ func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePat
}
func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Description: "manifest",
Output: outputFile,
Args: map[string]string{
"content": "Main-Class: " + mainClass + "\n",
},
})
android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
}
func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) {

View file

@ -651,14 +651,8 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf
updatableBcpPackagesName := "updatable-bcp-packages.txt"
updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName)
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: updatableBcpPackages,
Args: map[string]string{
// WriteFile automatically adds the last end-of-line.
"content": strings.Join(updatablePackages, "\\n"),
},
})
// WriteFileRule automatically adds the last end-of-line.
android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n"))
rule := android.NewRuleBuilder()
rule.MissingDeps(missingDeps)
@ -720,13 +714,7 @@ func dumpOatRules(ctx android.SingletonContext, image *bootImageConfig) {
func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
data := dexpreopt.GetGlobalConfigRawData(ctx)
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
Output: path,
Args: map[string]string{
"content": string(data),
},
})
android.WriteFileRule(ctx, path, string(data))
}
// Export paths for default boot image to Make