mirror of
https://github.com/golang/go
synced 2024-11-18 11:44:45 -07:00
bbe5c93e93
On amd64, the real time is reduced from 176.76s to 140.26s. On ARM, the real time is reduced from 921.61s to 726.30s. LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/101580043
227 lines
4.9 KiB
Go
227 lines
4.9 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Mkzip creates a zip file from a 'proto' file describing the contents.
|
|
//
|
|
// The proto file is inspired by the Plan 9 mkfs prototype file format.
|
|
// It describes a file tree, one directory per line, with leading tab
|
|
// indentation marking the tree structure. Each line contains a leading
|
|
// name field giving the name of the file to copy into the zip file,
|
|
// and then a sequence of optional key=value attributes to control
|
|
// the copy. The only known attribute is src=foo, meaning copy the
|
|
// actual data for the file (or directory) from an alternate location.
|
|
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func usage() {
|
|
fmt.Fprintf(os.Stderr, "usage: mkzip [-r root] src.proto out.zip\n")
|
|
os.Exit(2)
|
|
}
|
|
|
|
func sysfatal(format string, args ...interface{}) {
|
|
fmt.Fprintf(os.Stderr, "mkzip: %s\n", fmt.Sprintf(format, args...))
|
|
os.Exit(2)
|
|
}
|
|
|
|
var (
|
|
root = flag.String("r", ".", "interpret source paths relative to this directory")
|
|
gopackage = flag.String("p", "", "write Go source file in this package")
|
|
)
|
|
|
|
type stack struct {
|
|
name string
|
|
src string
|
|
depth int
|
|
}
|
|
|
|
func main() {
|
|
log.SetFlags(0)
|
|
flag.Usage = usage
|
|
flag.Parse()
|
|
|
|
args := flag.Args()
|
|
if len(args) != 2 {
|
|
usage()
|
|
}
|
|
|
|
rf, err := os.Open(args[0])
|
|
if err != nil {
|
|
sysfatal("%v", err)
|
|
}
|
|
r := bufio.NewScanner(rf)
|
|
|
|
zf, err := os.Create(args[1])
|
|
if err != nil {
|
|
sysfatal("%v", err)
|
|
}
|
|
|
|
var w io.Writer = zf
|
|
if *gopackage != "" {
|
|
fmt.Fprintf(zf, `package %s
|
|
import "sync"
|
|
func init() {
|
|
var once sync.Once
|
|
fsinit = func() {
|
|
once.Do(func() {
|
|
unzip("`, *gopackage)
|
|
gw := &goWriter{b: bufio.NewWriter(w)}
|
|
defer func() {
|
|
if err := gw.Close(); err != nil {
|
|
sysfatal("finishing Go output: %v", err)
|
|
}
|
|
}()
|
|
w = gw
|
|
}
|
|
z := zip.NewWriter(w)
|
|
|
|
lineno := 0
|
|
|
|
addfile := func(info os.FileInfo, dst string, src string) {
|
|
zh, err := zip.FileInfoHeader(info)
|
|
if err != nil {
|
|
sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
|
|
}
|
|
zh.Name = dst
|
|
zh.Method = zip.Deflate
|
|
if info.IsDir() && !strings.HasSuffix(dst, "/") {
|
|
zh.Name += "/"
|
|
}
|
|
w, err := z.CreateHeader(zh)
|
|
if err != nil {
|
|
sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
|
|
}
|
|
if info.IsDir() {
|
|
return
|
|
}
|
|
r, err := os.Open(src)
|
|
if err != nil {
|
|
sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
|
|
}
|
|
defer r.Close()
|
|
if _, err := io.Copy(w, r); err != nil {
|
|
sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
|
|
}
|
|
}
|
|
|
|
var stk []stack
|
|
|
|
for r.Scan() {
|
|
line := r.Text()
|
|
lineno++
|
|
s := strings.TrimLeft(line, "\t")
|
|
prefix, line := line[:len(line)-len(s)], s
|
|
if i := strings.Index(line, "#"); i >= 0 {
|
|
line = line[:i]
|
|
}
|
|
f := strings.Fields(line)
|
|
if len(f) == 0 {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(line, " ") {
|
|
sysfatal("%s:%d: must use tabs for indentation", args[0], lineno)
|
|
}
|
|
depth := len(prefix)
|
|
for len(stk) > 0 && depth <= stk[len(stk)-1].depth {
|
|
stk = stk[:len(stk)-1]
|
|
}
|
|
parent := ""
|
|
psrc := *root
|
|
if len(stk) > 0 {
|
|
parent = stk[len(stk)-1].name
|
|
psrc = stk[len(stk)-1].src
|
|
}
|
|
if strings.Contains(f[0], "/") {
|
|
sysfatal("%s:%d: destination name cannot contain slash", args[0], lineno)
|
|
}
|
|
name := path.Join(parent, f[0])
|
|
src := filepath.Join(psrc, f[0])
|
|
for _, attr := range f[1:] {
|
|
i := strings.Index(attr, "=")
|
|
if i < 0 {
|
|
sysfatal("%s:%d: malformed attribute %q", args[0], lineno, attr)
|
|
}
|
|
key, val := attr[:i], attr[i+1:]
|
|
switch key {
|
|
case "src":
|
|
src = val
|
|
default:
|
|
sysfatal("%s:%d: unknown attribute %q", args[0], lineno, attr)
|
|
}
|
|
}
|
|
|
|
stk = append(stk, stack{name: name, src: src, depth: depth})
|
|
|
|
if f[0] == "*" || f[0] == "+" {
|
|
if f[0] == "*" {
|
|
dir, err := ioutil.ReadDir(psrc)
|
|
if err != nil {
|
|
sysfatal("%s:%d: %v", args[0], lineno, err)
|
|
}
|
|
for _, d := range dir {
|
|
addfile(d, path.Join(parent, d.Name()), filepath.Join(psrc, d.Name()))
|
|
}
|
|
} else {
|
|
err := filepath.Walk(psrc, func(src string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if src == psrc {
|
|
return nil
|
|
}
|
|
if psrc == "." {
|
|
psrc = ""
|
|
}
|
|
name := path.Join(parent, filepath.ToSlash(src[len(psrc):]))
|
|
addfile(info, name, src)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
sysfatal("%s:%d: %v", args[0], lineno, err)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
fi, err := os.Stat(src)
|
|
if err != nil {
|
|
sysfatal("%s:%d: %v", args[0], lineno, err)
|
|
}
|
|
addfile(fi, name, src)
|
|
}
|
|
|
|
if err := z.Close(); err != nil {
|
|
sysfatal("finishing zip file: %v", err)
|
|
}
|
|
}
|
|
|
|
type goWriter struct {
|
|
b *bufio.Writer
|
|
}
|
|
|
|
func (w *goWriter) Write(b []byte) (int, error) {
|
|
for _, c := range b {
|
|
fmt.Fprintf(w.b, "\\x%02x", c)
|
|
}
|
|
return len(b), nil
|
|
}
|
|
|
|
func (w *goWriter) Close() error {
|
|
fmt.Fprintf(w.b, "\")\n\t\t})\n\t}\n}")
|
|
w.b.Flush()
|
|
return nil
|
|
}
|