Merge "Add an isolated option for cc_test targets."
diff --git a/android/androidmk.go b/android/androidmk.go
index 44c266a..5df4a85 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -25,6 +25,7 @@
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/bootstrap"
)
func init() {
@@ -64,13 +65,13 @@
return
}
- var androidMkModulesList []Module
+ var androidMkModulesList []blueprint.Module
- ctx.VisitAllModules(func(module Module) {
+ ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
androidMkModulesList = append(androidMkModulesList, module)
})
- sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
+ sort.Sort(ModulesByName{androidMkModulesList, ctx})
transMk := PathForOutput(ctx, "Android"+String(ctx.Config().productVariables.Make_suffix)+".mk")
if ctx.Failed() {
@@ -88,7 +89,7 @@
})
}
-func translateAndroidMk(ctx SingletonContext, mkFile string, mods []Module) error {
+func translateAndroidMk(ctx SingletonContext, mkFile string, mods []blueprint.Module) error {
buf := &bytes.Buffer{}
fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
@@ -101,8 +102,8 @@
return err
}
- if ctx.PrimaryModule(mod) == mod {
- type_stats[ctx.ModuleType(mod)] += 1
+ if amod, ok := mod.(Module); ok && ctx.PrimaryModule(amod) == amod {
+ type_stats[ctx.ModuleType(amod)] += 1
}
}
@@ -148,10 +149,29 @@
}
}()
- provider, ok := mod.(AndroidMkDataProvider)
- if !ok {
+ switch x := mod.(type) {
+ case AndroidMkDataProvider:
+ return translateAndroidModule(ctx, w, mod, x)
+ case bootstrap.GoBinaryTool:
+ return translateGoBinaryModule(ctx, w, mod, x)
+ default:
return nil
}
+}
+
+func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
+ goBinary bootstrap.GoBinaryTool) error {
+
+ name := ctx.ModuleName(mod)
+ fmt.Fprintln(w, ".PHONY:", name)
+ fmt.Fprintln(w, name+":", goBinary.InstallPath())
+ fmt.Fprintln(w, "")
+
+ return nil
+}
+
+func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
+ provider AndroidMkDataProvider) error {
name := provider.BaseModuleName()
amod := mod.(Module).base()
diff --git a/android/module.go b/android/module.go
index 4dc4e9c..92b11ed 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1527,16 +1527,16 @@
}
}
-type AndroidModulesByName struct {
- slice []Module
+type ModulesByName struct {
+ slice []blueprint.Module
ctx interface {
ModuleName(blueprint.Module) string
ModuleSubDir(blueprint.Module) string
}
}
-func (s AndroidModulesByName) Len() int { return len(s.slice) }
-func (s AndroidModulesByName) Less(i, j int) bool {
+func (s ModulesByName) Len() int { return len(s.slice) }
+func (s ModulesByName) Less(i, j int) bool {
mi, mj := s.slice[i], s.slice[j]
ni, nj := s.ctx.ModuleName(mi), s.ctx.ModuleName(mj)
@@ -1546,7 +1546,7 @@
return s.ctx.ModuleSubDir(mi) < s.ctx.ModuleSubDir(mj)
}
}
-func (s AndroidModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
+func (s ModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
// Collect information for opening IDE project files in java/jdeps.go.
type IDEInfo interface {
diff --git a/android/singleton.go b/android/singleton.go
index fa1efdc..f926435 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -48,6 +48,7 @@
// are expanded in the scope of the PackageContext.
Eval(pctx PackageContext, ninjaStr string) (string, error)
+ VisitAllModulesBlueprint(visit func(blueprint.Module))
VisitAllModules(visit func(Module))
VisitAllModulesIf(pred func(Module) bool, visit func(Module))
// Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
@@ -138,6 +139,10 @@
}
}
+func (s singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
+ s.SingletonContext.VisitAllModules(visit)
+}
+
func (s singletonContextAdaptor) VisitAllModules(visit func(Module)) {
s.SingletonContext.VisitAllModules(visitAdaptor(visit))
}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index a2f46cd..699f5c0 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -204,10 +204,11 @@
writeAllIncludeDirectories(c.systemHeaderSearchPath, f, true)
writeAllIncludeDirectories(c.headerSearchPath, f, false)
if cflags {
+ writeAllRelativeFilePathFlags(c.relativeFilePathFlags, f, "CMAKE_C_FLAGS")
writeAllFlags(c.flags, f, "CMAKE_C_FLAGS")
}
-
if cppflags {
+ writeAllRelativeFilePathFlags(c.relativeFilePathFlags, f, "CMAKE_CXX_FLAGS")
writeAllFlags(c.flags, f, "CMAKE_CXX_FLAGS")
}
if c.sysroot != "" {
@@ -249,6 +250,17 @@
f.WriteString("list (APPEND SOURCE_FILES ${TMP_HEADERS})\n\n")
}
+type relativeFilePathFlagType struct {
+ flag string
+ relativeFilePath string
+}
+
+func writeAllRelativeFilePathFlags(relativeFilePathFlags []relativeFilePathFlagType, f *os.File, tag string) {
+ for _, flag := range relativeFilePathFlags {
+ f.WriteString(fmt.Sprintf("set(%s \"${%s} %s=%s\")\n", tag, tag, flag.flag, buildCMakePath(flag.relativeFilePath)))
+ }
+}
+
func writeAllFlags(flags []string, f *os.File, tag string) {
for _, flag := range flags {
f.WriteString(fmt.Sprintf("set(%s \"${%s} %s\")\n", tag, tag, flag))
@@ -263,6 +275,7 @@
systemHeaderSearchPath
flag
systemRoot
+ relativeFilePathFlag
)
type compilerParameters struct {
@@ -270,6 +283,8 @@
systemHeaderSearchPath []string
flags []string
sysroot string
+ // Must be in a=b/c/d format and can be split into "a" and "b/c/d"
+ relativeFilePathFlags []relativeFilePathFlagType
}
func makeCompilerParameters() compilerParameters {
@@ -294,6 +309,9 @@
if strings.HasPrefix(parameter, "--sysroot") {
return systemRoot
}
+ if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
+ return relativeFilePathFlag
+ }
return flag
}
@@ -347,6 +365,16 @@
f.WriteString("# Found a system root path marker with no path")
}
i = i + 1
+ case relativeFilePathFlag:
+ flagComponents := strings.Split(param, "=")
+ if len(flagComponents) == 2 {
+ flagStruct := relativeFilePathFlagType{flag: flagComponents[0], relativeFilePath: flagComponents[1]}
+ compilerParameters.relativeFilePathFlags = append(compilerParameters.relativeFilePathFlags, flagStruct)
+ } else {
+ if outputDebugInfo {
+ f.WriteString(fmt.Sprintf("# Relative File Path Flag [%s] is not formatted as a=b/c/d \n", param))
+ }
+ }
}
}
return compilerParameters
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 186d790..5e22d6a 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -178,6 +178,18 @@
// Disable c++98-specific warning since Android is not concerned with C++98
// compatibility.
"-Wno-c++98-compat-extra-semi",
+
+ // Disable this warning until we can fix all instances where it fails.
+ "-Wno-self-assign-overloaded",
+
+ // Disable this warning until we can fix all instances where it fails.
+ "-Wno-constant-logical-operand",
+
+ // Disable this warning because we don't care about behavior with older compilers.
+ "-Wno-return-std-move-in-c++11",
+
+ // Disable this warning until we can fix all instances where it fails.
+ "-Wno-dangling-field",
}, " "))
// Extra cflags for projects under external/ directory to disable warnings that are infeasible
diff --git a/cc/config/global.go b/cc/config/global.go
index 000aab6..ad63c97 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -126,8 +126,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r328903"
- ClangDefaultShortVersion = "7.0.2"
+ ClangDefaultVersion = "clang-r339409"
+ ClangDefaultShortVersion = "8.0.1"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 95ff70b..f383de9 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -250,7 +250,12 @@
addMapping(jar.MetaDir, dirSource)
}
- fh, buf, err := jar.ManifestFileContents(manifest)
+ contents, err := ioutil.ReadFile(manifest)
+ if err != nil {
+ return err
+ }
+
+ fh, buf, err := jar.ManifestFileContents(contents)
if err != nil {
return err
}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 374868c..9cb75fa 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -328,7 +328,7 @@
NumParallelJobs: runtime.NumCPU(),
CompressionLevel: 5,
}
- if err := zip.Run(args); err != nil {
+ if err := zip.Zip(args); err != nil {
log.Fatalf("Error zipping logs: %v", err)
}
}
@@ -409,13 +409,13 @@
NumParallelJobs: runtime.NumCPU(),
CompressionLevel: 5,
}
- if err := zip.Run(args); err != nil {
+ if err := zip.Zip(args); err != nil {
log.Fatalf("Error zipping artifacts: %v", err)
}
}
if *incremental {
// Save space, Kati doesn't notice
- if f := config.KatiNinjaFile(); f != "" {
+ if f := config.KatiBuildNinjaFile(); f != "" {
os.Truncate(f, 0)
}
} else {
@@ -436,7 +436,7 @@
// Save std_full.log if Kati re-read the makefiles
if buildWhat&build.BuildKati != 0 {
- if after, err := os.Stat(config.KatiNinjaFile()); err == nil && after.ModTime().After(before) {
+ if after, err := os.Stat(config.KatiBuildNinjaFile()); err == nil && after.ModTime().After(before) {
err := copyFile(stdLog, filepath.Join(filepath.Dir(stdLog), "std_full.log"))
if err != nil {
log.Fatalf("Error copying log file: %s", err)
diff --git a/jar/jar.go b/jar/jar.go
index 653e5ee..fa0e693 100644
--- a/jar/jar.go
+++ b/jar/jar.go
@@ -17,7 +17,6 @@
import (
"bytes"
"fmt"
- "io/ioutil"
"os"
"strings"
"time"
@@ -81,10 +80,9 @@
return dirHeader
}
-// Convert manifest source path to zip header and contents. If path is empty uses a default
-// manifest.
-func ManifestFileContents(src string) (*zip.FileHeader, []byte, error) {
- b, err := manifestContents(src)
+// Create a manifest zip header and contents using the provided contents if any.
+func ManifestFileContents(contents []byte) (*zip.FileHeader, []byte, error) {
+ b, err := manifestContents(contents)
if err != nil {
return nil, nil, err
}
@@ -100,26 +98,16 @@
return fh, b, nil
}
-// Convert manifest source path to contents. If path is empty uses a default manifest.
-func manifestContents(src string) ([]byte, error) {
- var givenBytes []byte
- var err error
-
- if src != "" {
- givenBytes, err = ioutil.ReadFile(src)
- if err != nil {
- return nil, err
- }
- }
-
+// Create manifest contents, using the provided contents if any.
+func manifestContents(contents []byte) ([]byte, error) {
manifestMarker := []byte("Manifest-Version:")
header := append(manifestMarker, []byte(" 1.0\nCreated-By: soong_zip\n")...)
var finalBytes []byte
- if !bytes.Contains(givenBytes, manifestMarker) {
- finalBytes = append(append(header, givenBytes...), byte('\n'))
+ if !bytes.Contains(contents, manifestMarker) {
+ finalBytes = append(append(header, contents...), byte('\n'))
} else {
- finalBytes = givenBytes
+ finalBytes = contents
}
return finalBytes, nil
diff --git a/java/app_builder.go b/java/app_builder.go
index 954ca44..e27b1b7 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -103,10 +103,10 @@
`cp ${manifest} ${outDir}/AndroidManifest.xml && ` +
`cp ${classesJar} ${outDir}/classes.jar && ` +
`cp ${rTxt} ${outDir}/R.txt && ` +
- `${config.SoongZipCmd} -jar -o $out -C ${outDir} -D ${outDir} ${resArgs}`,
+ `${config.SoongZipCmd} -jar -o $out -C ${outDir} -D ${outDir}`,
CommandDeps: []string{"${config.SoongZipCmd}"},
},
- "manifest", "classesJar", "rTxt", "resArgs", "outDir")
+ "manifest", "classesJar", "rTxt", "outDir")
func BuildAAR(ctx android.ModuleContext, outputFile android.WritablePath,
classesJar, manifest, rTxt android.Path, res android.Paths) {
diff --git a/java/builder.go b/java/builder.go
index 48b5a7b..07af8eb 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -321,7 +321,7 @@
Output: outputFile,
Implicits: deps,
Args: map[string]string{
- "jarArgs": strings.Join(proptools.NinjaEscape(jarArgs), " "),
+ "jarArgs": strings.Join(proptools.NinjaAndShellEscape(jarArgs), " "),
},
})
}
diff --git a/java/dex.go b/java/dex.go
index c612a0c..ce0c18e 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -26,7 +26,7 @@
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
`${config.D8Cmd} --output $outDir $d8Flags $in && ` +
- `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
+ `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
"${config.D8Cmd}",
@@ -46,7 +46,7 @@
`-printmapping $outDict ` +
`$r8Flags && ` +
`touch "$outDict" && ` +
- `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
+ `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
CommandDeps: []string{
"${config.R8Cmd}",
@@ -82,13 +82,17 @@
return flags
}
-func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) []string {
+func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
d8Flags := j.dexCommonFlags(ctx)
d8Flags = append(d8Flags, flags.bootClasspath.FormTurbineClasspath("--lib")...)
d8Flags = append(d8Flags, flags.classpath.FormTurbineClasspath("--lib")...)
- return d8Flags
+ var d8Deps android.Paths
+ d8Deps = append(d8Deps, flags.bootClasspath...)
+ d8Deps = append(d8Deps, flags.classpath...)
+
+ return d8Flags, d8Deps
}
func (j *Module) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
@@ -113,6 +117,10 @@
r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
r8Flags = append(r8Flags, "-forceprocessing")
+ r8Deps = append(r8Deps, proguardRaiseDeps...)
+ r8Deps = append(r8Deps, flags.bootClasspath...)
+ r8Deps = append(r8Deps, flags.classpath...)
+
flagFiles := android.Paths{
android.PathForSource(ctx, "build/make/core/proguard.flags"),
}
@@ -182,12 +190,13 @@
},
})
} else {
- d8Flags := j.d8Flags(ctx, flags)
+ d8Flags, d8Deps := j.d8Flags(ctx, flags)
ctx.Build(pctx, android.BuildParams{
Rule: d8,
Description: "d8",
Output: javalibJar,
Input: classesJar,
+ Implicits: d8Deps,
Args: map[string]string{
"d8Flags": strings.Join(d8Flags, " "),
"outDir": outDir.String(),
diff --git a/java/java.go b/java/java.go
index 0bd7857..7fd5344 100644
--- a/java/java.go
+++ b/java/java.go
@@ -95,6 +95,9 @@
// list of java libraries that will be compiled into the resulting jar
Static_libs []string `android:"arch_variant"`
+ // list of native libraries that will be provided in or alongside the resulting jar
+ Jni_libs []string `android:"arch_variant"`
+
// manifest file to be included in resulting jar
Manifest *string
diff --git a/java/java_resources.go b/java/java_resources.go
index fdc1590..6c1fd39 100644
--- a/java/java_resources.go
+++ b/java/java_resources.go
@@ -19,6 +19,8 @@
"path/filepath"
"strings"
+ "github.com/google/blueprint/pathtools"
+
"android/soong/android"
)
@@ -64,7 +66,7 @@
if !strings.HasPrefix(path, dir.String()) {
panic(fmt.Errorf("path %q does not start with %q", path, dir))
}
- args = append(args, "-f", path)
+ args = append(args, "-f", pathtools.MatchEscape(path))
}
}
}
@@ -107,7 +109,7 @@
if i == 0 || dir != lastDir {
args = append(args, "-C", dir)
}
- args = append(args, "-f", path)
+ args = append(args, "-f", pathtools.MatchEscape(path))
lastDir = dir
}
diff --git a/scripts/strip.sh b/scripts/strip.sh
index bfc66ee..29594dc 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -54,7 +54,7 @@
# ${CROSS_COMPILE}strip --strip-all does not strip .ARM.attributes,
# so we tell llvm-strip to keep it too.
if [ ! -z "${use_llvm_strip}" ]; then
- "${CLANG_BIN}/llvm-strip" --strip-all -keep=.ARM.attributes "${infile}" "${outfile}.tmp"
+ "${CLANG_BIN}/llvm-strip" --strip-all -keep=.ARM.attributes "${infile}" -o "${outfile}.tmp"
else
"${CROSS_COMPILE}strip" --strip-all "${infile}" -o "${outfile}.tmp"
fi
@@ -75,7 +75,7 @@
rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz"
local fail=
if [ ! -z "${use_llvm_strip}" ]; then
- "${CLANG_BIN}/llvm-strip" --strip-all -keep=.ARM.attributes -remove-section=.comment "${infile}" "${outfile}.tmp" || fail=true
+ "${CLANG_BIN}/llvm-strip" --strip-all -keep=.ARM.attributes -remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
else
"${CROSS_COMPILE}strip" --strip-all -R .comment "${infile}" -o "${outfile}.tmp" || fail=true
fi
diff --git a/ui/build/build.go b/ui/build/build.go
index 52ef005..377481b 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -37,7 +37,10 @@
var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
builddir = {{.OutDir}}
-{{if .HasKatiSuffix}}include {{.KatiNinjaFile}}
+pool local_pool
+ depth = {{.Parallel}}
+build _kati_always_build_: phony
+{{if .HasKatiSuffix}}include {{.KatiBuildNinjaFile}}
{{end -}}
include {{.SoongNinjaFile}}
`))
@@ -174,7 +177,9 @@
if what&BuildKati != 0 {
// Run ckati
- runKati(ctx, config)
+ genKatiSuffix(ctx, config)
+ runKatiCleanSpec(ctx, config)
+ runKatiBuild(ctx, config)
ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
} else {
diff --git a/ui/build/config.go b/ui/build/config.go
index fdeb8f2..d470b96 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -501,8 +501,8 @@
return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh")
}
-func (c *configImpl) KatiNinjaFile() string {
- return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+".ninja")
+func (c *configImpl) KatiBuildNinjaFile() string {
+ return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiBuildSuffix+".ninja")
}
func (c *configImpl) SoongNinjaFile() string {
diff --git a/ui/build/kati.go b/ui/build/kati.go
index b26d673..546fd1a 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -19,7 +19,6 @@
"fmt"
"io/ioutil"
"path/filepath"
- "strconv"
"strings"
"android/soong/ui/status"
@@ -27,6 +26,9 @@
var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_")
+const katiBuildSuffix = ""
+const katiCleanspecSuffix = "-cleanspec"
+
// genKatiSuffix creates a suffix for kati-generated files so that we can cache
// them based on their inputs. So this should encode all common changes to Kati
// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
@@ -49,7 +51,7 @@
ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix)
ctx.Verbosef("Replacing with: %q", shortSuffix)
- if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
+ if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiBuildNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
ctx.Println("Error writing suffix file:", err)
}
} else {
@@ -57,30 +59,49 @@
}
}
-func runKati(ctx Context, config Config) {
- genKatiSuffix(ctx, config)
-
- runKatiCleanSpec(ctx, config)
-
- ctx.BeginTrace("kati")
- defer ctx.EndTrace()
-
+func runKati(ctx Context, config Config, extraSuffix string, args []string) {
executable := config.PrebuiltBuildTool("ckati")
- args := []string{
+ args = append([]string{
"--ninja",
"--ninja_dir=" + config.OutDir(),
- "--ninja_suffix=" + config.KatiSuffix(),
+ "--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
+ "--no_ninja_prelude",
"--regen",
"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
"--detect_android_echo",
"--color_warnings",
"--gen_all_targets",
+ "--use_find_emulator",
"--werror_find_emulator",
"--no_builtin_rules",
"--werror_suffix_rules",
"--warn_real_to_phony",
"--warn_phony_looks_real",
"--kati_stats",
+ }, args...)
+
+ args = append(args,
+ "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
+ "TARGET_DEVICE_DIR="+config.TargetDeviceDir())
+
+ cmd := Command(ctx, config, "ckati", executable, args...)
+ cmd.Sandbox = katiSandbox
+ pipe, err := cmd.StdoutPipe()
+ if err != nil {
+ ctx.Fatalln("Error getting output pipe for ckati:", err)
+ }
+ cmd.Stderr = cmd.Stdout
+
+ cmd.StartOrFatal()
+ status.KatiReader(ctx.Status.StartTool(), pipe)
+ cmd.WaitOrFatal()
+}
+
+func runKatiBuild(ctx Context, config Config) {
+ ctx.BeginTrace("kati build")
+ defer ctx.EndTrace()
+
+ args := []string{
"--writable", config.OutDir() + "/",
"--writable", config.DistDir() + "/",
"-f", "build/make/core/main.mk",
@@ -102,67 +123,20 @@
"--werror_writable")
}
- if !config.Environment().IsFalse("KATI_EMULATE_FIND") {
- args = append(args, "--use_find_emulator")
- }
-
args = append(args, config.KatiArgs()...)
- args = append(args,
- "BUILDING_WITH_NINJA=true",
- "SOONG_ANDROID_MK="+config.SoongAndroidMk(),
- "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
- "TARGET_DEVICE_DIR="+config.TargetDeviceDir())
+ args = append(args, "SOONG_ANDROID_MK="+config.SoongAndroidMk())
- if config.UseGoma() {
- args = append(args, "-j"+strconv.Itoa(config.Parallel()))
- }
-
- cmd := Command(ctx, config, "ckati", executable, args...)
- cmd.Sandbox = katiSandbox
- pipe, err := cmd.StdoutPipe()
- if err != nil {
- ctx.Fatalln("Error getting output pipe for ckati:", err)
- }
- cmd.Stderr = cmd.Stdout
-
- cmd.StartOrFatal()
- status.KatiReader(ctx.Status.StartTool(), pipe)
- cmd.WaitOrFatal()
+ runKati(ctx, config, katiBuildSuffix, args)
}
func runKatiCleanSpec(ctx Context, config Config) {
ctx.BeginTrace("kati cleanspec")
defer ctx.EndTrace()
- executable := config.PrebuiltBuildTool("ckati")
- args := []string{
- "--ninja",
- "--ninja_dir=" + config.OutDir(),
- "--ninja_suffix=" + config.KatiSuffix() + "-cleanspec",
- "--regen",
- "--detect_android_echo",
- "--color_warnings",
- "--gen_all_targets",
- "--werror_find_emulator",
+ runKati(ctx, config, katiCleanspecSuffix, []string{
+ "--werror_implicit_rules",
"--werror_overriding_commands",
- "--use_find_emulator",
- "--kati_stats",
"-f", "build/make/core/cleanbuild.mk",
- "BUILDING_WITH_NINJA=true",
- "SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
- "TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
- }
-
- cmd := Command(ctx, config, "ckati", executable, args...)
- cmd.Sandbox = katiCleanSpecSandbox
- pipe, err := cmd.StdoutPipe()
- if err != nil {
- ctx.Fatalln("Error getting output pipe for ckati:", err)
- }
- cmd.Stderr = cmd.Stdout
-
- cmd.StartOrFatal()
- status.KatiReader(ctx.Status.StartTool(), pipe)
- cmd.WaitOrFatal()
+ })
}
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index dfd56dc..b4f75f7 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -15,29 +15,19 @@
package main
import (
- "bytes"
"flag"
"fmt"
- "io"
"io/ioutil"
"os"
"runtime"
+ "runtime/pprof"
+ "runtime/trace"
"strconv"
"strings"
"android/soong/zip"
)
-type byteReaderCloser struct {
- *bytes.Reader
- io.Closer
-}
-
-type pathMapping struct {
- dest, src string
- zipMethod uint16
-}
-
type uniqueSet map[string]bool
func (u *uniqueSet) String() string {
@@ -56,106 +46,67 @@
type file struct{}
+func (file) String() string { return `""` }
+
+func (file) Set(s string) error {
+ fileArgsBuilder.File(s)
+ return nil
+}
+
type listFiles struct{}
+func (listFiles) String() string { return `""` }
+
+func (listFiles) Set(s string) error {
+ fileArgsBuilder.List(s)
+ return nil
+}
+
type dir struct{}
-func (f *file) String() string {
- return `""`
-}
+func (dir) String() string { return `""` }
-func (f *file) Set(s string) error {
- if relativeRoot == "" && !junkPaths {
- return fmt.Errorf("must pass -C or -j before -f")
- }
-
- fArgs = append(fArgs, zip.FileArg{
- PathPrefixInZip: *rootPrefix,
- SourcePrefixToStrip: relativeRoot,
- JunkPaths: junkPaths,
- SourceFiles: []string{s},
- })
-
+func (dir) Set(s string) error {
+ fileArgsBuilder.Dir(s)
return nil
}
-func (l *listFiles) String() string {
- return `""`
-}
+type relativeRoot struct{}
-func (l *listFiles) Set(s string) error {
- if relativeRoot == "" && !junkPaths {
- return fmt.Errorf("must pass -C or -j before -l")
- }
+func (relativeRoot) String() string { return "" }
- list, err := ioutil.ReadFile(s)
- if err != nil {
- return err
- }
-
- fArgs = append(fArgs, zip.FileArg{
- PathPrefixInZip: *rootPrefix,
- SourcePrefixToStrip: relativeRoot,
- JunkPaths: junkPaths,
- SourceFiles: strings.Split(string(list), "\n"),
- })
-
+func (relativeRoot) Set(s string) error {
+ fileArgsBuilder.SourcePrefixToStrip(s)
return nil
}
-func (d *dir) String() string {
- return `""`
-}
+type junkPaths struct{}
-func (d *dir) Set(s string) error {
- if relativeRoot == "" && !junkPaths {
- return fmt.Errorf("must pass -C or -j before -D")
- }
+func (junkPaths) IsBoolFlag() bool { return true }
+func (junkPaths) String() string { return "" }
- fArgs = append(fArgs, zip.FileArg{
- PathPrefixInZip: *rootPrefix,
- SourcePrefixToStrip: relativeRoot,
- JunkPaths: junkPaths,
- GlobDir: s,
- })
-
- return nil
-}
-
-type relativeRootImpl struct{}
-
-func (*relativeRootImpl) String() string { return relativeRoot }
-
-func (*relativeRootImpl) Set(s string) error {
- relativeRoot = s
- junkPaths = false
- return nil
-}
-
-type junkPathsImpl struct{}
-
-func (*junkPathsImpl) IsBoolFlag() bool { return true }
-
-func (*junkPathsImpl) String() string { return relativeRoot }
-
-func (*junkPathsImpl) Set(s string) error {
- var err error
- junkPaths, err = strconv.ParseBool(s)
- relativeRoot = ""
+func (junkPaths) Set(s string) error {
+ v, err := strconv.ParseBool(s)
+ fileArgsBuilder.JunkPaths(v)
return err
}
-var (
- rootPrefix *string
- relativeRoot string
- junkPaths bool
+type rootPrefix struct{}
- fArgs zip.FileArgs
+func (rootPrefix) String() string { return "" }
+
+func (rootPrefix) Set(s string) error {
+ fileArgsBuilder.PathPrefixInZip(s)
+ return nil
+}
+
+var (
+ fileArgsBuilder = zip.NewFileArgsBuilder()
nonDeflatedFiles = make(uniqueSet)
)
func usage() {
- fmt.Fprintf(os.Stderr, "usage: zip -o zipfile [-m manifest] -C dir [-f|-l file]...\n")
+ fmt.Fprintf(os.Stderr, "usage: soong_zip -o zipfile [-m manifest] [-C dir] [-f|-l file] [-D dir]...\n")
flag.PrintDefaults()
os.Exit(2)
}
@@ -177,33 +128,70 @@
}
flags := flag.NewFlagSet("flags", flag.ExitOnError)
+ flags.Usage = usage
out := flags.String("o", "", "file to write zip file to")
manifest := flags.String("m", "", "input jar manifest file name")
directories := flags.Bool("d", false, "include directories in zip")
- rootPrefix = flags.String("P", "", "path prefix within the zip at which to place files")
compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
+ ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
+ symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
traceFile := flags.String("trace", "", "write trace to file")
+ flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
flags.Var(&listFiles{}, "l", "file containing list of .class files")
flags.Var(&dir{}, "D", "directory to include in zip")
flags.Var(&file{}, "f", "file to include in zip")
flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
- flags.Var(&relativeRootImpl{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
- flags.Var(&junkPathsImpl{}, "j", "junk paths, zip files without directory names")
+ flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
+ flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
flags.Parse(expandedArgs[1:])
- err := zip.Run(zip.ZipArgs{
- FileArgs: fArgs,
+ if flags.NArg() > 0 {
+ fmt.Fprintf(os.Stderr, "unexpected arguments %s\n", strings.Join(flags.Args(), " "))
+ flags.Usage()
+ }
+
+ if *cpuProfile != "" {
+ f, err := os.Create(*cpuProfile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ defer f.Close()
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ if *traceFile != "" {
+ f, err := os.Create(*traceFile)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ defer f.Close()
+ err = trace.Start(f)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ defer trace.Stop()
+ }
+
+ if fileArgsBuilder.Error() != nil {
+ fmt.Fprintln(os.Stderr, fileArgsBuilder.Error())
+ os.Exit(1)
+ }
+
+ err := zip.Zip(zip.ZipArgs{
+ FileArgs: fileArgsBuilder.FileArgs(),
OutputFilePath: *out,
- CpuProfileFilePath: *cpuProfile,
- TraceFilePath: *traceFile,
EmulateJar: *emulateJar,
AddDirectoryEntriesToZip: *directories,
CompressionLevel: *compLevel,
@@ -211,9 +199,11 @@
NumParallelJobs: *parallelJobs,
NonDeflatedFiles: nonDeflatedFiles,
WriteIfChanged: *writeIfChanged,
+ StoreSymlinks: *symlinks,
+ IgnoreMissingFiles: *ignoreMissingFiles,
})
if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
+ fmt.Fprintln(os.Stderr, "error:", err.Error())
os.Exit(1)
}
}
diff --git a/zip/zip.go b/zip/zip.go
index 6b36e10..774966a 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -22,14 +22,12 @@
"hash/crc32"
"io"
"io/ioutil"
- "log"
"os"
"path/filepath"
- "runtime/pprof"
- "runtime/trace"
"sort"
"strings"
"sync"
+ "syscall"
"time"
"unicode"
@@ -68,22 +66,6 @@
zipMethod uint16
}
-type uniqueSet map[string]bool
-
-func (u *uniqueSet) String() string {
- return `""`
-}
-
-func (u *uniqueSet) Set(s string) error {
- if _, found := (*u)[s]; found {
- return fmt.Errorf("File %q was specified twice as a file to not deflate", s)
- } else {
- (*u)[s] = true
- }
-
- return nil
-}
-
type FileArg struct {
PathPrefixInZip, SourcePrefixToStrip string
SourceFiles []string
@@ -91,7 +73,105 @@
GlobDir string
}
-type FileArgs []FileArg
+type FileArgsBuilder struct {
+ state FileArg
+ err error
+ fs pathtools.FileSystem
+
+ fileArgs []FileArg
+}
+
+func NewFileArgsBuilder() *FileArgsBuilder {
+ return &FileArgsBuilder{
+ fs: pathtools.OsFs,
+ }
+}
+
+func (b *FileArgsBuilder) JunkPaths(v bool) *FileArgsBuilder {
+ b.state.JunkPaths = v
+ b.state.SourcePrefixToStrip = ""
+ return b
+}
+
+func (b *FileArgsBuilder) SourcePrefixToStrip(prefixToStrip string) *FileArgsBuilder {
+ b.state.JunkPaths = false
+ b.state.SourcePrefixToStrip = prefixToStrip
+ return b
+}
+
+func (b *FileArgsBuilder) PathPrefixInZip(rootPrefix string) *FileArgsBuilder {
+ b.state.PathPrefixInZip = rootPrefix
+ return b
+}
+
+func (b *FileArgsBuilder) File(name string) *FileArgsBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ arg := b.state
+ arg.SourceFiles = []string{name}
+ b.fileArgs = append(b.fileArgs, arg)
+ return b
+}
+
+func (b *FileArgsBuilder) Dir(name string) *FileArgsBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ arg := b.state
+ arg.GlobDir = name
+ b.fileArgs = append(b.fileArgs, arg)
+ return b
+}
+
+func (b *FileArgsBuilder) List(name string) *FileArgsBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ f, err := b.fs.Open(name)
+ if err != nil {
+ b.err = err
+ return b
+ }
+ defer f.Close()
+
+ list, err := ioutil.ReadAll(f)
+ if err != nil {
+ b.err = err
+ return b
+ }
+
+ arg := b.state
+ arg.SourceFiles = strings.Split(string(list), "\n")
+ b.fileArgs = append(b.fileArgs, arg)
+ return b
+}
+
+func (b *FileArgsBuilder) Error() error {
+ if b == nil {
+ return nil
+ }
+ return b.err
+}
+
+func (b *FileArgsBuilder) FileArgs() []FileArg {
+ if b == nil {
+ return nil
+ }
+ return b.fileArgs
+}
+
+type IncorrectRelativeRootError struct {
+ RelativeRoot string
+ Path string
+}
+
+func (x IncorrectRelativeRootError) Error() string {
+ return fmt.Sprintf("path %q is outside relative root %q", x.Path, x.RelativeRoot)
+}
type ZipWriter struct {
time time.Time
@@ -107,6 +187,12 @@
compressorPool sync.Pool
compLevel int
+
+ followSymlinks pathtools.ShouldFollowSymlinks
+ ignoreMissingFiles bool
+
+ stderr io.Writer
+ fs pathtools.FileSystem
}
type zipEntry struct {
@@ -121,10 +207,8 @@
}
type ZipArgs struct {
- FileArgs FileArgs
+ FileArgs []FileArg
OutputFilePath string
- CpuProfileFilePath string
- TraceFilePath string
EmulateJar bool
AddDirectoryEntriesToZip bool
CompressionLevel int
@@ -132,6 +216,11 @@
NumParallelJobs int
NonDeflatedFiles map[string]bool
WriteIfChanged bool
+ StoreSymlinks bool
+ IgnoreMissingFiles bool
+
+ Stderr io.Writer
+ Filesystem pathtools.FileSystem
}
const NOQUOTE = '\x00'
@@ -177,65 +266,112 @@
return args
}
-func Run(args ZipArgs) (err error) {
- if args.CpuProfileFilePath != "" {
- f, err := os.Create(args.CpuProfileFilePath)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- defer f.Close()
- pprof.StartCPUProfile(f)
- defer pprof.StopCPUProfile()
- }
-
- if args.TraceFilePath != "" {
- f, err := os.Create(args.TraceFilePath)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- defer f.Close()
- err = trace.Start(f)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- defer trace.Stop()
- }
-
- if args.OutputFilePath == "" {
- return fmt.Errorf("output file path must be nonempty")
- }
-
+func ZipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar {
args.AddDirectoryEntriesToZip = true
}
- w := &ZipWriter{
- time: jar.DefaultTime,
- createdDirs: make(map[string]string),
- createdFiles: make(map[string]string),
- directories: args.AddDirectoryEntriesToZip,
- compLevel: args.CompressionLevel,
+ // Have Glob follow symlinks if they are not being stored as symlinks in the zip file.
+ followSymlinks := pathtools.ShouldFollowSymlinks(!args.StoreSymlinks)
+
+ z := &ZipWriter{
+ time: jar.DefaultTime,
+ createdDirs: make(map[string]string),
+ createdFiles: make(map[string]string),
+ directories: args.AddDirectoryEntriesToZip,
+ compLevel: args.CompressionLevel,
+ followSymlinks: followSymlinks,
+ ignoreMissingFiles: args.IgnoreMissingFiles,
+ stderr: args.Stderr,
+ fs: args.Filesystem,
}
+
+ if z.fs == nil {
+ z.fs = pathtools.OsFs
+ }
+
+ if z.stderr == nil {
+ z.stderr = os.Stderr
+ }
+
pathMappings := []pathMapping{}
noCompression := args.CompressionLevel == 0
for _, fa := range args.FileArgs {
- srcs := fa.SourceFiles
+ var srcs []string
+ for _, s := range fa.SourceFiles {
+ s = strings.TrimSpace(s)
+ if s == "" {
+ continue
+ }
+
+ globbed, _, err := z.fs.Glob(s, nil, followSymlinks)
+ if err != nil {
+ return err
+ }
+ if len(globbed) == 0 {
+ err := &os.PathError{
+ Op: "lstat",
+ Path: s,
+ Err: os.ErrNotExist,
+ }
+ if args.IgnoreMissingFiles {
+ fmt.Fprintln(args.Stderr, "warning:", err)
+ } else {
+ return err
+ }
+ }
+ srcs = append(srcs, globbed...)
+ }
if fa.GlobDir != "" {
- srcs = append(srcs, recursiveGlobFiles(fa.GlobDir)...)
+ if exists, isDir, err := z.fs.Exists(fa.GlobDir); err != nil {
+ return err
+ } else if !exists && !args.IgnoreMissingFiles {
+ err := &os.PathError{
+ Op: "lstat",
+ Path: fa.GlobDir,
+ Err: os.ErrNotExist,
+ }
+ if args.IgnoreMissingFiles {
+ fmt.Fprintln(args.Stderr, "warning:", err)
+ } else {
+ return err
+ }
+ } else if !isDir && !args.IgnoreMissingFiles {
+ err := &os.PathError{
+ Op: "lstat",
+ Path: fa.GlobDir,
+ Err: syscall.ENOTDIR,
+ }
+ if args.IgnoreMissingFiles {
+ fmt.Fprintln(args.Stderr, "warning:", err)
+ } else {
+ return err
+ }
+ }
+ globbed, _, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
+ if err != nil {
+ return err
+ }
+ srcs = append(srcs, globbed...)
}
for _, src := range srcs {
err := fillPathPairs(fa, src, &pathMappings, args.NonDeflatedFiles, noCompression)
if err != nil {
- log.Fatal(err)
+ return err
}
}
}
+ return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
+}
+
+func Zip(args ZipArgs) error {
+ if args.OutputFilePath == "" {
+ return fmt.Errorf("output file path must be nonempty")
+ }
+
buf := &bytes.Buffer{}
var out io.Writer = buf
@@ -255,7 +391,7 @@
out = f
}
- err = w.write(out, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
+ err := ZipTo(args, out)
if err != nil {
return err
}
@@ -273,11 +409,6 @@
func fillPathPairs(fa FileArg, src string, pathMappings *[]pathMapping,
nonDeflatedFiles map[string]bool, noCompression bool) error {
- src = strings.TrimSpace(src)
- if src == "" {
- return nil
- }
- src = filepath.Clean(src)
var dest string
if fa.JunkPaths {
@@ -288,6 +419,13 @@
if err != nil {
return err
}
+ if strings.HasPrefix(dest, "../") {
+ return IncorrectRelativeRootError{
+ Path: src,
+ RelativeRoot: fa.SourcePrefixToStrip,
+ }
+ }
+
}
dest = filepath.Join(fa.PathPrefixInZip, dest)
@@ -308,13 +446,6 @@
sort.SliceStable(mappings, less)
}
-type readerSeekerCloser interface {
- io.Reader
- io.ReaderAt
- io.Closer
- io.Seeker
-}
-
func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar bool, parallelJobs int) error {
z.errors = make(chan error)
defer close(z.errors)
@@ -461,7 +592,19 @@
var fileSize int64
var executable bool
- if s, err := os.Lstat(src); err != nil {
+ var s os.FileInfo
+ var err error
+ if z.followSymlinks {
+ s, err = z.fs.Stat(src)
+ } else {
+ s, err = z.fs.Lstat(src)
+ }
+
+ if err != nil {
+ if os.IsNotExist(err) && z.ignoreMissingFiles {
+ fmt.Fprintln(z.stderr, "warning:", err)
+ return nil
+ }
return err
} else if s.IsDir() {
if z.directories {
@@ -492,7 +635,7 @@
executable = s.Mode()&0100 != 0
}
- r, err := os.Open(src)
+ r, err := z.fs.Open(src)
if err != nil {
return err
}
@@ -522,7 +665,21 @@
return err
}
- fh, buf, err := jar.ManifestFileContents(src)
+ var contents []byte
+ if src != "" {
+ f, err := z.fs.Open(src)
+ if err != nil {
+ return err
+ }
+
+ contents, err = ioutil.ReadAll(f)
+ f.Close()
+ if err != nil {
+ return err
+ }
+ }
+
+ fh, buf, err := jar.ManifestFileContents(contents)
if err != nil {
return err
}
@@ -532,7 +689,7 @@
return z.writeFileContents(fh, reader)
}
-func (z *ZipWriter) writeFileContents(header *zip.FileHeader, r readerSeekerCloser) (err error) {
+func (z *ZipWriter) writeFileContents(header *zip.FileHeader, r pathtools.ReaderAtSeekerCloser) (err error) {
header.SetModTime(z.time)
@@ -802,7 +959,7 @@
fileHeader.SetModTime(z.time)
fileHeader.SetMode(0777 | os.ModeSymlink)
- dest, err := os.Readlink(file)
+ dest, err := z.fs.Readlink(file)
if err != nil {
return err
}
@@ -827,15 +984,3 @@
return nil
}
-
-func recursiveGlobFiles(path string) []string {
- var files []string
- filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
- if !info.IsDir() {
- files = append(files, path)
- }
- return nil
- })
-
- return files
-}
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 03e7958..93c5f3d 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -15,10 +15,479 @@
package zip
import (
+ "bytes"
+ "hash/crc32"
+ "io"
+ "os"
"reflect"
+ "syscall"
"testing"
+
+ "android/soong/third_party/zip"
+
+ "github.com/google/blueprint/pathtools"
)
+var (
+ fileA = []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
+ fileB = []byte("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
+ fileC = []byte("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")
+ fileEmpty = []byte("")
+ fileManifest = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\n\n")
+
+ fileCustomManifest = []byte("Custom manifest: true\n")
+ customManifestAfter = []byte("Manifest-Version: 1.0\nCreated-By: soong_zip\nCustom manifest: true\n\n")
+)
+
+var mockFs = pathtools.MockFs(map[string][]byte{
+ "a/a/a": fileA,
+ "a/a/b": fileB,
+ "a/a/c -> ../../c": nil,
+ "a/a/d -> b": nil,
+ "c": fileC,
+ "l": []byte("a/a/a\na/a/b\nc\n"),
+ "l2": []byte("missing\n"),
+ "manifest.txt": fileCustomManifest,
+})
+
+func fh(name string, contents []byte, method uint16) zip.FileHeader {
+ return zip.FileHeader{
+ Name: name,
+ Method: method,
+ CRC32: crc32.ChecksumIEEE(contents),
+ UncompressedSize64: uint64(len(contents)),
+ ExternalAttrs: 0,
+ }
+}
+
+func fhManifest(contents []byte) zip.FileHeader {
+ return zip.FileHeader{
+ Name: "META-INF/MANIFEST.MF",
+ Method: zip.Store,
+ CRC32: crc32.ChecksumIEEE(contents),
+ UncompressedSize64: uint64(len(contents)),
+ ExternalAttrs: (syscall.S_IFREG | 0700) << 16,
+ }
+}
+
+func fhLink(name string, to string) zip.FileHeader {
+ return zip.FileHeader{
+ Name: name,
+ Method: zip.Store,
+ CRC32: crc32.ChecksumIEEE([]byte(to)),
+ UncompressedSize64: uint64(len(to)),
+ ExternalAttrs: (syscall.S_IFLNK | 0777) << 16,
+ }
+}
+
+func fhDir(name string) zip.FileHeader {
+ return zip.FileHeader{
+ Name: name,
+ Method: zip.Store,
+ CRC32: crc32.ChecksumIEEE(nil),
+ UncompressedSize64: 0,
+ ExternalAttrs: (syscall.S_IFDIR|0700)<<16 | 0x10,
+ }
+}
+
+func fileArgsBuilder() *FileArgsBuilder {
+ return &FileArgsBuilder{
+ fs: mockFs,
+ }
+}
+
+func TestZip(t *testing.T) {
+ testCases := []struct {
+ name string
+ args *FileArgsBuilder
+ compressionLevel int
+ emulateJar bool
+ nonDeflatedFiles map[string]bool
+ dirEntries bool
+ manifest string
+ storeSymlinks bool
+ ignoreMissingFiles bool
+
+ files []zip.FileHeader
+ err error
+ }{
+ {
+ name: "empty args",
+ args: fileArgsBuilder(),
+
+ files: []zip.FileHeader{},
+ },
+ {
+ name: "files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("c"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fh("c", fileC, zip.Deflate),
+ },
+ },
+ {
+ name: "files glob",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/**/*"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("a/b", fileB, zip.Deflate),
+ fhLink("a/c", "../../c"),
+ fhLink("a/d", "b"),
+ },
+ },
+ {
+ name: "dir",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ Dir("a"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("a/b", fileB, zip.Deflate),
+ fhLink("a/c", "../../c"),
+ fhLink("a/d", "b"),
+ },
+ },
+ {
+ name: "stored files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("c"),
+ compressionLevel: 0,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Store),
+ fh("a/a/b", fileB, zip.Store),
+ fh("c", fileC, zip.Store),
+ },
+ },
+ {
+ name: "symlinks in zip",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("a/a/d"),
+ compressionLevel: 9,
+ storeSymlinks: true,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fhLink("a/a/c", "../../c"),
+ fhLink("a/a/d", "b"),
+ },
+ },
+ {
+ name: "follow symlinks",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("a/a/c").
+ File("a/a/d"),
+ compressionLevel: 9,
+ storeSymlinks: false,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fh("a/a/c", fileC, zip.Deflate),
+ fh("a/a/d", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "list",
+ args: fileArgsBuilder().
+ List("l"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ fh("c", fileC, zip.Deflate),
+ },
+ },
+ {
+ name: "prefix in zip",
+ args: fileArgsBuilder().
+ PathPrefixInZip("foo").
+ File("a/a/a").
+ File("a/a/b").
+ File("c"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("foo/a/a/a", fileA, zip.Deflate),
+ fh("foo/a/a/b", fileB, zip.Deflate),
+ fh("foo/c", fileC, zip.Deflate),
+ },
+ },
+ {
+ name: "relative root",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "multiple relative root",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("a").
+ File("a/a/a").
+ SourcePrefixToStrip("a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a/a", fileA, zip.Deflate),
+ fh("b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "emulate jar",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ emulateJar: true,
+
+ files: []zip.FileHeader{
+ fhDir("META-INF/"),
+ fhManifest(fileManifest),
+ fhDir("a/"),
+ fhDir("a/a/"),
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "emulate jar with manifest",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ emulateJar: true,
+ manifest: "manifest.txt",
+
+ files: []zip.FileHeader{
+ fhDir("META-INF/"),
+ fhManifest(customManifestAfter),
+ fhDir("a/"),
+ fhDir("a/a/"),
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "dir entries",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ dirEntries: true,
+
+ files: []zip.FileHeader{
+ fhDir("a/"),
+ fhDir("a/a/"),
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "junk paths",
+ args: fileArgsBuilder().
+ JunkPaths(true).
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("a", fileA, zip.Deflate),
+ fh("b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "non deflated files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+ nonDeflatedFiles: map[string]bool{"a/a/a": true},
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Store),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "ignore missing files",
+ args: fileArgsBuilder().
+ File("a/a/a").
+ File("a/a/b").
+ File("missing"),
+ compressionLevel: 9,
+ ignoreMissingFiles: true,
+
+ files: []zip.FileHeader{
+ fh("a/a/a", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+
+ // errors
+ {
+ name: "error missing file",
+ args: fileArgsBuilder().
+ File("missing"),
+ err: os.ErrNotExist,
+ },
+ {
+ name: "error missing dir",
+ args: fileArgsBuilder().
+ Dir("missing"),
+ err: os.ErrNotExist,
+ },
+ {
+ name: "error missing file in list",
+ args: fileArgsBuilder().
+ List("l2"),
+ err: os.ErrNotExist,
+ },
+ {
+ name: "error incorrect relative root",
+ args: fileArgsBuilder().
+ SourcePrefixToStrip("b").
+ File("a/a/a"),
+ err: IncorrectRelativeRootError{},
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ if test.args.Error() != nil {
+ t.Fatal(test.args.Error())
+ }
+
+ args := ZipArgs{}
+ args.FileArgs = test.args.FileArgs()
+ args.CompressionLevel = test.compressionLevel
+ args.EmulateJar = test.emulateJar
+ args.AddDirectoryEntriesToZip = test.dirEntries
+ args.NonDeflatedFiles = test.nonDeflatedFiles
+ args.ManifestSourcePath = test.manifest
+ args.StoreSymlinks = test.storeSymlinks
+ args.IgnoreMissingFiles = test.ignoreMissingFiles
+ args.Filesystem = mockFs
+ args.Stderr = &bytes.Buffer{}
+
+ buf := &bytes.Buffer{}
+ err := ZipTo(args, buf)
+
+ if (err != nil) != (test.err != nil) {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ } else if test.err != nil {
+ if os.IsNotExist(test.err) {
+ if !os.IsNotExist(test.err) {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
+ } else if _, wantRelativeRootErr := test.err.(IncorrectRelativeRootError); wantRelativeRootErr {
+ if _, gotRelativeRootErr := err.(IncorrectRelativeRootError); !gotRelativeRootErr {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
+ } else {
+ t.Fatalf("want error %v, got %v", test.err, err)
+ }
+ return
+ }
+
+ br := bytes.NewReader(buf.Bytes())
+ zr, err := zip.NewReader(br, int64(br.Len()))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var files []zip.FileHeader
+ for _, f := range zr.File {
+ r, err := f.Open()
+ if err != nil {
+ t.Fatalf("error when opening %s: %s", f.Name, err)
+ }
+
+ crc := crc32.NewIEEE()
+ len, err := io.Copy(crc, r)
+ r.Close()
+ if err != nil {
+ t.Fatalf("error when reading %s: %s", f.Name, err)
+ }
+
+ if uint64(len) != f.UncompressedSize64 {
+ t.Errorf("incorrect length for %s, want %d got %d", f.Name, f.UncompressedSize64, len)
+ }
+
+ if crc.Sum32() != f.CRC32 {
+ t.Errorf("incorrect crc for %s, want %x got %x", f.Name, f.CRC32, crc)
+ }
+
+ files = append(files, f.FileHeader)
+ }
+
+ if len(files) != len(test.files) {
+ t.Fatalf("want %d files, got %d", len(test.files), len(files))
+ }
+
+ for i := range files {
+ want := test.files[i]
+ got := files[i]
+
+ if want.Name != got.Name {
+ t.Errorf("incorrect file %d want %q got %q", i, want.Name, got.Name)
+ continue
+ }
+
+ if want.UncompressedSize64 != got.UncompressedSize64 {
+ t.Errorf("incorrect file %s length want %v got %v", want.Name,
+ want.UncompressedSize64, got.UncompressedSize64)
+ }
+
+ if want.ExternalAttrs != got.ExternalAttrs {
+ t.Errorf("incorrect file %s attrs want %x got %x", want.Name,
+ want.ExternalAttrs, got.ExternalAttrs)
+ }
+
+ if want.CRC32 != got.CRC32 {
+ t.Errorf("incorrect file %s crc want %v got %v", want.Name,
+ want.CRC32, got.CRC32)
+ }
+
+ if want.Method != got.Method {
+ t.Errorf("incorrect file %s method want %v got %v", want.Name,
+ want.Method, got.Method)
+ }
+ }
+ })
+ }
+}
+
func TestReadRespFile(t *testing.T) {
testCases := []struct {
name, in string