// Copyright 2011 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. package build import ( "fmt" "log" "os" "path/filepath" "runtime" ) // Path is a validated list of Trees derived from $GOROOT and $GOPATH at init. var Path []*Tree // Tree describes a Go source tree, either $GOROOT or one from $GOPATH. type Tree struct { Path string Goroot bool } func newTree(p string) (*Tree, os.Error) { if !filepath.IsAbs(p) { return nil, os.NewError("must be absolute") } ep, err := filepath.EvalSymlinks(p) if err != nil { return nil, err } return &Tree{Path: ep}, nil } // SrcDir returns the tree's package source directory. func (t *Tree) SrcDir() string { if t.Goroot { return filepath.Join(t.Path, "src", "pkg") } return filepath.Join(t.Path, "src") } // PkgDir returns the tree's package object directory. func (t *Tree) PkgDir() string { goos, goarch := runtime.GOOS, runtime.GOARCH if e := os.Getenv("GOOS"); e != "" { goos = e } if e := os.Getenv("GOARCH"); e != "" { goarch = e } return filepath.Join(t.Path, "pkg", goos+"_"+goarch) } // BinDir returns the tree's binary executable directory. func (t *Tree) BinDir() string { if t.Goroot { if gobin := os.Getenv("GOBIN"); gobin != "" { return gobin } } return filepath.Join(t.Path, "bin") } // HasSrc returns whether the given package's // source can be found inside this Tree. func (t *Tree) HasSrc(pkg string) bool { fi, err := os.Stat(filepath.Join(t.SrcDir(), pkg)) if err != nil { return false } return fi.IsDirectory() } // HasPkg returns whether the given package's // object file can be found inside this Tree. func (t *Tree) HasPkg(pkg string) bool { fi, err := os.Stat(filepath.Join(t.PkgDir(), pkg+".a")) if err != nil { return false } return fi.IsRegular() // TODO(adg): check object version is consistent } var ( ErrNotFound = os.NewError("go/build: package could not be found locally") ErrTreeNotFound = os.NewError("go/build: no valid GOROOT or GOPATH could be found") ) // FindTree takes an import or filesystem path and returns the // tree where the package source should be and the package import path. func FindTree(path string) (tree *Tree, pkg string, err os.Error) { if isLocalPath(path) { if path, err = filepath.Abs(path); err != nil { return } if path, err = filepath.EvalSymlinks(path); err != nil { return } for _, t := range Path { tpath := t.SrcDir() + string(filepath.Separator) if !filepath.HasPrefix(path, tpath) { continue } tree = t pkg = path[len(tpath):] return } err = fmt.Errorf("path %q not inside a GOPATH", path) return } tree = defaultTree pkg = path for _, t := range Path { if t.HasSrc(pkg) { tree = t return } } if tree == nil { err = ErrTreeNotFound } else { err = ErrNotFound } return } // isLocalPath returns whether the given path is local (/foo ./foo ../foo . ..) // Windows paths that starts with drive letter (c:\foo c:foo) are considered local. func isLocalPath(s string) bool { const sep = string(filepath.Separator) return s == "." || s == ".." || filepath.HasPrefix(s, sep) || filepath.HasPrefix(s, "."+sep) || filepath.HasPrefix(s, ".."+sep) || filepath.VolumeName(s) != "" } var ( // argument lists used by the build's gc and ld methods gcImportArgs []string ldImportArgs []string // default tree for remote packages defaultTree *Tree ) // set up Path: parse and validate GOROOT and GOPATH variables func init() { root := runtime.GOROOT() t, err := newTree(root) if err != nil { log.Printf("go/build: invalid GOROOT %q: %v", root, err) } else { t.Goroot = true Path = []*Tree{t} } for _, p := range filepath.SplitList(os.Getenv("GOPATH")) { if p == "" { continue } t, err := newTree(p) if err != nil { log.Printf("go/build: invalid GOPATH %q: %v", p, err) continue } Path = append(Path, t) gcImportArgs = append(gcImportArgs, "-I", t.PkgDir()) ldImportArgs = append(ldImportArgs, "-L", t.PkgDir()) // select first GOPATH entry as default if defaultTree == nil { defaultTree = t } } // use GOROOT if no valid GOPATH specified if defaultTree == nil && len(Path) > 0 { defaultTree = Path[0] } }