blob: 95c86c3d1416d8e7e28f28e104f2616342f8720d [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
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 {
Colin Cross2fe66872015-03-30 17:20:39 -0700165 rel, err := filepath.Rel(root, file)
166 if err != nil {
167 return err
168 }
169
Colin Crosse19c7932015-04-24 15:08:38 -0700170 err = z.writeFile(rel, file)
Colin Cross2fe66872015-03-30 17:20:39 -0700171 if err != nil {
172 return err
173 }
174
175 return nil
176}
177
Colin Crosse19c7932015-04-24 15:08:38 -0700178func (z *zipWriter) writeFile(rel, file string) error {
179 if z.directories {
Colin Cross2fe66872015-03-30 17:20:39 -0700180 dir, _ := filepath.Split(rel)
Colin Crosse19c7932015-04-24 15:08:38 -0700181 err := z.writeDirectory(dir)
182 if err != nil {
183 return err
Colin Cross2fe66872015-03-30 17:20:39 -0700184 }
185 }
186
187 fileHeader := &zip.FileHeader{
188 Name: rel,
189 Method: zip.Deflate,
190 }
Colin Crosse19c7932015-04-24 15:08:38 -0700191 fileHeader.SetModTime(z.time)
Colin Cross2fe66872015-03-30 17:20:39 -0700192
Colin Crosse19c7932015-04-24 15:08:38 -0700193 out, err := z.w.CreateHeader(fileHeader)
Colin Cross2fe66872015-03-30 17:20:39 -0700194 if err != nil {
195 return err
196 }
197
198 in, err := os.Open(file)
199 if err != nil {
200 return err
201 }
202 defer in.Close()
203
204 _, err = io.Copy(out, in)
205 if err != nil {
206 return err
207 }
208
209 return nil
210}
Colin Crosse19c7932015-04-24 15:08:38 -0700211
212func (z *zipWriter) writeDirectory(dir string) error {
213 for dir != "" && !z.createdDirs[dir] {
214 z.createdDirs[dir] = true
215
216 dirHeader := &zip.FileHeader{
217 Name: dir,
218 }
219 dirHeader.SetMode(os.ModeDir)
220 dirHeader.SetModTime(z.time)
221
222 _, err := z.w.CreateHeader(dirHeader)
223 if err != nil {
224 return err
225 }
226
227 dir, _ = filepath.Split(dir)
228 }
229
230 return nil
231}