Skip to content
This repository has been archived by the owner on Feb 26, 2019. It is now read-only.

Commit

Permalink
save: new flag -copy to copy dependency code
Browse files Browse the repository at this point in the history
  • Loading branch information
kr committed Oct 11, 2013
1 parent a4fb666 commit f44942b
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 24 deletions.
5 changes: 5 additions & 0 deletions dep.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ type Dependency struct {
Comment string `json:",omitempty"` // Description of commit, if present.
Rev string // VCS-specific commit ID.

// used by command save
pkg *Package

// used by command go
outerRoot string // dir, if present, in outer GOPATH
repoRoot *vcs.RepoRoot
vcs *VCS
Expand Down Expand Up @@ -95,6 +99,7 @@ func (g *Godeps) Load(pkgs []*Package) error {
ImportPath: pkg.ImportPath,
Rev: id,
Comment: comment,
pkg: pkg,
})
}
return err1
Expand Down
35 changes: 25 additions & 10 deletions go.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,7 @@ with the dependencies listed in file Godeps.
// space is cheap and plentiful, and writing files is slow.
// Everything is kept in the spool directory.
func runGo(cmd *Command, args []string) {
g, err := ReadGodeps("Godeps")
if err != nil {
log.Fatalln(err)
}
gopath, err := sandboxAll(g.Deps)
if err != nil {
log.Fatalln(err)
}

gopath := prepareGopath("Godeps")
if s := os.Getenv("GOPATH"); s != "" {
gopath += ":" + os.Getenv("GOPATH")
}
Expand All @@ -44,12 +36,35 @@ func runGo(cmd *Command, args []string) {
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
err = c.Run()
err := c.Run()
if err != nil {
log.Fatalln("go", err)
}
}

// prepareGopath reads dependency information from the filesystem
// entry name, fetches any necessary code, and returns a gopath
// causing the specified dependencies to be used.
func prepareGopath(name string) (gopath string) {
if fi, err := os.Stat(name); err == nil && fi.IsDir() {
wd, err := os.Getwd()
if err != nil {
log.Fatalln(err)
}
gopath = filepath.Join(wd, name, "_workspace")
} else {
g, err := ReadGodeps(name)
if err != nil {
log.Fatalln(err)
}
gopath, err = sandboxAll(g.Deps)
if err != nil {
log.Fatalln(err)
}
}
return gopath
}

func envNoGopath() (a []string) {
for _, s := range os.Environ() {
if !strings.HasPrefix(s, "GOPATH=") {
Expand Down
10 changes: 1 addition & 9 deletions path.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"fmt"
"log"
)

var cmdPath = &Command{
Expand All @@ -26,13 +25,6 @@ func runPath(cmd *Command, args []string) {
if len(args) != 0 {
cmd.UsageExit()
}
g, err := ReadGodeps("Godeps")
if err != nil {
log.Fatalln(err)
}
gopath, err := sandboxAll(g.Deps)
if err != nil {
log.Fatalln(err)
}
gopath := prepareGopath("Godeps")
fmt.Println(gopath)
}
129 changes: 124 additions & 5 deletions save.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package main

import (
"errors"
"github.com/kr/fs"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)

var cmdSave = &Command{
Usage: "save [packages]",
Usage: "save [-copy] [packages]",
Short: "list current dependencies to a file",
Long: `
Save writes a list of the dependencies of the named packages along
with the exact source control revision of each dependency. It writes
output to file Godeps in the current directory, in JSON format with
the following structure:
with the exact source control revision of each dependency. Output
is a JSON document with the following structure:
type Godeps struct {
ImportPath string
Expand All @@ -25,12 +30,28 @@ the following structure:
}
}
If flag -copy is given, the list is written to Godeps/Godeps.json,
and source code for all dependencies is copied into Godeps.
Otherwise, the list alone is written to file Godeps.
For more about specifying packages, see 'go help packages'.
`,
Run: runSave,
}

var flagCopy bool

func init() {
cmdSave.Flag.BoolVar(&flagCopy, "copy", false, "copy source code")
}

func runSave(cmd *Command, args []string) {
// Remove Godeps before listing packages, so that args
// such as ./... don't match anything in there.
if err := os.RemoveAll("Godeps"); err != nil {
log.Fatalln(err)
}
g := &Godeps{
ImportPath: MustLoadPackages(".")[0].ImportPath,
GoVersion: mustGoVersion(),
Expand All @@ -48,7 +69,26 @@ func runSave(cmd *Command, args []string) {
if g.Deps == nil {
g.Deps = make([]Dependency, 0) // produce json [], not null
}
f, err := os.Create("Godeps")
manifest := "Godeps"
if flagCopy {
manifest = filepath.Join("Godeps", "Godeps.json")
// We use a name starting with "_" so the go tool
// ignores this directory when traversing packages
// starting at the project's root. For example,
// godep go list ./...
workspace := filepath.Join("Godeps", "_workspace")
err = copySrc(workspace, g)
if err != nil {
log.Fatalln(err)
}
path := filepath.Join("Godeps", "Readme")
err = ioutil.WriteFile(path, []byte(strings.TrimSpace(Readme)+"\n"), 0666)
if err != nil {
log.Println(err)
}
writeVCSIgnore(workspace)
}
f, err := os.Create(manifest)
if err != nil {
log.Fatalln(err)
}
Expand All @@ -61,3 +101,82 @@ func runSave(cmd *Command, args []string) {
log.Fatalln(err)
}
}

func copySrc(dir string, g *Godeps) error {
ok := true
for _, dep := range g.Deps {
w := fs.Walk(dep.pkg.Dir)
for w.Step() {
if w.Err() != nil {
log.Println(w.Err())
ok = false
continue
}
if s := w.Stat().Name(); s[0] == '.' || s[1] == '_' {
// Skip directories using a rule similar to how
// the go tool enumerates packages.
// See $GOROOT/src/cmd/go/main.go:/matchPackagesInFs
w.SkipDir()
}
if w.Stat().IsDir() {
continue
}
dst := filepath.Join(dir, w.Path()[len(dep.pkg.Root)+1:])
if err := copyFile(dst, w.Path()); err != nil {
log.Println(err)
ok = false
}
}
}
if !ok {
return errors.New("error copying source code")
}
return nil
}

// copyFile copies a regular file from src to dst.
// dst is opened with os.Create.
func copyFile(dst, src string) error {
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()
err = os.MkdirAll(filepath.Dir(dst), 0777)
if err != nil {
return err
}
w, err := os.Create(dst)
if err != nil {
return err
}
_, err = io.Copy(w, r)
err1 := w.Close()
if err == nil {
err = err1
}
return err
}

// Func writeVCSIgnore writes "ignore" files inside dir for known VCSs,
// so that dir/pkg and dir/bin don't accidentally get committed.
// It logs any errors it encounters.
func writeVCSIgnore(dir string) {
// Currently git is the only VCS for which we know how to do this.
// Mercurial and Bazaar have similar mechasims, but they apparently
// require writing files outside of dir.
const ignore = "/pkg\n/bin\n"
name := filepath.Join(dir, ".gitignore")
err := ioutil.WriteFile(name, []byte(ignore), 0666)
if err != nil {
log.Println(err)
}
}

const Readme = `
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/kr/godep for more information.
`

0 comments on commit f44942b

Please sign in to comment.