aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2011-06-01 10:48:15 +1000
committerAndrew Gerrand <adg@golang.org>2011-06-01 10:48:15 +1000
commit9f0cabfab97aa2194d5757c1fcd78018d72d65e2 (patch)
tree8508b2c76a267951923830fc8238619b5fc362ac
parent9ec0c01e19db38f809403858fbd4ef6e6d6e03b8 (diff)
downloadgo-9f0cabfab97aa2194d5757c1fcd78018d72d65e2.tar.gz
go-9f0cabfab97aa2194d5757c1fcd78018d72d65e2.zip
goinstall: document GOPATH and support relative/absolute installs
goinstall: more verbose logging with -v Fixes #1901. R=rsc, n13m3y3r CC=golang-dev https://golang.org/cl/4524078
-rw-r--r--src/cmd/goinstall/doc.go55
-rw-r--r--src/cmd/goinstall/main.go102
-rw-r--r--src/cmd/goinstall/make.go32
-rw-r--r--src/cmd/goinstall/path.go44
4 files changed, 151 insertions, 82 deletions
diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go
index 15845b5745..13c37d0a23 100644
--- a/src/cmd/goinstall/doc.go
+++ b/src/cmd/goinstall/doc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
/*
-
Goinstall is an experiment in automatic package installation.
It installs packages, possibly downloading them from the internet.
It maintains a list of public Go packages at http://godashboard.appspot.com/package.
@@ -100,5 +99,59 @@ Instead, it invokes "make install" after locating the package sources.
For local packages without a Makefile and all remote packages,
goinstall creates and uses a temporary Makefile constructed from
the import path and the list of Go files in the package.
+
+
+The GOPATH Environment Variable
+
+GOPATH may be set to a colon-separated list of paths inside which Go code,
+package objects, and executables may be found.
+
+Set a GOPATH to use goinstall to build and install your own code and
+external libraries outside of the Go tree (and to avoid writing Makefiles).
+
+The top-level directory structure of a GOPATH is prescribed:
+
+The 'src' directory is for source code. The directory naming inside 'src'
+determines the package import path or executable name.
+
+The 'pkg' directory is for package objects. Like the Go tree, package objects
+are stored inside a directory named after the target operating system and
+processor architecture ('pkg/$GOOS_$GOARCH').
+A package whose source is located at '$GOPATH/src/foo/bar' would be imported
+as 'foo/bar' and installed as '$GOPATH/pkg/$GOOS_$GOARCH/foo/bar.a'.
+
+The 'bin' directory is for executable files.
+Goinstall installs program binaries using the name of the source folder.
+A binary whose source is at 'src/foo/qux' would be built and installed to
+'$GOPATH/bin/qux'. (Note 'bin/qux', not 'bin/foo/qux' - this is such that
+you can put the bin directory in your PATH.)
+
+Here's an example directory layout:
+
+ GOPATH=/home/user/gocode
+
+ /home/user/gocode/
+ src/foo/
+ bar/ (go code in package bar)
+ qux/ (go code in package main)
+ bin/qux (executable file)
+ pkg/linux_amd64/foo/bar.a (object file)
+
+Run 'goinstall foo/bar' to build and install the package 'foo/bar'
+(and its dependencies).
+Goinstall will search each GOPATH (in order) for 'src/foo/bar'.
+If the directory cannot be found, goinstall will attempt to fetch the
+source from a remote repository and write it to the 'src' directory of the
+first GOPATH (or $GOROOT/src/pkg if GOPATH is not set).
+
+Goinstall recognizes relative and absolute paths (paths beginning with / or .).
+The following commands would build our example packages:
+
+ goinstall /home/user/gocode/src/foo/bar # build and install foo/bar
+ cd /home/user/gocode/src/foo
+ goinstall ./bar # build and install foo/bar (again)
+ cd qux
+ goinstall . # build and install foo/qux
+
*/
package documentation
diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go
index ffa37aa417..9434c05606 100644
--- a/src/cmd/goinstall/main.go
+++ b/src/cmd/goinstall/main.go
@@ -32,9 +32,8 @@ var (
argv0 = os.Args[0]
errors = false
parents = make(map[string]string)
- root = runtime.GOROOT()
visit = make(map[string]status)
- logfile = filepath.Join(root, "goinstall.log")
+ logfile = filepath.Join(runtime.GOROOT(), "goinstall.log")
installedPkgs = make(map[string]bool)
allpkg = flag.Bool("a", false, "install all previously installed packages")
@@ -52,14 +51,30 @@ const (
done
)
+func logf(format string, args ...interface{}) {
+ format = "%s: " + format
+ args = append([]interface{}{argv0}, args...)
+ fmt.Fprintf(os.Stderr, format, args...)
+}
+
+func vlogf(format string, args ...interface{}) {
+ if *verbose {
+ logf(format, args...)
+ }
+}
+
+func errorf(format string, args ...interface{}) {
+ errors = true
+ logf(format, args...)
+}
+
func main() {
flag.Usage = usage
flag.Parse()
- if root == "" {
+ if runtime.GOROOT() == "" {
fmt.Fprintf(os.Stderr, "%s: no $GOROOT\n", argv0)
os.Exit(1)
}
- root += filepath.FromSlash("/src/pkg/")
// special case - "unsafe" is already installed
visit["unsafe"] = done
@@ -88,6 +103,11 @@ func main() {
usage()
}
for _, path := range args {
+ if strings.HasPrefix(path, "http://") {
+ errorf("'http://' used in remote path, try '%s'\n", path[7:])
+ continue
+ }
+
install(path, "")
}
if errors {
@@ -131,11 +151,6 @@ func logPackage(pkg string) {
// install installs the package named by path, which is needed by parent.
func install(pkg, parent string) {
- if isStandardPath(pkg) {
- visit[pkg] = done
- return
- }
-
// Make sure we're not already trying to install pkg.
switch visit[pkg] {
case done:
@@ -148,46 +163,44 @@ func install(pkg, parent string) {
}
visit[pkg] = visiting
parents[pkg] = parent
- if *verbose {
- fmt.Println(pkg)
- }
+
+ vlogf("%s: visit\n", pkg)
// Check whether package is local or remote.
// If remote, download or update it.
- var dir string
- proot := gopath[0] // default to GOROOT
- local := false
- if strings.HasPrefix(pkg, "http://") {
- fmt.Fprintf(os.Stderr, "%s: %s: 'http://' used in remote path, try '%s'\n", argv0, pkg, pkg[7:])
- errors = true
+ proot, pkg, err := findPackageRoot(pkg)
+ // Don't build the standard library.
+ if err == nil && proot.goroot && isStandardPath(pkg) {
+ if parent == "" {
+ errorf("%s: can not goinstall the standard library\n", pkg)
+ } else {
+ vlogf("%s: skipping standard library\n", pkg)
+ }
+ visit[pkg] = done
return
}
- if isLocalPath(pkg) {
- dir = pkg
- local = true
- } else {
- proot = findPkgroot(pkg)
- err := download(pkg, proot.srcDir())
- dir = filepath.Join(proot.srcDir(), pkg)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s: %s: %s\n", argv0, pkg, err)
- errors = true
- visit[pkg] = done
- return
- }
+ // Download remote packages if not found or forced with -u flag.
+ remote := isRemote(pkg)
+ if remote && (err == ErrPackageNotFound || (err == nil && *update)) {
+ vlogf("%s: download\n", pkg)
+ err = download(pkg, proot.srcDir())
+ }
+ if err != nil {
+ errorf("%s: %v\n", pkg, err)
+ visit[pkg] = done
+ return
}
+ dir := filepath.Join(proot.srcDir(), pkg)
// Install prerequisites.
dirInfo, err := scanDir(dir, parent == "")
if err != nil {
- fmt.Fprintf(os.Stderr, "%s: %s: %s\n", argv0, pkg, err)
- errors = true
+ errorf("%s: %v\n", pkg, err)
visit[pkg] = done
return
}
if len(dirInfo.goFiles) == 0 {
- fmt.Fprintf(os.Stderr, "%s: %s: package has no files\n", argv0, pkg)
- errors = true
+ errorf("%s: package has no files\n", pkg)
visit[pkg] = done
return
}
@@ -200,22 +213,16 @@ func install(pkg, parent string) {
// Install this package.
if !errors {
isCmd := dirInfo.pkgName == "main"
- if err := domake(dir, pkg, proot, local, isCmd); err != nil {
- fmt.Fprintf(os.Stderr, "%s: installing %s: %s\n", argv0, pkg, err)
- errors = true
- } else if !local && *logPkgs {
- // mark this package as installed in $GOROOT/goinstall.log
+ if err := domake(dir, pkg, proot, isCmd); err != nil {
+ errorf("installing: %v\n", err)
+ } else if remote && *logPkgs {
+ // mark package as installed in $GOROOT/goinstall.log
logPackage(pkg)
}
}
visit[pkg] = done
}
-// Is this a local path? /foo ./foo ../foo . ..
-func isLocalPath(s string) bool {
- const sep = string(filepath.Separator)
- return strings.HasPrefix(s, sep) || strings.HasPrefix(s, "."+sep) || strings.HasPrefix(s, ".."+sep) || s == "." || s == ".."
-}
// Is this a standard package path? strings container/vector etc.
// Assume that if the first element has a dot, it's a domain name
@@ -245,9 +252,7 @@ func genRun(dir string, stdin []byte, cmd []string, quiet bool) os.Error {
return err
}
p, err := exec.Run(bin, cmd, os.Environ(), dir, exec.Pipe, exec.Pipe, exec.MergeWithStdout)
- if *verbose {
- fmt.Fprintf(os.Stderr, "%s: %s; %s %s\n", argv0, dir, bin, strings.Join(cmd[1:], " "))
- }
+ vlogf("%s: %s %s\n", dir, bin, strings.Join(cmd[1:], " "))
if err != nil {
return err
}
@@ -257,7 +262,6 @@ func genRun(dir string, stdin []byte, cmd []string, quiet bool) os.Error {
}()
var buf bytes.Buffer
io.Copy(&buf, p.Stdout)
- io.Copy(&buf, p.Stdout)
w, err := p.Wait(0)
p.Close()
if err != nil {
diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go
index 67c7b93ef3..0c44481d71 100644
--- a/src/cmd/goinstall/make.go
+++ b/src/cmd/goinstall/make.go
@@ -14,26 +14,14 @@ import (
)
// domake builds the package in dir.
-// If local is false, the package was copied from an external system.
-// For non-local packages or packages without Makefiles,
// domake generates a standard Makefile and passes it
// to make on standard input.
-func domake(dir, pkg string, root *pkgroot, local, isCmd bool) (err os.Error) {
- needMakefile := true
- if local {
- _, err := os.Stat(filepath.Join(dir, "Makefile"))
- if err == nil {
- needMakefile = false
- }
- }
- cmd := []string{"bash", "gomake"}
- var makefile []byte
- if needMakefile {
- if makefile, err = makeMakefile(dir, pkg, root, isCmd); err != nil {
- return err
- }
- cmd = append(cmd, "-f-")
+func domake(dir, pkg string, root *pkgroot, isCmd bool) (err os.Error) {
+ makefile, err := makeMakefile(dir, pkg, root, isCmd)
+ if err != nil {
+ return err
}
+ cmd := []string{"bash", "gomake", "-f-"}
if *clean {
cmd = append(cmd, "clean")
}
@@ -51,16 +39,8 @@ func makeMakefile(dir, pkg string, root *pkgroot, isCmd bool) ([]byte, os.Error)
targ := pkg
targDir := root.pkgDir()
if isCmd {
- // use the last part of the package name only
+ // use the last part of the package name for targ
_, targ = filepath.Split(pkg)
- // if building the working dir use the directory name
- if targ == "." {
- d, err := filepath.Abs(dir)
- if err != nil {
- return nil, os.NewError("finding path: " + err.String())
- }
- _, targ = filepath.Split(d)
- }
targDir = root.binDir()
}
dirInfo, err := scanDir(dir, isCmd)
diff --git a/src/cmd/goinstall/path.go b/src/cmd/goinstall/path.go
index 1153e04714..7b4bda0fb8 100644
--- a/src/cmd/goinstall/path.go
+++ b/src/cmd/goinstall/path.go
@@ -5,10 +5,12 @@
package main
import (
+ "fmt"
"log"
"os"
"path/filepath"
"runtime"
+ "strings"
)
var (
@@ -19,6 +21,7 @@ var (
// set up gopath: parse and validate GOROOT and GOPATH variables
func init() {
+ root := runtime.GOROOT()
p, err := newPkgroot(root)
if err != nil {
log.Fatalf("Invalid GOROOT %q: %v", root, err)
@@ -105,13 +108,42 @@ func (r *pkgroot) hasPkg(name string) bool {
// TODO(adg): check object version is consistent
}
-// findPkgroot searches each of the gopath roots
-// for the source code for the given import path.
-func findPkgroot(importPath string) *pkgroot {
+
+var ErrPackageNotFound = os.NewError("package could not be found locally")
+
+// findPackageRoot takes an import or filesystem path and returns the
+// root where the package source should be and the package import path.
+func findPackageRoot(path string) (root *pkgroot, pkg string, err os.Error) {
+ if isLocalPath(path) {
+ if path, err = filepath.Abs(path); err != nil {
+ return
+ }
+ for _, r := range gopath {
+ rpath := r.srcDir() + filepath.SeparatorString
+ if !strings.HasPrefix(path, rpath) {
+ continue
+ }
+ root = r
+ pkg = path[len(rpath):]
+ return
+ }
+ err = fmt.Errorf("path %q not inside a GOPATH", path)
+ return
+ }
+ root = defaultRoot
+ pkg = path
for _, r := range gopath {
- if r.hasSrcDir(importPath) {
- return r
+ if r.hasSrcDir(path) {
+ root = r
+ return
}
}
- return defaultRoot
+ err = ErrPackageNotFound
+ return
+}
+
+// Is this a local path? /foo ./foo ../foo . ..
+func isLocalPath(s string) bool {
+ const sep = string(filepath.Separator)
+ return strings.HasPrefix(s, sep) || strings.HasPrefix(s, "."+sep) || strings.HasPrefix(s, ".."+sep) || s == "." || s == ".."
}