blob: f48d458b8d3f01b7e8e71309c999541a801fbbb9 [file] [log] [blame]
Dan Willemsen3bf1a082016-08-03 00:35:25 -07001// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "flag"
19 "fmt"
Dan Willemsen82218f22017-06-19 16:35:00 -070020 "log"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070021 "os"
22 "path/filepath"
Dan Willemsen82218f22017-06-19 16:35:00 -070023 "sort"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070024 "strings"
Dan Willemsen82218f22017-06-19 16:35:00 -070025 "time"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070026
Jeff Gaston01547b22017-08-21 20:13:28 -070027 "android/soong/jar"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070028 "android/soong/third_party/zip"
29)
30
31var (
Dan Willemsen82218f22017-06-19 16:35:00 -070032 input = flag.String("i", "", "zip file to read from")
33 output = flag.String("o", "", "output file")
34 sortGlobs = flag.Bool("s", false, "sort matches from each glob (defaults to the order from the input zip file)")
Colin Cross8936b022017-06-23 13:00:17 -070035 sortJava = flag.Bool("j", false, "sort using jar ordering within each glob (META-INF/MANIFEST.MF first)")
Dan Willemsen82218f22017-06-19 16:35:00 -070036 setTime = flag.Bool("t", false, "set timestamps to 2009-01-01 00:00:00")
37
38 staticTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070039)
40
Dan Willemsen3bf1a082016-08-03 00:35:25 -070041func main() {
Dan Willemsen82218f22017-06-19 16:35:00 -070042 flag.Usage = func() {
Colin Cross8936b022017-06-23 13:00:17 -070043 fmt.Fprintln(os.Stderr, "usage: zip2zip -i zipfile -o zipfile [-s|-j] [-t] [filespec]...")
Dan Willemsen82218f22017-06-19 16:35:00 -070044 flag.PrintDefaults()
45 fmt.Fprintln(os.Stderr, " filespec:")
46 fmt.Fprintln(os.Stderr, " <name>")
47 fmt.Fprintln(os.Stderr, " <in_name>:<out_name>")
48 fmt.Fprintln(os.Stderr, " <glob>:<out_dir>/")
49 fmt.Fprintln(os.Stderr, "")
50 fmt.Fprintln(os.Stderr, "<glob> uses the rules at https://golang.org/pkg/path/filepath/#Match")
Colin Cross06382992017-06-23 14:08:42 -070051 fmt.Fprintln(os.Stderr, "As a special exception, '**' is supported to specify all files in the input zip.")
Dan Willemsen82218f22017-06-19 16:35:00 -070052 fmt.Fprintln(os.Stderr, "")
53 fmt.Fprintln(os.Stderr, "Files will be copied with their existing compression from the input zipfile to")
Colin Cross06382992017-06-23 14:08:42 -070054 fmt.Fprintln(os.Stderr, "the output zipfile, in the order of filespec arguments.")
55 fmt.Fprintln(os.Stderr, "")
56 fmt.Fprintln(os.Stderr, "If no filepsec is provided all files are copied (equivalent to '**').")
Dan Willemsen82218f22017-06-19 16:35:00 -070057 }
58
Dan Willemsen3bf1a082016-08-03 00:35:25 -070059 flag.Parse()
60
Colin Cross06382992017-06-23 14:08:42 -070061 if *input == "" || *output == "" {
Dan Willemsen82218f22017-06-19 16:35:00 -070062 flag.Usage()
63 os.Exit(1)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070064 }
65
Dan Willemsen82218f22017-06-19 16:35:00 -070066 log.SetFlags(log.Lshortfile)
67
Dan Willemsen3bf1a082016-08-03 00:35:25 -070068 reader, err := zip.OpenReader(*input)
69 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070070 log.Fatal(err)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070071 }
72 defer reader.Close()
73
74 output, err := os.Create(*output)
75 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070076 log.Fatal(err)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070077 }
78 defer output.Close()
79
80 writer := zip.NewWriter(output)
81 defer func() {
82 err := writer.Close()
83 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070084 log.Fatal(err)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070085 }
86 }()
87
Colin Cross8936b022017-06-23 13:00:17 -070088 if err := zip2zip(&reader.Reader, writer, *sortGlobs, *sortJava, *setTime, flag.Args()); err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070089 log.Fatal(err)
90 }
91}
92
Colin Cross8936b022017-06-23 13:00:17 -070093type pair struct {
94 *zip.File
95 newName string
96}
97
98func zip2zip(reader *zip.Reader, writer *zip.Writer, sortGlobs, sortJava, setTime bool, args []string) error {
Colin Cross06382992017-06-23 14:08:42 -070099 if len(args) == 0 {
100 // If no filespec is provided, default to copying everything
101 args = []string{"**"}
102 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700103 for _, arg := range args {
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700104 var input string
105 var output string
106
107 // Reserve escaping for future implementation, so make sure no
108 // one is using \ and expecting a certain behavior.
109 if strings.Contains(arg, "\\") {
Dan Willemsen82218f22017-06-19 16:35:00 -0700110 return fmt.Errorf("\\ characters are not currently supported")
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700111 }
112
113 args := strings.SplitN(arg, ":", 2)
114 input = args[0]
115 if len(args) == 2 {
116 output = args[1]
117 }
118
Dan Willemsen82218f22017-06-19 16:35:00 -0700119 matches := []pair{}
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700120 if strings.IndexAny(input, "*?[") >= 0 {
Dan Willemsen82218f22017-06-19 16:35:00 -0700121 matchAll := input == "**"
122 if !matchAll && strings.Contains(input, "**") {
123 return fmt.Errorf("** is only supported on its own, not with other characters")
124 }
125
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700126 for _, file := range reader.File {
Dan Willemsen82218f22017-06-19 16:35:00 -0700127 match := matchAll
128
129 if !match {
130 var err error
131 match, err = filepath.Match(input, file.Name)
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700132 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -0700133 return err
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700134 }
135 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700136
137 if match {
138 var newName string
139 if output == "" {
140 newName = file.Name
141 } else {
142 _, name := filepath.Split(file.Name)
143 newName = filepath.Join(output, name)
144 }
145 matches = append(matches, pair{file, newName})
146 }
147 }
148
Colin Cross8936b022017-06-23 13:00:17 -0700149 if sortJava {
150 jarSort(matches)
151 } else if sortGlobs {
Dan Willemsen82218f22017-06-19 16:35:00 -0700152 sort.SliceStable(matches, func(i, j int) bool {
153 return matches[i].newName < matches[j].newName
154 })
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700155 }
156 } else {
157 if output == "" {
158 output = input
159 }
160 for _, file := range reader.File {
161 if input == file.Name {
Dan Willemsen82218f22017-06-19 16:35:00 -0700162 matches = append(matches, pair{file, output})
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700163 break
164 }
165 }
166 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700167
168 for _, match := range matches {
169 if setTime {
170 match.File.SetModTime(staticTime)
171 }
172 if err := writer.CopyFrom(match.File, match.newName); err != nil {
173 return err
174 }
175 }
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700176 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700177
178 return nil
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700179}
Colin Cross8936b022017-06-23 13:00:17 -0700180
181func jarSort(files []pair) {
Colin Cross8936b022017-06-23 13:00:17 -0700182 sort.SliceStable(files, func(i, j int) bool {
Jeff Gaston01547b22017-08-21 20:13:28 -0700183 return jar.EntryNamesLess(files[i].newName, files[j].newName)
Colin Cross8936b022017-06-23 13:00:17 -0700184 })
185}