mirror of
https://github.com/golang/go
synced 2024-11-24 18:20:02 -07:00
goinstall: use go/make package to scan and build packages
R=rsc, n13m3y3r, kevlar CC=golang-dev https://golang.org/cl/4515180
This commit is contained in:
parent
414da2e4a3
commit
db5a4ffc2a
@ -8,23 +8,5 @@ TARG=goinstall
|
||||
GOFILES=\
|
||||
download.go\
|
||||
main.go\
|
||||
make.go\
|
||||
parse.go\
|
||||
path.go\
|
||||
syslist.go\
|
||||
|
||||
CLEANFILES+=syslist.go
|
||||
|
||||
include ../../Make.cmd
|
||||
|
||||
syslist.go:
|
||||
echo '// Generated automatically by make.' >$@
|
||||
echo 'package main' >>$@
|
||||
echo 'const goosList = "$(GOOS_LIST)"' >>$@
|
||||
echo 'const goarchList = "$(GOARCH_LIST)"' >>$@
|
||||
|
||||
test:
|
||||
gotest
|
||||
|
||||
testshort:
|
||||
gotest -test.short
|
||||
|
@ -15,7 +15,9 @@ Flags and default settings:
|
||||
-a=false install all previously installed packages
|
||||
-clean=false clean the package directory before installing
|
||||
-dashboard=true tally public packages on godashboard.appspot.com
|
||||
-install=true build and install the package and its dependencies
|
||||
-log=true log installed packages to $GOROOT/goinstall.log for use by -a
|
||||
-nuke=false remove the target object and clean before installing
|
||||
-u=false update already-downloaded packages
|
||||
-v=false verbose operation
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Experimental Go package installer; see doc.go.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -11,6 +9,7 @@ import (
|
||||
"exec"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -39,7 +38,9 @@ var (
|
||||
reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL)
|
||||
logPkgs = flag.Bool("log", true, "log installed packages to $GOROOT/goinstall.log for use by -a")
|
||||
update = flag.Bool("u", false, "update already-downloaded packages")
|
||||
doInstall = flag.Bool("install", true, "build and install")
|
||||
clean = flag.Bool("clean", false, "clean the package directory before installing")
|
||||
nuke = flag.Bool("nuke", false, "clean the package directory and target before installing")
|
||||
verbose = flag.Bool("v", false, "verbose")
|
||||
)
|
||||
|
||||
@ -160,66 +161,83 @@ func install(pkg, parent string) {
|
||||
fmt.Fprintf(os.Stderr, "\t%s\n", pkg)
|
||||
os.Exit(2)
|
||||
}
|
||||
visit[pkg] = visiting
|
||||
parents[pkg] = parent
|
||||
|
||||
vlogf("%s: visit\n", pkg)
|
||||
visit[pkg] = visiting
|
||||
defer func() {
|
||||
visit[pkg] = done
|
||||
}()
|
||||
|
||||
// Check whether package is local or remote.
|
||||
// If remote, download or update it.
|
||||
proot, pkg, err := findPackageRoot(pkg)
|
||||
tree, pkg, err := build.FindTree(pkg)
|
||||
// Don't build the standard library.
|
||||
if err == nil && proot.goroot && isStandardPath(pkg) {
|
||||
if err == nil && tree.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
|
||||
}
|
||||
// Download remote packages if not found or forced with -u flag.
|
||||
remote := isRemote(pkg)
|
||||
if remote && (err == ErrPackageNotFound || (err == nil && *update)) {
|
||||
if remote && (err == build.ErrNotFound || (err == nil && *update)) {
|
||||
vlogf("%s: download\n", pkg)
|
||||
err = download(pkg, proot.srcDir())
|
||||
err = download(pkg, tree.SrcDir())
|
||||
}
|
||||
if err != nil {
|
||||
errorf("%s: %v\n", pkg, err)
|
||||
visit[pkg] = done
|
||||
return
|
||||
}
|
||||
dir := filepath.Join(proot.srcDir(), pkg)
|
||||
dir := filepath.Join(tree.SrcDir(), pkg)
|
||||
|
||||
// Install prerequisites.
|
||||
dirInfo, err := scanDir(dir, parent == "")
|
||||
dirInfo, err := build.ScanDir(dir, parent == "")
|
||||
if err != nil {
|
||||
errorf("%s: %v\n", pkg, err)
|
||||
visit[pkg] = done
|
||||
return
|
||||
}
|
||||
if len(dirInfo.goFiles) == 0 {
|
||||
if len(dirInfo.GoFiles) == 0 {
|
||||
errorf("%s: package has no files\n", pkg)
|
||||
visit[pkg] = done
|
||||
return
|
||||
}
|
||||
for _, p := range dirInfo.imports {
|
||||
for _, p := range dirInfo.Imports {
|
||||
if p != "C" {
|
||||
install(p, pkg)
|
||||
}
|
||||
}
|
||||
if errors {
|
||||
return
|
||||
}
|
||||
|
||||
// Install this package.
|
||||
if !errors {
|
||||
isCmd := dirInfo.pkgName == "main"
|
||||
if err := domake(dir, pkg, proot, isCmd); err != nil {
|
||||
errorf("installing: %v\n", err)
|
||||
} else if remote && *logPkgs {
|
||||
script, err := build.Build(tree, pkg, dirInfo)
|
||||
if err != nil {
|
||||
errorf("%s: install: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
if *nuke {
|
||||
vlogf("%s: nuke\n", pkg)
|
||||
script.Nuke()
|
||||
} else if *clean {
|
||||
vlogf("%s: clean\n", pkg)
|
||||
script.Clean()
|
||||
}
|
||||
if *doInstall {
|
||||
if script.Stale() {
|
||||
vlogf("%s: install\n", pkg)
|
||||
if err := script.Run(); err != nil {
|
||||
errorf("%s: install: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
vlogf("%s: install: up-to-date\n", pkg)
|
||||
}
|
||||
}
|
||||
if remote {
|
||||
// mark package as installed in $GOROOT/goinstall.log
|
||||
logPackage(pkg)
|
||||
}
|
||||
}
|
||||
visit[pkg] = done
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,168 +0,0 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// Run "make install" to build package.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"template"
|
||||
)
|
||||
|
||||
// domake builds the package in dir.
|
||||
// domake generates a standard Makefile and passes it
|
||||
// to make on standard input.
|
||||
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")
|
||||
}
|
||||
cmd = append(cmd, "install")
|
||||
return run(dir, makefile, cmd...)
|
||||
}
|
||||
|
||||
// makeMakefile computes the standard Makefile for the directory dir
|
||||
// installing as package pkg. It includes all *.go files in the directory
|
||||
// except those in package main and those ending in _test.go.
|
||||
func makeMakefile(dir, pkg string, root *pkgroot, isCmd bool) ([]byte, os.Error) {
|
||||
if !safeName(pkg) {
|
||||
return nil, os.ErrorString("unsafe name: " + pkg)
|
||||
}
|
||||
targ := pkg
|
||||
targDir := root.pkgDir()
|
||||
if isCmd {
|
||||
// use the last part of the package name for targ
|
||||
_, targ = filepath.Split(pkg)
|
||||
targDir = root.binDir()
|
||||
}
|
||||
dirInfo, err := scanDir(dir, isCmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cgoFiles := dirInfo.cgoFiles
|
||||
isCgo := make(map[string]bool, len(cgoFiles))
|
||||
for _, file := range cgoFiles {
|
||||
if !safeName(file) {
|
||||
return nil, os.ErrorString("bad name: " + file)
|
||||
}
|
||||
isCgo[file] = true
|
||||
}
|
||||
|
||||
goFiles := make([]string, 0, len(dirInfo.goFiles))
|
||||
for _, file := range dirInfo.goFiles {
|
||||
if !safeName(file) {
|
||||
return nil, os.ErrorString("unsafe name: " + file)
|
||||
}
|
||||
if !isCgo[file] {
|
||||
goFiles = append(goFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
oFiles := make([]string, 0, len(dirInfo.cFiles)+len(dirInfo.sFiles))
|
||||
cgoOFiles := make([]string, 0, len(dirInfo.cFiles))
|
||||
for _, file := range dirInfo.cFiles {
|
||||
if !safeName(file) {
|
||||
return nil, os.ErrorString("unsafe name: " + file)
|
||||
}
|
||||
// When cgo is in use, C files are compiled with gcc,
|
||||
// otherwise they're compiled with gc.
|
||||
if len(cgoFiles) > 0 {
|
||||
cgoOFiles = append(cgoOFiles, file[:len(file)-2]+".o")
|
||||
} else {
|
||||
oFiles = append(oFiles, file[:len(file)-2]+".$O")
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range dirInfo.sFiles {
|
||||
if !safeName(file) {
|
||||
return nil, os.ErrorString("unsafe name: " + file)
|
||||
}
|
||||
oFiles = append(oFiles, file[:len(file)-2]+".$O")
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
md := makedata{targ, targDir, "pkg", goFiles, oFiles, cgoFiles, cgoOFiles, imports}
|
||||
if isCmd {
|
||||
md.Type = "cmd"
|
||||
}
|
||||
if err := makefileTemplate.Execute(&buf, &md); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var safeBytes = []byte("+-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
func safeName(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// makedata is the data type for the makefileTemplate.
|
||||
type makedata struct {
|
||||
Targ string // build target
|
||||
TargDir string // build target directory
|
||||
Type string // build type: "pkg" or "cmd"
|
||||
GoFiles []string // list of non-cgo .go files
|
||||
OFiles []string // list of .$O files
|
||||
CgoFiles []string // list of cgo .go files
|
||||
CgoOFiles []string // list of cgo .o files, without extension
|
||||
Imports []string // gc/ld import paths
|
||||
}
|
||||
|
||||
var makefileTemplate = template.MustParse(`
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG={Targ}
|
||||
TARGDIR={TargDir}
|
||||
|
||||
{.section GoFiles}
|
||||
GOFILES=\
|
||||
{.repeated section GoFiles}
|
||||
{@}\
|
||||
{.end}
|
||||
|
||||
{.end}
|
||||
{.section OFiles}
|
||||
OFILES=\
|
||||
{.repeated section OFiles}
|
||||
{@}\
|
||||
{.end}
|
||||
|
||||
{.end}
|
||||
{.section CgoFiles}
|
||||
CGOFILES=\
|
||||
{.repeated section CgoFiles}
|
||||
{@}\
|
||||
{.end}
|
||||
|
||||
{.end}
|
||||
{.section CgoOFiles}
|
||||
CGO_OFILES=\
|
||||
{.repeated section CgoOFiles}
|
||||
{@}\
|
||||
{.end}
|
||||
|
||||
{.end}
|
||||
GCIMPORTS={.repeated section Imports}-I "{@}" {.end}
|
||||
LDIMPORTS={.repeated section Imports}-L "{@}" {.end}
|
||||
|
||||
include $(GOROOT)/src/Make.{Type}
|
||||
`,
|
||||
nil)
|
@ -1,172 +0,0 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// Wrappers for Go parser.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
|
||||
type dirInfo struct {
|
||||
goFiles []string // .go files within dir (including cgoFiles)
|
||||
cgoFiles []string // .go files that import "C"
|
||||
cFiles []string // .c files within dir
|
||||
sFiles []string // .s files within dir
|
||||
imports []string // All packages imported by goFiles
|
||||
pkgName string // Name of package within dir
|
||||
}
|
||||
|
||||
// scanDir returns a structure with details about the Go content found
|
||||
// in the given directory. The list of files will NOT contain the
|
||||
// following entries:
|
||||
//
|
||||
// - Files in package main (unless allowMain is true)
|
||||
// - Files ending in _test.go
|
||||
// - Files starting with _ (temporary)
|
||||
// - Files containing .cgo in their names
|
||||
//
|
||||
// The imports map keys are package paths imported by listed Go files,
|
||||
// and the values are the Go files importing the respective package paths.
|
||||
func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) {
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dirs, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goFiles := make([]string, 0, len(dirs))
|
||||
cgoFiles := make([]string, 0, len(dirs))
|
||||
cFiles := make([]string, 0, len(dirs))
|
||||
sFiles := make([]string, 0, len(dirs))
|
||||
importsm := make(map[string]bool)
|
||||
pkgName := ""
|
||||
for i := range dirs {
|
||||
d := &dirs[i]
|
||||
if strings.HasPrefix(d.Name, "_") || strings.Index(d.Name, ".cgo") != -1 {
|
||||
continue
|
||||
}
|
||||
if !goodOSArch(d.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch filepath.Ext(d.Name) {
|
||||
case ".go":
|
||||
if strings.HasSuffix(d.Name, "_test.go") {
|
||||
continue
|
||||
}
|
||||
case ".c":
|
||||
cFiles = append(cFiles, d.Name)
|
||||
continue
|
||||
case ".s":
|
||||
sFiles = append(sFiles, d.Name)
|
||||
continue
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
filename := filepath.Join(dir, d.Name)
|
||||
pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := string(pf.Name.Name)
|
||||
if s == "main" && !allowMain {
|
||||
continue
|
||||
}
|
||||
if s == "documentation" {
|
||||
continue
|
||||
}
|
||||
if pkgName == "" {
|
||||
pkgName = s
|
||||
} else if pkgName != s {
|
||||
// Only if all files in the directory are in package main
|
||||
// do we return pkgName=="main".
|
||||
// A mix of main and another package reverts
|
||||
// to the original (allowMain=false) behaviour.
|
||||
if s == "main" || pkgName == "main" {
|
||||
return scanDir(dir, false)
|
||||
}
|
||||
return nil, os.ErrorString("multiple package names in " + dir)
|
||||
}
|
||||
goFiles = append(goFiles, d.Name)
|
||||
for _, decl := range pf.Decls {
|
||||
for _, spec := range decl.(*ast.GenDecl).Specs {
|
||||
quoted := string(spec.(*ast.ImportSpec).Path.Value)
|
||||
unquoted, err := strconv.Unquote(quoted)
|
||||
if err != nil {
|
||||
log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
|
||||
}
|
||||
importsm[unquoted] = true
|
||||
if unquoted == "C" {
|
||||
cgoFiles = append(cgoFiles, d.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
imports := make([]string, len(importsm))
|
||||
i := 0
|
||||
for p := range importsm {
|
||||
imports[i] = p
|
||||
i++
|
||||
}
|
||||
return &dirInfo{goFiles, cgoFiles, cFiles, sFiles, imports, pkgName}, nil
|
||||
}
|
||||
|
||||
// goodOSArch returns false if the filename contains a $GOOS or $GOARCH
|
||||
// suffix which does not match the current system.
|
||||
// The recognized filename formats are:
|
||||
//
|
||||
// name_$(GOOS).*
|
||||
// name_$(GOARCH).*
|
||||
// name_$(GOOS)_$(GOARCH).*
|
||||
//
|
||||
func goodOSArch(filename string) bool {
|
||||
if dot := strings.Index(filename, "."); dot != -1 {
|
||||
filename = filename[:dot]
|
||||
}
|
||||
l := strings.Split(filename, "_", -1)
|
||||
n := len(l)
|
||||
if n == 0 {
|
||||
return true
|
||||
}
|
||||
if good, known := goodOS[l[n-1]]; known {
|
||||
return good
|
||||
}
|
||||
if good, known := goodArch[l[n-1]]; known {
|
||||
if !good || n < 2 {
|
||||
return false
|
||||
}
|
||||
good, known = goodOS[l[n-2]]
|
||||
return good || !known
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var goodOS = make(map[string]bool)
|
||||
var goodArch = make(map[string]bool)
|
||||
|
||||
func init() {
|
||||
goodOS = make(map[string]bool)
|
||||
goodArch = make(map[string]bool)
|
||||
for _, v := range strings.Fields(goosList) {
|
||||
goodOS[v] = v == runtime.GOOS
|
||||
}
|
||||
for _, v := range strings.Fields(goarchList) {
|
||||
goodArch[v] = v == runtime.GOARCH
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
gopath []*pkgroot
|
||||
imports []string
|
||||
defaultRoot *pkgroot // default root for remote packages
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
p.goroot = true
|
||||
gopath = []*pkgroot{p}
|
||||
|
||||
for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
r, err := newPkgroot(p)
|
||||
if err != nil {
|
||||
log.Printf("Invalid GOPATH %q: %v", p, err)
|
||||
continue
|
||||
}
|
||||
gopath = append(gopath, r)
|
||||
imports = append(imports, r.pkgDir())
|
||||
|
||||
// select first GOPATH entry as default
|
||||
if defaultRoot == nil {
|
||||
defaultRoot = r
|
||||
}
|
||||
}
|
||||
|
||||
// use GOROOT if no valid GOPATH specified
|
||||
if defaultRoot == nil {
|
||||
defaultRoot = gopath[0]
|
||||
}
|
||||
}
|
||||
|
||||
type pkgroot struct {
|
||||
path string
|
||||
goroot bool // TODO(adg): remove this once Go tree re-organized
|
||||
}
|
||||
|
||||
func newPkgroot(p string) (*pkgroot, 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 &pkgroot{path: ep}, nil
|
||||
}
|
||||
|
||||
func (r *pkgroot) srcDir() string {
|
||||
if r.goroot {
|
||||
return filepath.Join(r.path, "src", "pkg")
|
||||
}
|
||||
return filepath.Join(r.path, "src")
|
||||
}
|
||||
|
||||
func (r *pkgroot) 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(r.path, "pkg", goos+"_"+goarch)
|
||||
}
|
||||
|
||||
func (r *pkgroot) binDir() string {
|
||||
return filepath.Join(r.path, "bin")
|
||||
}
|
||||
|
||||
func (r *pkgroot) hasSrcDir(name string) bool {
|
||||
fi, err := os.Stat(filepath.Join(r.srcDir(), name))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.IsDirectory()
|
||||
}
|
||||
|
||||
func (r *pkgroot) hasPkg(name string) bool {
|
||||
fi, err := os.Stat(filepath.Join(r.pkgDir(), name+".a"))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.IsRegular()
|
||||
// TODO(adg): check object version is consistent
|
||||
}
|
||||
|
||||
|
||||
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() + string(filepath.Separator)
|
||||
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(path) {
|
||||
root = r
|
||||
return
|
||||
}
|
||||
}
|
||||
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 == ".."
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
thisOS = runtime.GOOS
|
||||
thisArch = runtime.GOARCH
|
||||
otherOS = anotherOS()
|
||||
otherArch = anotherArch()
|
||||
)
|
||||
|
||||
func anotherOS() string {
|
||||
if thisOS != "darwin" {
|
||||
return "darwin"
|
||||
}
|
||||
return "linux"
|
||||
}
|
||||
|
||||
func anotherArch() string {
|
||||
if thisArch != "amd64" {
|
||||
return "amd64"
|
||||
}
|
||||
return "386"
|
||||
}
|
||||
|
||||
type GoodFileTest struct {
|
||||
name string
|
||||
result bool
|
||||
}
|
||||
|
||||
var tests = []GoodFileTest{
|
||||
{"file.go", true},
|
||||
{"file.c", true},
|
||||
{"file_foo.go", true},
|
||||
{"file_" + thisArch + ".go", true},
|
||||
{"file_" + otherArch + ".go", false},
|
||||
{"file_" + thisOS + ".go", true},
|
||||
{"file_" + otherOS + ".go", false},
|
||||
{"file_" + thisOS + "_" + thisArch + ".go", true},
|
||||
{"file_" + otherOS + "_" + thisArch + ".go", false},
|
||||
{"file_" + thisOS + "_" + otherArch + ".go", false},
|
||||
{"file_" + otherOS + "_" + otherArch + ".go", false},
|
||||
{"file_foo_" + thisArch + ".go", true},
|
||||
{"file_foo_" + otherArch + ".go", false},
|
||||
{"file_" + thisOS + ".c", true},
|
||||
{"file_" + otherOS + ".c", false},
|
||||
}
|
||||
|
||||
func TestGoodOSArch(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
if goodOSArch(test.name) != test.result {
|
||||
t.Fatalf("goodOSArch(%q) != %v", test.name, test.result)
|
||||
}
|
||||
}
|
||||
}
|
@ -208,6 +208,7 @@ NOTEST+=\
|
||||
../cmd/cgo\
|
||||
../cmd/ebnflint\
|
||||
../cmd/godoc\
|
||||
../cmd/goinstall\
|
||||
../cmd/gotest\
|
||||
../cmd/govet\
|
||||
../cmd/goyacc\
|
||||
|
@ -15,8 +15,14 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) {
|
||||
b := &build{obj: "_obj/"}
|
||||
// Build produces a build Script for the given package.
|
||||
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
|
||||
s := &Script{}
|
||||
b := &build{
|
||||
script: s,
|
||||
path: filepath.Join(tree.SrcDir(), pkg),
|
||||
}
|
||||
b.obj = b.abs("_obj") + "/"
|
||||
|
||||
goarch := runtime.GOARCH
|
||||
if g := os.Getenv("GOARCH"); g != "" {
|
||||
@ -28,17 +34,25 @@ func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var gofiles = d.GoFiles // .go files to be built with gc
|
||||
var ofiles []string // *.GOARCH files to be linked or packed
|
||||
// .go files to be built with gc
|
||||
gofiles := b.abss(info.GoFiles...)
|
||||
s.addInput(gofiles...)
|
||||
|
||||
var ofiles []string // object files to be linked or packed
|
||||
|
||||
// make build directory
|
||||
b.mkdir(b.obj)
|
||||
s.addIntermediate(b.obj)
|
||||
|
||||
// cgo
|
||||
if len(d.CgoFiles) > 0 {
|
||||
outGo, outObj := b.cgo(d.CgoFiles)
|
||||
if len(info.CgoFiles) > 0 {
|
||||
cgoFiles := b.abss(info.CgoFiles...)
|
||||
s.addInput(cgoFiles...)
|
||||
outGo, outObj := b.cgo(cgoFiles)
|
||||
gofiles = append(gofiles, outGo...)
|
||||
ofiles = append(ofiles, outObj...)
|
||||
s.addIntermediate(outGo...)
|
||||
s.addIntermediate(outObj...)
|
||||
}
|
||||
|
||||
// compile
|
||||
@ -46,31 +60,130 @@ func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) {
|
||||
ofile := b.obj + "_go_." + b.arch
|
||||
b.gc(ofile, gofiles...)
|
||||
ofiles = append(ofiles, ofile)
|
||||
s.addIntermediate(ofile)
|
||||
}
|
||||
|
||||
// assemble
|
||||
for _, sfile := range d.SFiles {
|
||||
for _, sfile := range info.SFiles {
|
||||
ofile := b.obj + sfile[:len(sfile)-1] + b.arch
|
||||
sfile = b.abs(sfile)
|
||||
s.addInput(sfile)
|
||||
b.asm(ofile, sfile)
|
||||
ofiles = append(ofiles, ofile)
|
||||
s.addIntermediate(sfile, ofile)
|
||||
}
|
||||
|
||||
if len(ofiles) == 0 {
|
||||
return nil, os.NewError("make: no object files to build")
|
||||
}
|
||||
|
||||
if d.IsCommand() {
|
||||
// choose target file
|
||||
var targ string
|
||||
if info.IsCommand() {
|
||||
// use the last part of the import path as binary name
|
||||
_, bin := filepath.Split(pkg)
|
||||
targ = filepath.Join(tree.BinDir(), bin)
|
||||
} else {
|
||||
targ = filepath.Join(tree.PkgDir(), pkg+".a")
|
||||
}
|
||||
|
||||
// make target directory
|
||||
targDir, _ := filepath.Split(targ)
|
||||
b.mkdir(targDir)
|
||||
|
||||
// link binary or pack object
|
||||
if info.IsCommand() {
|
||||
b.ld(targ, ofiles...)
|
||||
} else {
|
||||
b.gopack(targ, ofiles...)
|
||||
}
|
||||
s.Output = append(s.Output, targ)
|
||||
|
||||
return b.cmds, nil
|
||||
return b.script, nil
|
||||
}
|
||||
|
||||
// A Script describes the build process for a Go package.
|
||||
// The Input, Intermediate, and Output fields are lists of absolute paths.
|
||||
type Script struct {
|
||||
Cmd []*Cmd
|
||||
Input []string
|
||||
Intermediate []string
|
||||
Output []string
|
||||
}
|
||||
|
||||
func (s *Script) addInput(file ...string) {
|
||||
s.Input = append(s.Input, file...)
|
||||
}
|
||||
|
||||
func (s *Script) addIntermediate(file ...string) {
|
||||
s.Intermediate = append(s.Intermediate, file...)
|
||||
}
|
||||
|
||||
// Run runs the Script's Cmds in order.
|
||||
func (s *Script) Run() os.Error {
|
||||
for _, c := range s.Cmd {
|
||||
if err := c.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stale returns true if the build's inputs are newer than its outputs.
|
||||
func (s *Script) Stale() bool {
|
||||
var latest int64
|
||||
// get latest mtime of outputs
|
||||
for _, file := range s.Output {
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
// any error reading output files means stale
|
||||
return true
|
||||
}
|
||||
if m := fi.Mtime_ns; m > latest {
|
||||
latest = m
|
||||
}
|
||||
}
|
||||
for _, file := range s.Input {
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil || fi.Mtime_ns > latest {
|
||||
// any error reading input files means stale
|
||||
// (attempt to rebuild to figure out why)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Clean removes the Script's Intermediate files.
|
||||
// It tries to remove every file and returns the first error it encounters.
|
||||
func (s *Script) Clean() (err os.Error) {
|
||||
for i := len(s.Intermediate) - 1; i >= 0; i-- {
|
||||
if e := os.Remove(s.Intermediate[i]); err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clean removes the Script's Intermediate and Output files.
|
||||
// It tries to remove every file and returns the first error it encounters.
|
||||
func (s *Script) Nuke() (err os.Error) {
|
||||
for i := len(s.Output) - 1; i >= 0; i-- {
|
||||
if e := os.Remove(s.Output[i]); err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
if e := s.Clean(); err == nil {
|
||||
err = e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// A Cmd describes an individual build command.
|
||||
type Cmd struct {
|
||||
Args []string // command-line
|
||||
Stdout string // write standard output to this file, "" is passthrough
|
||||
Dir string // working directory
|
||||
Input []string // file paths (dependencies)
|
||||
Output []string // file paths
|
||||
}
|
||||
@ -79,14 +192,15 @@ func (c *Cmd) String() string {
|
||||
return strings.Join(c.Args, " ")
|
||||
}
|
||||
|
||||
func (c *Cmd) Run(dir string) os.Error {
|
||||
// Run executes the Cmd.
|
||||
func (c *Cmd) Run() os.Error {
|
||||
out := new(bytes.Buffer)
|
||||
cmd := exec.Command(c.Args[0], c.Args[1:]...)
|
||||
cmd.Dir = dir
|
||||
cmd.Dir = c.Dir
|
||||
cmd.Stdout = out
|
||||
cmd.Stderr = out
|
||||
if c.Stdout != "" {
|
||||
f, err := os.Create(filepath.Join(dir, c.Stdout))
|
||||
f, err := os.Create(c.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -99,15 +213,6 @@ func (c *Cmd) Run(dir string) os.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cmd) Clean(dir string) (err os.Error) {
|
||||
for _, fn := range c.Output {
|
||||
if e := os.RemoveAll(fn); err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ArchChar returns the architecture character for the given goarch.
|
||||
// For example, ArchChar("amd64") returns "6".
|
||||
func ArchChar(goarch string) (string, os.Error) {
|
||||
@ -123,13 +228,29 @@ func ArchChar(goarch string) (string, os.Error) {
|
||||
}
|
||||
|
||||
type build struct {
|
||||
cmds []*Cmd
|
||||
script *Script
|
||||
path string
|
||||
obj string
|
||||
arch string
|
||||
}
|
||||
|
||||
func (b *build) abs(file string) string {
|
||||
if filepath.IsAbs(file) {
|
||||
return file
|
||||
}
|
||||
return filepath.Join(b.path, file)
|
||||
}
|
||||
|
||||
func (b *build) abss(file ...string) []string {
|
||||
s := make([]string, len(file))
|
||||
for i, f := range file {
|
||||
s[i] = b.abs(f)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (b *build) add(c Cmd) {
|
||||
b.cmds = append(b.cmds, &c)
|
||||
b.script.Cmd = append(b.script.Cmd, &c)
|
||||
}
|
||||
|
||||
func (b *build) mkdir(name string) {
|
||||
@ -222,6 +343,7 @@ func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
|
||||
gofiles := []string{b.obj + "_cgo_gotypes.go"}
|
||||
cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
|
||||
for _, fn := range cgofiles {
|
||||
fn = filepath.Base(fn)
|
||||
f := b.obj + fn[:len(fn)-2]
|
||||
gofiles = append(gofiles, f+"cgo1.go")
|
||||
cfiles = append(cfiles, f+"cgo2.c")
|
||||
|
@ -5,50 +5,46 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var buildDirs = []string{
|
||||
"pkg/path",
|
||||
"cmd/gofix",
|
||||
"pkg/big",
|
||||
"pkg/go/build/cgotest",
|
||||
// TODO(adg): test building binaries
|
||||
|
||||
var buildPkgs = []string{
|
||||
"path",
|
||||
"big",
|
||||
"go/build/cgotest",
|
||||
}
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
out, err := filepath.Abs("_test/out")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, d := range buildDirs {
|
||||
if runtime.GOARCH == "arm" && strings.Contains(d, "/cgo") {
|
||||
for _, pkg := range buildPkgs {
|
||||
if runtime.GOARCH == "arm" && strings.Contains(pkg, "/cgo") {
|
||||
// no cgo for arm, yet.
|
||||
continue
|
||||
}
|
||||
dir := filepath.Join(runtime.GOROOT(), "src", d)
|
||||
testBuild(t, dir, out)
|
||||
tree := Path[0] // Goroot
|
||||
testBuild(t, tree, pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func testBuild(t *testing.T, dir, targ string) {
|
||||
d, err := ScanDir(dir, true)
|
||||
func testBuild(t *testing.T, tree *Tree, pkg string) {
|
||||
dir := filepath.Join(tree.SrcDir(), pkg)
|
||||
info, err := ScanDir(dir, true)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(targ)
|
||||
cmds, err := d.Build(targ)
|
||||
s, err := Build(tree, pkg, info)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
for _, c := range cmds {
|
||||
for _, c := range s.Cmd {
|
||||
t.Log("Run:", c)
|
||||
err = c.Run(dir)
|
||||
err = c.Run()
|
||||
if err != nil {
|
||||
t.Error(c, err)
|
||||
return
|
||||
|
@ -50,7 +50,6 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
|
||||
|
||||
var di DirInfo
|
||||
imported := make(map[string]bool)
|
||||
pkgName := ""
|
||||
fset := token.NewFileSet()
|
||||
for i := range dirs {
|
||||
d := &dirs[i]
|
||||
@ -89,14 +88,14 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
|
||||
if s == "documentation" {
|
||||
continue
|
||||
}
|
||||
if pkgName == "" {
|
||||
pkgName = s
|
||||
} else if pkgName != s {
|
||||
if di.PkgName == "" {
|
||||
di.PkgName = s
|
||||
} else if di.PkgName != s {
|
||||
// Only if all files in the directory are in package main
|
||||
// do we return pkgName=="main".
|
||||
// do we return PkgName=="main".
|
||||
// A mix of main and another package reverts
|
||||
// to the original (allowMain=false) behaviour.
|
||||
if s == "main" || pkgName == "main" {
|
||||
if s == "main" || di.PkgName == "main" {
|
||||
return ScanDir(dir, false)
|
||||
}
|
||||
return nil, os.ErrorString("multiple package names in " + dir)
|
||||
|
Loading…
Reference in New Issue
Block a user