diff --git a/pack.go b/pack.go index 72b0599..3dfda34 100644 --- a/pack.go +++ b/pack.go @@ -41,18 +41,20 @@ var cmdPack = &Command{ Long: ` compress an beego project --p app path. default is current path --b build specify platform app. default true --ba additional args of go build --o compressed file output dir. default use current path --f format. [ tar.gz / zip ]. default tar.gz. note: zip doesn't support embed symlink, skip it --exp path exclude prefix. default: . --exs path exclude suffix. default: .go:.DS_Store:.tmp - all path use : as separator --fs follow symlink. default false --ss skip symlink. default false - default embed symlink into compressed file --v verbose +-p app path. default is current path +-b build specify platform app. default true +-ba additional args of go build +-be=[] additional ENV Variables of go build. eg: GOARCH=arm +-o compressed file output dir. default use current path +-f="" format. [ tar.gz / zip ]. default tar.gz. note: zip doesn't support embed symlink, skip it +-exp="" relpath exclude prefix. default: . +-exs="" relpath exclude suffix. default: .go:.DS_Store:.tmp + all path use : as separator +-exr=[] file/directory name exclude by Regexp. default: ^. +-fs=false follow symlink. default false +-ss=false skip symlink. default false + default embed symlink into compressed file +-v=false verbose `, } @@ -61,26 +63,41 @@ var ( excludeP string excludeS string outputP string + excludeR ListOpts fsym bool ssym bool build bool buildArgs string + buildEnvs ListOpts verbose bool format string ) +type ListOpts []string + +func (opts *ListOpts) String() string { + return fmt.Sprint(*opts) +} + +func (opts *ListOpts) Set(value string) error { + *opts = append(*opts, value) + return nil +} + func init() { fs := flag.NewFlagSet("pack", flag.ContinueOnError) - fs.StringVar(&appPath, "p", "", "") - fs.StringVar(&excludeP, "exp", ".", "") - fs.StringVar(&excludeS, "exs", ".go:.DS_Store:.tmp", "") - fs.StringVar(&outputP, "o", "", "") - fs.BoolVar(&build, "b", true, "") - fs.StringVar(&buildArgs, "ba", "", "") - fs.BoolVar(&fsym, "fs", false, "") - fs.BoolVar(&ssym, "ss", false, "") - fs.BoolVar(&verbose, "v", false, "") - fs.StringVar(&format, "f", "tar.gz", "") + fs.StringVar(&appPath, "p", "", "app path. default is current path") + fs.BoolVar(&build, "b", true, "build specify platform app") + fs.StringVar(&buildArgs, "ba", "", "additional args of go build") + fs.Var(&buildEnvs, "be", "additional ENV Variables of go build. eg: GOARCH=arm") + fs.StringVar(&outputP, "o", "", "compressed file output dir. default use current path") + fs.StringVar(&format, "f", "tar.gz", "format. [ tar.gz / zip ] note: zip doesn't support embed symlink, skip it") + fs.StringVar(&excludeP, "exp", ".", "path exclude prefix. use : as separator") + fs.StringVar(&excludeS, "exs", ".go:.DS_Store:.tmp", "path exclude suffix. use : as separator") + fs.Var(&excludeR, "exr", "filename exclude by Regexp") + fs.BoolVar(&fsym, "fs", false, "follow symlink") + fs.BoolVar(&ssym, "ss", false, "skip symlink") + fs.BoolVar(&verbose, "v", false, "verbose") cmdPack.Flag = *fs cmdPack.Run = packApp } @@ -109,6 +126,7 @@ type walkFileTree struct { wak walker prefix string excludePrefix []string + excludeRegexp []*regexp.Regexp excludeSuffix []string allfiles map[string]bool } @@ -117,32 +135,47 @@ func (wft *walkFileTree) setPrefix(prefix string) { wft.prefix = prefix } -func (wft *walkFileTree) isExclude(name string) bool { - if name == "" { +func (wft *walkFileTree) isExclude(fPath string) bool { + if fPath == "" { return true } + for _, prefix := range wft.excludePrefix { - if strings.HasPrefix(name, prefix) { + if strings.HasPrefix(fPath, prefix) { return true } } for _, suffix := range wft.excludeSuffix { - if strings.HasSuffix(name, suffix) { + if strings.HasSuffix(fPath, suffix) { return true } } return false } +func (wft *walkFileTree) isExcludeName(name string) bool { + for _, r := range wft.excludeRegexp { + if r.MatchString(name) { + return true + } + } + + return false +} + func (wft *walkFileTree) isEmpty(fpath string) bool { fh, _ := os.Open(fpath) defer fh.Close() infos, _ := fh.Readdir(-1) for _, fi := range infos { - fp := path.Join(fpath, fi.Name()) + fn := fi.Name() + fp := path.Join(fpath, fn) if wft.isExclude(wft.virPath(fp)) { continue } + if wft.isExcludeName(fn) { + continue + } if fi.Mode()&os.ModeSymlink > 0 { continue } @@ -195,15 +228,12 @@ func (wft *walkFileTree) walkLeaf(fpath string, fi os.FileInfo, err error) error return nil } - name := wft.virPath(fpath) - if wft.isExclude(name) { - return nil - } - if ssym && fi.Mode()&os.ModeSymlink > 0 { return nil } + name := wft.virPath(fpath) + if wft.allfiles[name] { return nil } @@ -229,6 +259,18 @@ func (wft *walkFileTree) iterDirectory(fpath string, fi os.FileInfo) error { fi = nfi } + relPath := wft.virPath(fpath) + + if len(relPath) > 0 { + if wft.isExcludeName(fi.Name()) { + return nil + } + + if wft.isExclude(relPath) { + return nil + } + } + err := wft.walkLeaf(fpath, fi, nil) if err != nil { if fi.IsDir() && err == path.SkipDir { @@ -314,7 +356,7 @@ type zipWalk struct { func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { isSym := fi.Mode()&os.ModeSymlink > 0 if isSym { - // golang1.1 doesn't support embed symlink + // golang1.2 still not support embed symlink // what i miss something? return false, nil } @@ -346,9 +388,14 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { return true, nil } -func packDirectory(excludePrefix []string, excludeSuffix []string, includePath ...string) (err error) { - fmt.Printf("exclude prefix: %s\n", strings.Join(excludePrefix, ":")) - fmt.Printf("exclude suffix: %s\n", strings.Join(excludeSuffix, ":")) +func packDirectory(excludePrefix []string, excludeSuffix []string, + excludeRegexp []*regexp.Regexp, includePath ...string) (err error) { + + fmt.Printf("exclude relpath prefix: %s\n", strings.Join(excludePrefix, ":")) + fmt.Printf("exclude relpath suffix: %s\n", strings.Join(excludeSuffix, ":")) + if len(excludeRegexp) > 0 { + fmt.Printf("exclude filename regex: `%s`\n", strings.Join(excludeR, "`, `")) + } w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { @@ -368,6 +415,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, includePath . walk.wak = walk walk.excludePrefix = excludePrefix walk.excludeSuffix = excludeSuffix + walk.excludeRegexp = excludeRegexp wft = walk } else { walk := new(tarWalk) @@ -385,6 +433,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string, includePath . walk.wak = walk walk.excludePrefix = excludePrefix walk.excludeSuffix = excludeSuffix + walk.excludeRegexp = excludeRegexp wft = walk } @@ -469,12 +518,31 @@ func packApp(cmd *Command, args []string) { os.Mkdir(tmpdir, 0700) if build { - fmt.Println("GOOS", goos, "GOARCH", goarch) fmt.Println("build", appName) + var envs []string + for _, env := range buildEnvs { + parts := strings.SplitN(env, "=", 2) + if len(parts) == 2 { + k, v := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) + if len(k) > 0 && len(v) > 0 { + switch k { + case "GOOS": + goos = v + case "GOARCH": + goarch = v + default: + envs = append(envs, fmt.Sprintf("%s=%s", k, v)) + } + } + } + } + os.Setenv("GOOS", goos) os.Setenv("GOARCH", goarch) + fmt.Println("GOOS", goos, "GOARCH", goarch) + binPath := path.Join(tmpdir, appName) if goos == "windows" { binPath += ".exe" @@ -490,6 +558,7 @@ func packApp(cmd *Command, args []string) { } execmd := exec.Command(gobin, args...) + execmd.Env = append(os.Environ(), envs...) execmd.Stdout = os.Stdout execmd.Stderr = os.Stderr execmd.Dir = thePath @@ -534,7 +603,18 @@ func packApp(cmd *Command, args []string) { } } - err = packDirectory(exp, exs, tmpdir, thePath) + var exr []*regexp.Regexp + for _, r := range excludeR { + if len(r) > 0 { + if re, err := regexp.Compile(r); err != nil { + exitPrint(err.Error()) + } else { + exr = append(exr, re) + } + } + } + + err = packDirectory(exp, exs, exr, tmpdir, thePath) if err != nil { exitPrint(err.Error()) }