Merge changes I5059a1e3,I5a06a893

* changes:
  Add --write_if_changed argument to soong_zip
  Add searchable android.DirectorySortedPaths
This commit is contained in:
Colin Cross 2017-11-14 23:44:25 +00:00 committed by Gerrit Code Review
commit fff8ad2f35
5 changed files with 155 additions and 25 deletions

View file

@ -18,6 +18,7 @@ import (
"fmt"
"path/filepath"
"reflect"
"sort"
"strings"
"github.com/google/blueprint"
@ -380,6 +381,38 @@ func (p Paths) FilterOutByExt(ext string) Paths {
return ret
}
// DirectorySortedPaths is a slice of paths that are sorted such that all files in a directory
// (including subdirectories) are in a contiguous subslice of the list, and can be found in
// O(log(N)) time using a binary search on the directory prefix.
type DirectorySortedPaths Paths
func PathsToDirectorySortedPaths(paths Paths) DirectorySortedPaths {
ret := append(DirectorySortedPaths(nil), paths...)
sort.Slice(ret, func(i, j int) bool {
return ret[i].String() < ret[j].String()
})
return ret
}
// PathsInDirectory returns a subslice of the DirectorySortedPaths as a Paths that contains all entries
// that are in the specified directory and its subdirectories.
func (p DirectorySortedPaths) PathsInDirectory(dir string) Paths {
prefix := filepath.Clean(dir) + "/"
start := sort.Search(len(p), func(i int) bool {
return prefix < p[i].String()
})
ret := p[start:]
end := sort.Search(len(ret), func(i int) bool {
return !strings.HasPrefix(ret[i].String(), prefix)
})
ret = ret[:end]
return Paths(ret)
}
// WritablePaths is a slice of WritablePaths, used for multiple outputs.
type WritablePaths []WritablePath

View file

@ -340,3 +340,75 @@ func TestPathForModuleInstall(t *testing.T) {
})
}
}
func TestDirectorySortedPaths(t *testing.T) {
makePaths := func() Paths {
return Paths{
PathForTesting("a.txt"),
PathForTesting("a/txt"),
PathForTesting("a/b/c"),
PathForTesting("a/b/d"),
PathForTesting("b"),
PathForTesting("b/b.txt"),
PathForTesting("a/a.txt"),
}
}
expected := []string{
"a.txt",
"a/a.txt",
"a/b/c",
"a/b/d",
"a/txt",
"b",
"b/b.txt",
}
paths := makePaths()
reversePaths := make(Paths, len(paths))
for i, v := range paths {
reversePaths[len(paths)-i-1] = v
}
sortedPaths := PathsToDirectorySortedPaths(paths)
reverseSortedPaths := PathsToDirectorySortedPaths(reversePaths)
if !reflect.DeepEqual(Paths(sortedPaths).Strings(), expected) {
t.Fatalf("sorted paths:\n %#v\n != \n %#v", paths.Strings(), expected)
}
if !reflect.DeepEqual(Paths(reverseSortedPaths).Strings(), expected) {
t.Fatalf("sorted reversed paths:\n %#v\n !=\n %#v", reversePaths.Strings(), expected)
}
expectedA := []string{
"a/a.txt",
"a/b/c",
"a/b/d",
"a/txt",
}
inA := sortedPaths.PathsInDirectory("a")
if !reflect.DeepEqual(inA.Strings(), expectedA) {
t.Errorf("FilesInDirectory(a):\n %#v\n != \n %#v", inA.Strings(), expectedA)
}
expectedA_B := []string{
"a/b/c",
"a/b/d",
}
inA_B := sortedPaths.PathsInDirectory("a/b")
if !reflect.DeepEqual(inA_B.Strings(), expectedA_B) {
t.Errorf("FilesInDirectory(a/b):\n %#v\n != \n %#v", inA_B.Strings(), expectedA_B)
}
expectedB := []string{
"b/b.txt",
}
inB := sortedPaths.PathsInDirectory("b")
if !reflect.DeepEqual(inB.Strings(), expectedB) {
t.Errorf("FilesInDirectory(b):\n %#v\n != \n %#v", inA.Strings(), expectedA)
}
}

View file

@ -19,6 +19,7 @@ bootstrap_go_package {
pkgPath: "android/soong/zip",
deps: [
"android-archive-zip",
"blueprint-pathtools",
"soong-jar",
],
srcs: [

View file

@ -120,14 +120,15 @@ func (d *dir) Set(s string) error {
}
var (
out = flag.String("o", "", "file to write zip file to")
manifest = flag.String("m", "", "input jar manifest file name")
directories = flag.Bool("d", false, "include directories in zip")
rootPrefix = flag.String("P", "", "path prefix within the zip at which to place files")
relativeRoot = flag.String("C", "", "path to use as relative root of files in following -f, -l, or -D arguments")
parallelJobs = flag.Int("j", runtime.NumCPU(), "number of parallel threads to use")
compLevel = flag.Int("L", 5, "deflate compression level (0-9)")
emulateJar = flag.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
out = flag.String("o", "", "file to write zip file to")
manifest = flag.String("m", "", "input jar manifest file name")
directories = flag.Bool("d", false, "include directories in zip")
rootPrefix = flag.String("P", "", "path prefix within the zip at which to place files")
relativeRoot = flag.String("C", "", "path to use as relative root of files in following -f, -l, or -D arguments")
parallelJobs = flag.Int("j", runtime.NumCPU(), "number of parallel threads to use")
compLevel = flag.Int("L", 5, "deflate compression level (0-9)")
emulateJar = flag.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
writeIfChanged = flag.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
fArgs zip.FileArgs
nonDeflatedFiles = make(uniqueSet)
@ -163,6 +164,7 @@ func main() {
ManifestSourcePath: *manifest,
NumParallelJobs: *parallelJobs,
NonDeflatedFiles: nonDeflatedFiles,
WriteIfChanged: *writeIfChanged,
})
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())

View file

@ -32,6 +32,8 @@ import (
"sync"
"time"
"github.com/google/blueprint/pathtools"
"android/soong/jar"
"android/soong/third_party/zip"
)
@ -127,6 +129,7 @@ type ZipArgs struct {
ManifestSourcePath string
NumParallelJobs int
NonDeflatedFiles map[string]bool
WriteIfChanged bool
}
func Run(args ZipArgs) (err error) {
@ -186,8 +189,38 @@ func Run(args ZipArgs) (err error) {
}
}
return w.write(args.OutputFilePath, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
buf := &bytes.Buffer{}
var out io.Writer = buf
if !args.WriteIfChanged {
f, err := os.Create(args.OutputFilePath)
if err != nil {
return err
}
defer f.Close()
defer func() {
if err != nil {
os.Remove(args.OutputFilePath)
}
}()
out = f
}
err = w.write(out, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.NumParallelJobs)
if err != nil {
return err
}
if args.WriteIfChanged {
err := pathtools.WriteFileIfChanged(args.OutputFilePath, buf.Bytes(), 0666)
if err != nil {
return err
}
}
return nil
}
func fillPathPairs(prefix, rel, src string, pathMappings *[]pathMapping, nonDeflatedFiles map[string]bool) error {
@ -226,19 +259,7 @@ type readerSeekerCloser interface {
io.Seeker
}
func (z *ZipWriter) write(out string, pathMappings []pathMapping, manifest string, emulateJar bool, parallelJobs int) error {
f, err := os.Create(out)
if err != nil {
return err
}
defer f.Close()
defer func() {
if err != nil {
os.Remove(out)
}
}()
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)
@ -324,6 +345,7 @@ func (z *ZipWriter) write(out string, pathMappings []pathMapping, manifest strin
case op := <-writeOpChan:
currentWriteOpChan = nil
var err error
if op.fh.Method == zip.Deflate {
currentWriter, err = zipw.CreateCompressedHeader(op.fh)
} else {
@ -356,21 +378,21 @@ func (z *ZipWriter) write(out string, pathMappings []pathMapping, manifest strin
currentReader = futureReader
case reader := <-currentReader:
_, err = io.Copy(currentWriter, reader)
_, err := io.Copy(currentWriter, reader)
if err != nil {
return err
}
currentReader = nil
case err = <-z.errors:
case err := <-z.errors:
return err
}
}
// One last chance to catch an error
select {
case err = <-z.errors:
case err := <-z.errors:
return err
default:
zipw.Close()