// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "archive/zip" "flag" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "time" ) type fileArg struct { relativeRoot, file string } type fileArgs []fileArg func (l *fileArgs) String() string { return `""` } func (l *fileArgs) Set(s string) error { if *relativeRoot == "" { return fmt.Errorf("must pass -C before -f") } *l = append(*l, fileArg{*relativeRoot, s}) return nil } func (l *fileArgs) Get() interface{} { return l } var ( out = flag.String("o", "", "file to write jar file to") manifest = flag.String("m", "", "input manifest file name") directories = flag.Bool("d", false, "include directories in jar") relativeRoot = flag.String("C", "", "path to use as relative root of files in next -f or -l argument") listFiles fileArgs files fileArgs ) func init() { flag.Var(&listFiles, "l", "file containing list of .class files") flag.Var(&files, "f", "file to include in jar") } func usage() { fmt.Fprintf(os.Stderr, "usage: soong_jar -o jarfile [-m manifest] -C dir [-f|-l file]...\n") flag.PrintDefaults() os.Exit(2) } type zipWriter struct { time time.Time createdDirs map[string]bool directories bool w *zip.Writer } func main() { flag.Parse() if *out == "" { fmt.Fprintf(os.Stderr, "error: -o is required\n") usage() } w := &zipWriter{ time: time.Now(), createdDirs: make(map[string]bool), directories: *directories, } // TODO: Go's zip implementation doesn't support increasing the compression level yet err := w.write(*out, listFiles, *manifest) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } } func (z *zipWriter) write(out string, listFiles fileArgs, manifest string) error { f, err := os.Create(out) if err != nil { return err } defer f.Close() defer func() { if err != nil { os.Remove(out) } }() z.w = zip.NewWriter(f) defer z.w.Close() for _, listFile := range listFiles { err = z.writeListFile(listFile) if err != nil { return err } } for _, file := range files { err = z.writeRelFile(file.relativeRoot, file.file) if err != nil { return err } } if manifest != "" { err = z.writeFile("META-INF/MANIFEST.MF", manifest) if err != nil { return err } } return nil } func (z *zipWriter) writeListFile(listFile fileArg) error { list, err := ioutil.ReadFile(listFile.file) if err != nil { return err } files := strings.Split(string(list), "\n") for _, file := range files { file = strings.TrimSpace(file) if file == "" { continue } err = z.writeRelFile(listFile.relativeRoot, file) if err != nil { return err } } return nil } func (z *zipWriter) writeRelFile(root, file string) error { rel, err := filepath.Rel(root, file) if err != nil { return err } err = z.writeFile(rel, file) if err != nil { return err } return nil } func (z *zipWriter) writeFile(rel, file string) error { if s, _ := os.Stat(file); s.IsDir() { if z.directories { return z.writeDirectory(file) } return nil } if z.directories { dir, _ := filepath.Split(rel) err := z.writeDirectory(dir) if err != nil { return err } } fileHeader := &zip.FileHeader{ Name: rel, Method: zip.Deflate, } fileHeader.SetModTime(z.time) out, err := z.w.CreateHeader(fileHeader) if err != nil { return err } in, err := os.Open(file) if err != nil { return err } defer in.Close() _, err = io.Copy(out, in) if err != nil { return err } return nil } func (z *zipWriter) writeDirectory(dir string) error { for dir != "" && !z.createdDirs[dir] { z.createdDirs[dir] = true dirHeader := &zip.FileHeader{ Name: dir, } dirHeader.SetMode(os.ModeDir) dirHeader.SetModTime(z.time) _, err := z.w.CreateHeader(dirHeader) if err != nil { return err } dir, _ = filepath.Split(dir) } return nil }