blob: 231449692da43433b2cf481ba6b54c454272f611 [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
44 *l = append(*l, fileArg{*relativeRoot, s})
45 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
72type zipInfo struct {
73 time time.Time
74 createdDirs map[string]bool
75 directories bool
76}
77
78func main() {
79 flag.Parse()
80
81 if *out == "" {
82 fmt.Fprintf(os.Stderr, "error: -o is required\n")
83 usage()
84 }
85
86 info := zipInfo{
87 time: time.Now(),
88 createdDirs: make(map[string]bool),
89 directories: *directories,
90 }
91
92 // TODO: Go's zip implementation doesn't support increasing the compression level yet
93 err := writeZipFile(*out, listFiles, *manifest, info)
94 if err != nil {
95 fmt.Fprintln(os.Stderr, err.Error())
96 os.Exit(1)
97 }
98}
99
100func writeZipFile(out string, listFiles fileArgs, manifest string, info zipInfo) error {
101 f, err := os.Create(out)
102 if err != nil {
103 return err
104 }
105
106 defer f.Close()
107 defer func() {
108 if err != nil {
109 os.Remove(out)
110 }
111 }()
112
113 zipFile := zip.NewWriter(f)
114 defer zipFile.Close()
115
116 for _, listFile := range listFiles {
117 err = writeListFile(zipFile, listFile, info)
118 if err != nil {
119 return err
120 }
121 }
122
123 for _, file := range files {
124 err = writeRelFile(zipFile, file.relativeRoot, file.file, info)
125 if err != nil {
126 return err
127 }
128 }
129
130 if manifest != "" {
131 err = writeFile(zipFile, "META-INF/MANIFEST.MF", manifest, info)
132 if err != nil {
133 return err
134 }
135 }
136
137 return nil
138}
139
140func writeListFile(zipFile *zip.Writer, listFile fileArg, info zipInfo) error {
141 list, err := ioutil.ReadFile(listFile.file)
142 if err != nil {
143 return err
144 }
145
146 files := strings.Split(string(list), "\n")
147
148 for _, file := range files {
149 file = strings.TrimSpace(file)
150 if file == "" {
151 continue
152 }
153 err = writeRelFile(zipFile, listFile.relativeRoot, file, info)
154 if err != nil {
155 return err
156 }
157 }
158
159 return nil
160}
161
162func writeRelFile(zipFile *zip.Writer, root, file string, info zipInfo) error {
163 rel, err := filepath.Rel(root, file)
164 if err != nil {
165 return err
166 }
167
168 err = writeFile(zipFile, rel, file, info)
169 if err != nil {
170 return err
171 }
172
173 return nil
174}
175
176func writeFile(zipFile *zip.Writer, rel, file string, info zipInfo) error {
177 if info.directories {
178 dir, _ := filepath.Split(rel)
179 for dir != "" && !info.createdDirs[dir] {
180 info.createdDirs[dir] = true
181
182 dirHeader := &zip.FileHeader{
183 Name: dir,
184 }
185 dirHeader.SetMode(os.ModeDir)
186 dirHeader.SetModTime(info.time)
187
188 _, err := zipFile.CreateHeader(dirHeader)
189 if err != nil {
190 return err
191 }
192
193 dir, _ = filepath.Split(dir)
194 }
195 }
196
197 fileHeader := &zip.FileHeader{
198 Name: rel,
199 Method: zip.Deflate,
200 }
201 fileHeader.SetModTime(info.time)
202
203 out, err := zipFile.CreateHeader(fileHeader)
204 if err != nil {
205 return err
206 }
207
208 in, err := os.Open(file)
209 if err != nil {
210 return err
211 }
212 defer in.Close()
213
214 _, err = io.Copy(out, in)
215 if err != nil {
216 return err
217 }
218
219 return nil
220}