• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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 zipWriter struct {
73	time        time.Time
74	createdDirs map[string]bool
75	directories bool
76
77	w *zip.Writer
78}
79
80func main() {
81	flag.Parse()
82
83	if *out == "" {
84		fmt.Fprintf(os.Stderr, "error: -o is required\n")
85		usage()
86	}
87
88	w := &zipWriter{
89		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
95	err := w.write(*out, listFiles, *manifest)
96	if err != nil {
97		fmt.Fprintln(os.Stderr, err.Error())
98		os.Exit(1)
99	}
100}
101
102func (z *zipWriter) write(out string, listFiles fileArgs, manifest string) error {
103	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
115	z.w = zip.NewWriter(f)
116	defer z.w.Close()
117
118	for _, listFile := range listFiles {
119		err = z.writeListFile(listFile)
120		if err != nil {
121			return err
122		}
123	}
124
125	for _, file := range files {
126		err = z.writeRelFile(file.relativeRoot, file.file)
127		if err != nil {
128			return err
129		}
130	}
131
132	if manifest != "" {
133		err = z.writeFile("META-INF/MANIFEST.MF", manifest)
134		if err != nil {
135			return err
136		}
137	}
138
139	return nil
140}
141
142func (z *zipWriter) writeListFile(listFile fileArg) error {
143	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		}
155		err = z.writeRelFile(listFile.relativeRoot, file)
156		if err != nil {
157			return err
158		}
159	}
160
161	return nil
162}
163
164func (z *zipWriter) writeRelFile(root, file string) error {
165	rel, err := filepath.Rel(root, file)
166	if err != nil {
167		return err
168	}
169
170	err = z.writeFile(rel, file)
171	if err != nil {
172		return err
173	}
174
175	return nil
176}
177
178func (z *zipWriter) writeFile(rel, file string) error {
179	if s, _ := os.Stat(file); s.IsDir() {
180		if z.directories {
181			return z.writeDirectory(file)
182		}
183		return nil
184	}
185
186	if z.directories {
187		dir, _ := filepath.Split(rel)
188		err := z.writeDirectory(dir)
189		if err != nil {
190			return err
191		}
192	}
193
194	fileHeader := &zip.FileHeader{
195		Name:   rel,
196		Method: zip.Deflate,
197	}
198	fileHeader.SetModTime(z.time)
199
200	out, err := z.w.CreateHeader(fileHeader)
201	if err != nil {
202		return err
203	}
204
205	in, err := os.Open(file)
206	if err != nil {
207		return err
208	}
209	defer in.Close()
210
211	_, err = io.Copy(out, in)
212	if err != nil {
213		return err
214	}
215
216	return nil
217}
218
219func (z *zipWriter) writeDirectory(dir string) error {
220	for dir != "" && !z.createdDirs[dir] {
221		z.createdDirs[dir] = true
222
223		dirHeader := &zip.FileHeader{
224			Name: dir,
225		}
226		dirHeader.SetMode(os.ModeDir)
227		dirHeader.SetModTime(z.time)
228
229		_, err := z.w.CreateHeader(dirHeader)
230		if err != nil {
231			return err
232		}
233
234		dir, _ = filepath.Split(dir)
235	}
236
237	return nil
238}
239