blob: 6461f8c711dd2ef3ca847d86c61ec38b44835735 [file] [log] [blame]
Colin Cross2fe66872015-03-30 17:20:39 -07001// Copyright 2015 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 "archive/zip"
19 "flag"
20 "fmt"
21 "io"
22 "io/ioutil"
23 "os"
24 "path/filepath"
25 "strings"
26 "time"
27)
28
29type fileArg struct {
30 relativeRoot, file string
31}
32
33type fileArgs []fileArg
34
35func (l *fileArgs) String() string {
36 return `""`
37}
38
39func (l *fileArgs) Set(s string) error {
40 if *relativeRoot == "" {
41 return fmt.Errorf("must pass -C before -f")
42 }
43
Dan Willemsena59a3bc2016-08-03 17:47:23 -070044 *l = append(*l, fileArg{filepath.Clean(*relativeRoot), s})
Colin Cross2fe66872015-03-30 17:20:39 -070045 return nil
46}
47
48func (l *fileArgs) Get() interface{} {
49 return l
50}
51
52var (
53 out = flag.String("o", "", "file to write jar file to")
54 manifest = flag.String("m", "", "input manifest file name")
55 directories = flag.Bool("d", false, "include directories in jar")
56 relativeRoot = flag.String("C", "", "path to use as relative root of files in next -f or -l argument")
57 listFiles fileArgs
58 files fileArgs
59)
60
61func init() {
62 flag.Var(&listFiles, "l", "file containing list of .class files")
63 flag.Var(&files, "f", "file to include in jar")
64}
65
66func usage() {
67 fmt.Fprintf(os.Stderr, "usage: soong_jar -o jarfile [-m manifest] -C dir [-f|-l file]...\n")
68 flag.PrintDefaults()
69 os.Exit(2)
70}
71
Colin Crosse19c7932015-04-24 15:08:38 -070072type zipWriter struct {
Colin Cross2fe66872015-03-30 17:20:39 -070073 time time.Time
74 createdDirs map[string]bool
75 directories bool
Colin Crosse19c7932015-04-24 15:08:38 -070076
77 w *zip.Writer
Colin Cross2fe66872015-03-30 17:20:39 -070078}
79
80func main() {
81 flag.Parse()
82
83 if *out == "" {
84 fmt.Fprintf(os.Stderr, "error: -o is required\n")
85 usage()
86 }
87
Colin Crosse19c7932015-04-24 15:08:38 -070088 w := &zipWriter{
Colin Cross2fe66872015-03-30 17:20:39 -070089 time: time.Now(),
90 createdDirs: make(map[string]bool),
91 directories: *directories,
92 }
93
94 // TODO: Go's zip implementation doesn't support increasing the compression level yet
Colin Crosse19c7932015-04-24 15:08:38 -070095 err := w.write(*out, listFiles, *manifest)
Colin Cross2fe66872015-03-30 17:20:39 -070096 if err != nil {
97 fmt.Fprintln(os.Stderr, err.Error())
98 os.Exit(1)
99 }
100}
101
Colin Crosse19c7932015-04-24 15:08:38 -0700102func (z *zipWriter) write(out string, listFiles fileArgs, manifest string) error {
Colin Cross2fe66872015-03-30 17:20:39 -0700103 f, err := os.Create(out)
104 if err != nil {
105 return err
106 }
107
108 defer f.Close()
109 defer func() {
110 if err != nil {
111 os.Remove(out)
112 }
113 }()
114
Colin Crosse19c7932015-04-24 15:08:38 -0700115 z.w = zip.NewWriter(f)
116 defer z.w.Close()
Colin Cross2fe66872015-03-30 17:20:39 -0700117
118 for _, listFile := range listFiles {
Colin Crosse19c7932015-04-24 15:08:38 -0700119 err = z.writeListFile(listFile)
Colin Cross2fe66872015-03-30 17:20:39 -0700120 if err != nil {
121 return err
122 }
123 }
124
125 for _, file := range files {
Colin Crosse19c7932015-04-24 15:08:38 -0700126 err = z.writeRelFile(file.relativeRoot, file.file)
Colin Cross2fe66872015-03-30 17:20:39 -0700127 if err != nil {
128 return err
129 }
130 }
131
132 if manifest != "" {
Colin Crosse19c7932015-04-24 15:08:38 -0700133 err = z.writeFile("META-INF/MANIFEST.MF", manifest)
Colin Cross2fe66872015-03-30 17:20:39 -0700134 if err != nil {
135 return err
136 }
137 }
138
139 return nil
140}
141
Colin Crosse19c7932015-04-24 15:08:38 -0700142func (z *zipWriter) writeListFile(listFile fileArg) error {
Colin Cross2fe66872015-03-30 17:20:39 -0700143 list, err := ioutil.ReadFile(listFile.file)
144 if err != nil {
145 return err
146 }
147
148 files := strings.Split(string(list), "\n")
149
150 for _, file := range files {
151 file = strings.TrimSpace(file)
152 if file == "" {
153 continue
154 }
Colin Crosse19c7932015-04-24 15:08:38 -0700155 err = z.writeRelFile(listFile.relativeRoot, file)
Colin Cross2fe66872015-03-30 17:20:39 -0700156 if err != nil {
157 return err
158 }
159 }
160
161 return nil
162}
163
Colin Crosse19c7932015-04-24 15:08:38 -0700164func (z *zipWriter) writeRelFile(root, file string) error {
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700165 file = filepath.Clean(file)
166
Colin Cross2fe66872015-03-30 17:20:39 -0700167 rel, err := filepath.Rel(root, file)
168 if err != nil {
169 return err
170 }
171
Colin Crosse19c7932015-04-24 15:08:38 -0700172 err = z.writeFile(rel, file)
Colin Cross2fe66872015-03-30 17:20:39 -0700173 if err != nil {
174 return err
175 }
176
177 return nil
178}
179
Colin Crosse19c7932015-04-24 15:08:38 -0700180func (z *zipWriter) writeFile(rel, file string) error {
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700181 if s, err := os.Lstat(file); err != nil {
182 return err
183 } else if s.IsDir() {
Colin Cross957cc4e2015-04-24 15:10:32 -0700184 if z.directories {
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700185 return z.writeDirectory(rel)
Colin Cross957cc4e2015-04-24 15:10:32 -0700186 }
187 return nil
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700188 } else if s.Mode()&os.ModeSymlink != 0 {
189 return z.writeSymlink(rel, file)
190 } else if !s.Mode().IsRegular() {
191 return fmt.Errorf("%s is not a file, directory, or symlink", file)
Colin Cross957cc4e2015-04-24 15:10:32 -0700192 }
193
Colin Crosse19c7932015-04-24 15:08:38 -0700194 if z.directories {
Colin Cross2fe66872015-03-30 17:20:39 -0700195 dir, _ := filepath.Split(rel)
Colin Crosse19c7932015-04-24 15:08:38 -0700196 err := z.writeDirectory(dir)
197 if err != nil {
198 return err
Colin Cross2fe66872015-03-30 17:20:39 -0700199 }
200 }
201
202 fileHeader := &zip.FileHeader{
203 Name: rel,
204 Method: zip.Deflate,
205 }
Colin Crosse19c7932015-04-24 15:08:38 -0700206 fileHeader.SetModTime(z.time)
Colin Cross2fe66872015-03-30 17:20:39 -0700207
Colin Crosse19c7932015-04-24 15:08:38 -0700208 out, err := z.w.CreateHeader(fileHeader)
Colin Cross2fe66872015-03-30 17:20:39 -0700209 if err != nil {
210 return err
211 }
212
213 in, err := os.Open(file)
214 if err != nil {
215 return err
216 }
217 defer in.Close()
218
219 _, err = io.Copy(out, in)
220 if err != nil {
221 return err
222 }
223
224 return nil
225}
Colin Crosse19c7932015-04-24 15:08:38 -0700226
227func (z *zipWriter) writeDirectory(dir string) error {
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700228 if dir != "" && !strings.HasSuffix(dir, "/") {
229 dir = dir + "/"
230 }
231
232 for dir != "" && dir != "./" && !z.createdDirs[dir] {
Colin Crosse19c7932015-04-24 15:08:38 -0700233 z.createdDirs[dir] = true
234
235 dirHeader := &zip.FileHeader{
236 Name: dir,
237 }
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700238 dirHeader.SetMode(0700 | os.ModeDir)
Colin Crosse19c7932015-04-24 15:08:38 -0700239 dirHeader.SetModTime(z.time)
240
241 _, err := z.w.CreateHeader(dirHeader)
242 if err != nil {
243 return err
244 }
245
246 dir, _ = filepath.Split(dir)
247 }
248
249 return nil
250}
Dan Willemsena59a3bc2016-08-03 17:47:23 -0700251
252func (z *zipWriter) writeSymlink(rel, file string) error {
253 if z.directories {
254 dir, _ := filepath.Split(rel)
255 if err := z.writeDirectory(dir); err != nil {
256 return err
257 }
258 }
259
260 fileHeader := &zip.FileHeader{
261 Name: rel,
262 }
263 fileHeader.SetModTime(z.time)
264 fileHeader.SetMode(0700 | os.ModeSymlink)
265
266 out, err := z.w.CreateHeader(fileHeader)
267 if err != nil {
268 return err
269 }
270
271 dest, err := os.Readlink(file)
272 if err != nil {
273 return err
274 }
275
276 _, err = io.WriteString(out, dest)
277 return err
278}