// 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)