goinstall: build with make by default, add -make flag

This is a temporary measure until go/build can build cgo packages.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/4627056
diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile
index 399d294..f61354f 100644
--- a/src/cmd/goinstall/Makefile
+++ b/src/cmd/goinstall/Makefile
@@ -8,5 +8,6 @@
 GOFILES=\
 	download.go\
 	main.go\
+	make.go\
 
 include ../../Make.cmd
diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go
index c4383aa..bdf8469 100644
--- a/src/cmd/goinstall/main.go
+++ b/src/cmd/goinstall/main.go
@@ -41,6 +41,7 @@
 	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")
+	useMake           = flag.Bool("make", true, "use make to build and install")
 	verbose           = flag.Bool("v", false, "verbose")
 )
 
@@ -211,27 +212,35 @@
 	}
 
 	// Install this package.
-	script, err := build.Build(tree, pkg, dirInfo)
-	if err != nil {
-		errorf("%s: install: %v\n", pkg, err)
-		return
-	}
-	if *nuke {
-		printf("%s: nuke\n", pkg)
-		script.Nuke()
-	} else if *clean {
-		printf("%s: clean\n", pkg)
-		script.Clean()
-	}
-	if *doInstall {
-		if script.Stale() {
-			printf("%s: install\n", pkg)
-			if err := script.Run(); err != nil {
-				errorf("%s: install: %v\n", pkg, err)
-				return
+	if *useMake {
+		err := domake(dir, pkg, tree, dirInfo.IsCommand())
+		if err != nil {
+			errorf("%s: install: %v\n", pkg, err)
+			return
+		}
+	} else {
+		script, err := build.Build(tree, pkg, dirInfo)
+		if err != nil {
+			errorf("%s: install: %v\n", pkg, err)
+			return
+		}
+		if *nuke {
+			printf("%s: nuke\n", pkg)
+			script.Nuke()
+		} else if *clean {
+			printf("%s: clean\n", pkg)
+			script.Clean()
+		}
+		if *doInstall {
+			if script.Stale() {
+				printf("%s: install\n", pkg)
+				if err := script.Run(); err != nil {
+					errorf("%s: install: %v\n", pkg, err)
+					return
+				}
+			} else {
+				printf("%s: up-to-date\n", pkg)
 			}
-		} else {
-			printf("%s: up-to-date\n", pkg)
 		}
 	}
 	if remote {
diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go
new file mode 100644
index 0000000..0fd9b02
--- /dev/null
+++ b/src/cmd/goinstall/make.go
@@ -0,0 +1,180 @@
+// 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.
+
+// Run "make install" to build package.
+
+package main
+
+import (
+	"bytes"
+	"go/build"
+	"os"
+	"path/filepath"
+	"strings"
+	"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, tree *build.Tree, isCmd bool) (err os.Error) {
+	makefile, err := makeMakefile(dir, pkg, tree, isCmd)
+	if err != nil {
+		return err
+	}
+	cmd := []string{"bash", "gomake", "-f-"}
+	if *nuke {
+		cmd = append(cmd, "nuke")
+	} else 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, tree *build.Tree, isCmd bool) ([]byte, os.Error) {
+	if !safeName(pkg) {
+		return nil, os.NewError("unsafe name: " + pkg)
+	}
+	targ := pkg
+	targDir := tree.PkgDir()
+	if isCmd {
+		// use the last part of the package name for targ
+		_, targ = filepath.Split(pkg)
+		targDir = tree.BinDir()
+	}
+	dirInfo, err := build.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.NewError("bad name: " + file)
+		}
+		isCgo[file] = true
+	}
+
+	goFiles := make([]string, 0, len(dirInfo.GoFiles))
+	for _, file := range dirInfo.GoFiles {
+		if !safeName(file) {
+			return nil, os.NewError("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.NewError("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.NewError("unsafe name: " + file)
+		}
+		oFiles = append(oFiles, file[:len(file)-2]+".$O")
+	}
+
+	var imports []string
+	for _, t := range build.Path {
+		imports = append(imports, t.PkgDir())
+	}
+
+	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
+	}
+	if strings.Contains(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 @}
+	{@}\
+{.end}
+
+{.end}
+{.section OFiles}
+OFILES=\
+{.repeated section @}
+	{@}\
+{.end}
+
+{.end}
+{.section CgoFiles}
+CGOFILES=\
+{.repeated section @}
+	{@}\
+{.end}
+
+{.end}
+{.section CgoOFiles}
+CGO_OFILES=\
+{.repeated section @}
+	{@}\
+{.end}
+
+{.end}
+GCIMPORTS={.repeated section Imports}-I "{@}" {.end}
+LDIMPORTS={.repeated section Imports}-L "{@}" {.end}
+
+include $(GOROOT)/src/Make.{Type}
+`,
+	nil)