From d8be8494133a2dad17fc1285a954ee9f6d6df154 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 13 Aug 2013 19:18:59 +0800 Subject: [PATCH] Fixed code of command get --- README.md | 16 ++ beewatch.json | 9 + cmd/cmd.go | 21 +- cmd/gen.go | 14 + cmd/get.go | 111 ++++++-- cmd/source.go | 18 +- doc/utils.go | 88 ++++++ docs/{ => images}/arch.png | Bin features_CN.md | 159 ----------- gopm.go | 534 ++++--------------------------------- 10 files changed, 298 insertions(+), 672 deletions(-) create mode 100644 README.md create mode 100644 beewatch.json create mode 100644 doc/utils.go rename docs/{ => images}/arch.png (100%) delete mode 100644 features_CN.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..2ced87b5c --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +gopm - Go Package Manager +========================= + +![GPMGo_Logo](https://raw.github.com/gpmgo/gopmweb/master/static/img/gpmgo.png?raw=true) + +gopm(Go Package Manager) is a Go package manage tool for search, install, update and share packages in Go. + +**Attention** This application still in experiment, we'are working on new break version, you may use [old version](https://github.com/gpmgo/gopm/tree/v0.1.0) for now. + +## Credits + +- [garyburd/gddo](https://github.com/garyburd/gddo) + +## License + +[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). \ No newline at end of file diff --git a/beewatch.json b/beewatch.json new file mode 100644 index 000000000..b0d5a08aa --- /dev/null +++ b/beewatch.json @@ -0,0 +1,9 @@ +{ + "app_name": "Go Package Manager", + "http_port": 23456, + "watch_enabled": true, + "cmd_mode": true, + "skip_suspend": false, + "print_stack": true, + "print_source": true +} diff --git a/cmd/cmd.go b/cmd/cmd.go index 30fa463f3..bf4176e04 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,7 +1,20 @@ +// Copyright 2013 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + package cmd import ( - "flag" "fmt" "os" "strings" @@ -29,11 +42,7 @@ type Command struct { Long string // Flag is a set of flags specific to this command. - Flag flag.FlagSet - - // CustomFlags indicates that the command will do its own - // flag parsing. - CustomFlags bool + Flags map[string]bool } // Name returns the command's name: the first word in the usage line. diff --git a/cmd/gen.go b/cmd/gen.go index 90444330c..03c3d8a7a 100644 --- a/cmd/gen.go +++ b/cmd/gen.go @@ -1,3 +1,17 @@ +// Copyright 2013 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + package cmd var CmdGen = &Command{ diff --git a/cmd/get.go b/cmd/get.go index 9d598910f..0fa1e1733 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -1,3 +1,17 @@ +// Copyright 2013 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + package cmd import ( @@ -11,49 +25,108 @@ import ( "path" "path/filepath" "strings" + + "../doc" +) + +var ( + installGOPATH string // The GOPATH that packages are downloaded to. ) var CmdGet = &Command{ - UsageLine: "get [-u] [packages]", + UsageLine: "get [flags] ", Short: "download and install packages and dependencies", Long: ` Get downloads and installs the packages named by the import paths, along with their dependencies. -The -u flag instructs get to use the network to update the named packages -and their dependencies. By default, get uses the network to check out -missing packages but does not use it to look for updates to existing packages. +This command works even you haven't installed any version control tool +such as git, hg, etc. -Get also accepts all the flags in the 'go build' and 'go install' commands, -to control the installation. See 'go help build'. +The install flags are: -When checking out or updating a package, get looks for a branch or tag -that matches the locally installed version of Go. The most important -rule is that if the local installation is running version "go1", get -searches for a branch or tag named "go1". If no such version exists it -retrieves the most recent version of the package. + -d + download without installing package(s). + -u + force to update pakcage(s). + -e + download dependencies for example(s). -For more about specifying packages, see 'go help packages'. - -For more about how 'gopm get' finds source code to -download, see 'gopm help'. +The list flags accept a space-separated list of strings. -See also: gopm build, gopm install, gopm clean. +For more about specifying packages, see 'go help packages'. `, } -var getD = CmdGet.Flag.Bool("f", false, "") -var getU = CmdGet.Flag.Bool("u", false, "") - func init() { + downloadCache = make(map[string]bool) CmdGet.Run = runGet + CmdGet.Flags = map[string]bool{ + "-d": false, + "-u": false, + "-e": false, + } } func isStandalone() bool { return true } +// printGetPrompt prints prompt information to users to +// let them know what's going on. +func printGetPrompt(flag string) { + switch flag { + case "-d": + doc.ColorLog("[INFO] You enabled download without installing.\n") + case "-u": + doc.ColorLog("[INFO] You enabled force update.\n") + case "-e": + doc.ColorLog("[INFO] You enabled download dependencies of example(s).\n") + } +} + +// checkFlags checks if the flag exists with correct format. +func checkFlags(flags map[string]bool, args []string, print func(string)) int { + num := 0 // Number of valid flags, use to cut out. + for i, f := range args { + // Check flag prefix '-'. + if !strings.HasPrefix(f, "-") { + // Not a flag, finish check process. + break + } + + // Check if it a valid flag. + if v, ok := flags[f]; ok { + flags[f] = !v + if !v { + print(f) + } else { + fmt.Println("DISABLE: " + f) + } + } else { + doc.ColorLog("[ERRO] Unknown flag: %s.\n", f) + return -1 + } + num = i + 1 + } + + return num +} + func runGet(cmd *Command, args []string) { + // Check flags. + num := checkFlags(cmd.Flags, args, printGetPrompt) + if num == -1 { + return + } + args = args[num:] + + // Check length of arguments. + if len(args) < 1 { + doc.ColorLog("[ERRO] Please list the package that you want to install.\n") + return + } + if len(args) > 0 { var ver string = TRUNK if len(args) == 2 { diff --git a/cmd/source.go b/cmd/source.go index 89378c3d1..bfddb7320 100644 --- a/cmd/source.go +++ b/cmd/source.go @@ -1,3 +1,17 @@ +// Copyright 2013 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + package cmd import ( @@ -14,8 +28,8 @@ const ( ) var ( - downloadCache map[string]bool - sources []Source = []Source{ + downloadCache map[string]bool // Saves packages that have been downloaded. + sources []Source = []Source{ &GithubSource{}, } ) diff --git a/doc/utils.go b/doc/utils.go new file mode 100644 index 000000000..bf2f74524 --- /dev/null +++ b/doc/utils.go @@ -0,0 +1,88 @@ +// Copyright 2013 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package doc + +import ( + "fmt" + "runtime" + "strings" +) + +const ( + Gray = uint8(iota + 90) + Red + Green + Yellow + Blue + Magenta + //NRed = uint8(31) // Normal + EndColor = "\033[0m" +) + +// ColorLog colors log and print to stdout. +// Log format: [ error ]. +// Level: TRAC -> blue; ERRO -> red; WARN -> Magenta; SUCC -> green; others -> default. +// Content: default; path: yellow; error -> red. +// Level has to be surrounded by "[" and "]". +// Highlights have to be surrounded by "# " and " #"(space). +// Paths have to be surrounded by "( " and " )"(sapce). +// Errors have to be surrounded by "[ " and " ]"(space). +func ColorLog(format string, a ...interface{}) { + log := fmt.Sprintf(format, a...) + if runtime.GOOS != "windows" { + var clog string + + // Level. + i := strings.Index(log, "]") + if log[0] == '[' && i > -1 { + clog += "[" + getColorLevel(log[1:i]) + "]" + } + + log = log[i+1:] + + // Error. + log = strings.Replace(log, "[ ", fmt.Sprintf("[\033[%dm", Red), -1) + log = strings.Replace(log, " ]", EndColor+"]", -1) + + // Path. + log = strings.Replace(log, "( ", fmt.Sprintf("(\033[%dm", Yellow), -1) + log = strings.Replace(log, " )", EndColor+")", -1) + + // Highlights. + log = strings.Replace(log, "# ", fmt.Sprintf("\033[%dm", Gray), -1) + log = strings.Replace(log, " #", EndColor, -1) + + log = clog + log + } + + fmt.Print(log) +} + +// getColorLevel returns colored level string by given level. +func getColorLevel(level string) string { + level = strings.ToUpper(level) + switch level { + case "TRAC": + return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level) + case "ERRO": + return fmt.Sprintf("\033[%dm%s\033[0m", Red, level) + case "WARN": + return fmt.Sprintf("\033[%dm%s\033[0m", Magenta, level) + case "SUCC": + return fmt.Sprintf("\033[%dm%s\033[0m", Green, level) + default: + return level + } +} diff --git a/docs/arch.png b/docs/images/arch.png similarity index 100% rename from docs/arch.png rename to docs/images/arch.png diff --git a/features_CN.md b/features_CN.md deleted file mode 100644 index 88b010ac3..000000000 --- a/features_CN.md +++ /dev/null @@ -1,159 +0,0 @@ -gopm -==== - -* [总体设计目标](#10) -* [Go包版本说明](#20) -* [各命令的目标和作用](#30) - * [gopm help](#31) - * [gopm sources](#32) - * [gopm list](#33) - * [gopm get](#34) - * [gopm rm](#35) - * [gopm search](#36) - * [gopm doc](#37) - * [gopm serve](#38) - * [gopm sync](#39) - * [gopm import](#40) - * [gopm gen](#41) - * [gopm build](#42) - * [gopm run](#43) - * [gopm test](#44) -* [gopmspec文件格式](#50) - - -#总体设计目标 - -1. 支持go语言的版本管理 -2. 支持文档管理 -3. 支持本地源服务器 -4. 本地源服务器同时支持公共包和私有包 -5. 支持依赖管理 -6. 支持从github, code.google.com, gitLab, 等常见的源码托管服务下载 - - -#Go包版本说明 - -版本分为四种: - -* []: 表示的是当前最新版本即trunk -* branch: 表示的是某个分支 -* tag: 表示的是某个tag -* commit: 表示的是某个reversion - -#配置文件说明 - -默认没有配置文件,当系统第一次启动时检测homedir/.gopm/config,看是否存在,如果不存在则自动创建此配置文件。 -配置文件内容如下: -[sources] -http://gopm.io - -[repos] -~/.gopm/repos - - - -#各命令的目标和作用 - - -###gopm help - -显示当前可用的命令,以下命令中,[]表示可选,{}表示是参数 - - -###gopm sources [add|rm [{url}]] - -* [] 列出当前可用的所有源,默认为http://gopm.io/ -* add url 添加一个源到本地 -* rm url 删除一个源到本地,如果没有任何源,则自己成为一个独立的服务器,类似gopm.io - - -###gopm list [{packagename}[:{version}]] - -* [] 列出所有本地的包 -* packagename 显示指定名称的包的详细信息 - - -###gopm get [-u] [{packagename} [{version}]] [-f {gopmfile}] - -* [] 查找当前目录下的所有.gopmfile文件,根据文件的描述下载所有的包 -* packagename 从源中下载某个包 -* -u packagename 从源中更新某个包 -* -f gopmfile 根据指定的文件来下载包 - - -###gopm rm {packagename}[:{version}] - -去除一个包,如果不加版本标示,则删除该包的所有版本 - - -###gopm search {keyword} - -根据关键词查找包 - - -###gopm doc [-b] {packagename}[:{version}] - -* [] 显示一个包的文档 -* -b 在默认浏览器中显示该包的文档 - - -###gopm serve [-p {port}] - -将本地仓库作为服务对外提供,如果没有-p,则端口为80,如果有,则端口为指定端口,该服务是一个web服务,通过浏览器也可以进行浏览。 - - -###gopm sync [-u] - -[] 如果当前配置了源,则从可用的源中同步所有的包信息和包内容的最新版本到本地仓库; - 如果当前没有配置任何源,则将所有已有的包从源头进行更新 --u 仅更新本地仓库已经有的包,不包含本地仓库没有的包 - - -###gopm import [{url}|{filepath}] - -将某个地址或者本地的包导入到本地仓库中,url应为可支持的源码托管站点或者gitLab - - -###gopm gen [{gopmfile}] - -扫描当前目录下的go工程,并自动生成一个.gopmspec的文件依赖文档,如果未指定,则文件名为.gopmspec,如果指定了,则为指定的文件名 - - -###gopm build [-u] - -此命令依赖于go build - -1. 如果当前没有.gopmspec文件,则扫描当前的go工程的依赖,自动生成.gopmspec文档 -2. 根据.gopmspec文件自动下载所有需要的包,如果加了-u参数,则同时更新所有的包 -3. 根据.gopmspec文件自动切换gopath中的相关版本 -4. 调用go build对工程进行编译 - - -###gopm run [{gofile}] - -此命令依赖于go run - -调用gopm build在临时文件夹生成可执行文件,并设置程序当前目录为当前目录,并执行 - - -###gopm test - -此命令依赖于go test - -调用gopm build在临时文件夹生成可执行的测试文件,并设置程序当前目录为当前目录,并执行 - - -#gopmspec文件格式 - -.gopmspec文件的格式类似一个ini文件,当前分为两个section。 -build段内的依赖保存的是go build所需要依赖的所有包,一行一个,可用 =, >=等等,如果什么符号都没有,就是取最新版本 - -``` -[build] -xweb -beego = tag:0.1 -xorm >= branch:0.2 - -[test] -testing -``` diff --git a/gopm.go b/gopm.go index ae96d216b..b42af87ea 100644 --- a/gopm.go +++ b/gopm.go @@ -1,21 +1,24 @@ -// 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. - +// Copyright 2013 gopm authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// gopm(Go Package Manager) is a Go package manage tool for search, install, update and share packages in Go. package main import ( - "bytes" - "flag" "fmt" - "go/build" "io" - "log" "os" - "os/exec" - //"path" - "path/filepath" - "regexp" "runtime" "strings" "sync" @@ -23,7 +26,6 @@ import ( "unicode" "unicode/utf8" - //"github.com/gpmgo/gopm/cmd" "./cmd" ) @@ -31,13 +33,14 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true +const APP_VER = "0.1.0.0813" var ( config map[string]interface{} ) // Commands lists the available commands and help topics. -// The order here is the order in which they are printed by 'go help'. +// The order here is the order in which they are printed by 'gopm help'. var commands = []*cmd.Command{ cmd.CmdGet, /*cmd.CmdGen, @@ -62,69 +65,36 @@ var commands = []*cmd.Command{ helpTestfunc,*/ } -var exitStatus = 0 -var exitMu sync.Mutex - -func setExitStatus(n int) { - exitMu.Lock() - if exitStatus < n { - exitStatus = n - } - exitMu.Unlock() +// We don't use init() to initialize +// bacause we need to get execute path in runtime. +func initialize() bool { + runtime.GOMAXPROCS(runtime.NumCPU()) + return true } func main() { - _ = go11tag - flag.Usage = usage - flag.Parse() - log.SetFlags(0) + // Initialization. + if !initialize() { + return + } - args := flag.Args() + // Check length of arguments. + args := os.Args[1:] if len(args) < 1 { usage() + return } + // Show help documentation. if args[0] == "help" { help(args[1:]) return } - // Diagnose common mistake: GOPATH==GOROOT. - // This setting is equivalent to not setting GOPATH at all, - // which is not what most people want when they do it. - if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { - fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) - } else { - for _, p := range filepath.SplitList(gopath) { - // Note: using HasPrefix instead of Contains because a ~ can appear - // in the middle of directory elements, such as /tmp/git-1.8.2~rc3 - // or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell. - if strings.HasPrefix(p, "~") { - fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p) - os.Exit(2) - } - if build.IsLocalImport(p) { - fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) - os.Exit(2) - } - } - } - - /*if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() { - fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot) - os.Exit(2) - }*/ - - for _, cmd := range commands { - if cmd.Name() == args[0] && cmd.Run != nil { - cmd.Flag.Usage = func() { cmd.Usage() } - if cmd.CustomFlags { - args = args[1:] - } else { - cmd.Flag.Parse(args[1:]) - args = cmd.Flag.Args() - } - cmd.Run(cmd, args) + // Check commands and run. + for _, comm := range commands { + if comm.Name() == args[0] && comm.Run != nil { + comm.Run(comm, args[1:]) exit() return } @@ -135,7 +105,18 @@ func main() { exit() } -var usageTemplate = `Gopm is a tool for managing Go source code and versions. +var exitStatus = 0 +var exitMu sync.Mutex + +func setExitStatus(n int) { + exitMu.Lock() + if exitStatus < n { + exitStatus = n + } + exitMu.Unlock() +} + +var usageTemplate = `gopm is a package manage tool for Go programming language. Usage: @@ -160,29 +141,6 @@ var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}} {{end}}{{.Long | trim}} ` -var documentationTemplate = `// Copyright 2013 The Gopm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// DO NOT EDIT THIS FILE. GENERATED BY mkdoc.sh. -// Edit the documentation in other files and rerun mkdoc.sh to generate this one. - -/* -{{range .}}{{if .Short}}{{.Short | capitalize}} - -{{end}}{{if .Runnable}}Usage: - - gopm {{.UsageLine}} - -{{end}}{{.Long | trim}} - - -{{end}}*/ -package main - -// NOTE: cmdDoc is in fmt.go. -` - // tmpl executes the given template text on data, writing the result to w. func tmpl(w io.Writer, text string, data interface{}) { t := template.New("top") @@ -224,79 +182,18 @@ func help(args []string) { arg := args[0] - // 'go help documentation' generates doc.go. - if arg == "documentation" { - buf := new(bytes.Buffer) - printUsage(buf) - usage := &cmd.Command{Long: buf.String()} - tmpl(os.Stdout, documentationTemplate, append([]*cmd.Command{usage}, commands...)) - return - } - for _, cmd := range commands { if cmd.Name() == arg { tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'go help cmd'. + // not exit 2: succeeded at 'gopm help cmd'. return } } fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'gopm help'.\n", arg) - os.Exit(2) // failed at 'go help cmd' + os.Exit(2) // failed at 'gopm help cmd' } -// importPathsNoDotExpansion returns the import paths to use for the given -// command line, but it does no ... expansion. -/*func importPathsNoDotExpansion(args []string) []string { - if len(args) == 0 { - return []string{"."} - } - var out []string - for _, a := range args { - // Arguments are supposed to be import paths, but - // as a courtesy to Windows developers, rewrite \ to / - // in command-line arguments. Handles .\... and so on. - if filepath.Separator == '\\' { - a = strings.Replace(a, `\`, `/`, -1) - } - - // Put argument in canonical form, but preserve leading ./. - if strings.HasPrefix(a, "./") { - a = "./" + path.Clean(a) - if a == "./." { - a = "." - } - } else { - a = path.Clean(a) - } - if a == "all" || a == "std" { - out = append(out, allPackages(a)...) - continue - } - out = append(out, a) - } - return out -}*/ - -/* -// importPaths returns the import paths to use for the given command line. -func importPaths(args []string) []string { - args = importPathsNoDotExpansion(args) - var out []string - for _, a := range args { - if strings.Contains(a, "...") { - if build.IsLocalImport(a) { - out = append(out, allPackagesInFS(a)...) - } else { - out = append(out, allPackages(a)...) - } - continue - } - out = append(out, a) - } - return out -}*/ - var atexitFuncs []func() func atexit(f func()) { @@ -309,338 +206,3 @@ func exit() { } os.Exit(exitStatus) } - -func fatalf(format string, args ...interface{}) { - errorf(format, args...) - exit() -} - -func errorf(format string, args ...interface{}) { - log.Printf(format, args...) - setExitStatus(1) -} - -var logf = log.Printf - -func exitIfErrors() { - if exitStatus != 0 { - exit() - } -} - -func run(cmdargs ...interface{}) { - cmdline := stringList(cmdargs...) - /*if buildN || buildV { - fmt.Printf("%s\n", strings.Join(cmdline, " ")) - if buildN { - return - } - }*/ - - cmd := exec.Command(cmdline[0], cmdline[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - errorf("%v", err) - } -} - -func runOut(dir string, cmdargs ...interface{}) []byte { - cmdline := stringList(cmdargs...) - cmd := exec.Command(cmdline[0], cmdline[1:]...) - cmd.Dir = dir - out, err := cmd.CombinedOutput() - if err != nil { - os.Stderr.Write(out) - errorf("%v", err) - out = nil - } - return out -} - -// envForDir returns a copy of the environment -// suitable for running in the given directory. -// The environment is the current process's environment -// but with an updated $PWD, so that an os.Getwd in the -// child will be faster. -func envForDir(dir string) []string { - env := os.Environ() - // Internally we only use rooted paths, so dir is rooted. - // Even if dir is not rooted, no harm done. - return mergeEnvLists([]string{"PWD=" + dir}, env) -} - -// mergeEnvLists merges the two environment lists such that -// variables with the same name in "in" replace those in "out". -func mergeEnvLists(in, out []string) []string { -NextVar: - for _, inkv := range in { - k := strings.SplitAfterN(inkv, "=", 2)[0] - for i, outkv := range out { - if strings.HasPrefix(outkv, k) { - out[i] = inkv - continue NextVar - } - } - out = append(out, inkv) - } - return out -} - -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -func matchPattern(pattern string) func(name string) bool { - re := regexp.QuoteMeta(pattern) - re = strings.Replace(re, `\.\.\.`, `.*`, -1) - // Special case: foo/... matches foo too. - if strings.HasSuffix(re, `/.*`) { - re = re[:len(re)-len(`/.*`)] + `(/.*)?` - } - reg := regexp.MustCompile(`^` + re + `$`) - return func(name string) bool { - return reg.MatchString(name) - } -} - -// allPackages returns all the packages that can be found -// under the $GOPATH directories and $GOROOT matching pattern. -// The pattern is either "all" (all packages), "std" (standard packages) -// or a path including "...". -/*func allPackages(pattern string) []string { - pkgs := matchPackages(pattern) - if len(pkgs) == 0 { - fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) - } - return pkgs -}*/ - -/*func matchPackages(pattern string) []string { - match := func(string) bool { return true } - if pattern != "all" && pattern != "std" { - match = matchPattern(pattern) - } - - have := map[string]bool{ - "builtin": true, // ignore pseudo-package that exists only for documentation - } - //if !buildContext.CgoEnabled { - have["runtime/cgo"] = true // ignore during walk - //} - var pkgs []string - - // Commands - cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) - filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() || path == cmd { - return nil - } - name := path[len(cmd):] - // Commands are all in cmd/, not in subdirectories. - if strings.Contains(name, string(filepath.Separator)) { - return filepath.SkipDir - } - - // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. - name = "cmd/" + name - if have[name] { - return nil - } - have[name] = true - if !match(name) { - return nil - } - _, err = buildContext.ImportDir(path, 0) - if err != nil { - if _, noGo := err.(*build.NoGoError); !noGo { - log.Print(err) - } - return nil - } - pkgs = append(pkgs, name) - return nil - }) - - for _, src := range buildContext.SrcDirs() { - if pattern == "std" && src != gorootSrcPkg { - continue - } - src = filepath.Clean(src) + string(filepath.Separator) - filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() || path == src { - return nil - } - - // Avoid .foo, _foo, and testdata directory trees. - _, elem := filepath.Split(path) - if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { - return filepath.SkipDir - } - - name := filepath.ToSlash(path[len(src):]) - if pattern == "std" && strings.Contains(name, ".") { - return filepath.SkipDir - } - if have[name] { - return nil - } - have[name] = true - if !match(name) { - return nil - } - _, err = buildContext.ImportDir(path, 0) - if err != nil { - if _, noGo := err.(*build.NoGoError); noGo { - return nil - } - } - pkgs = append(pkgs, name) - return nil - }) - } - return pkgs -} - -// allPackagesInFS is like allPackages but is passed a pattern -// beginning ./ or ../, meaning it should scan the tree rooted -// at the given directory. There are ... in the pattern too. -func allPackagesInFS(pattern string) []string { - pkgs := matchPackagesInFS(pattern) - if len(pkgs) == 0 { - fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) - } - return pkgs -} - -func matchPackagesInFS(pattern string) []string { - // Find directory to begin the scan. - // Could be smarter but this one optimization - // is enough for now, since ... is usually at the - // end of a path. - i := strings.Index(pattern, "...") - dir, _ := path.Split(pattern[:i]) - - // pattern begins with ./ or ../. - // path.Clean will discard the ./ but not the ../. - // We need to preserve the ./ for pattern matching - // and in the returned import paths. - prefix := "" - if strings.HasPrefix(pattern, "./") { - prefix = "./" - } - match := matchPattern(pattern) - - var pkgs []string - filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() { - return nil - } - if path == dir { - // filepath.Walk starts at dir and recurses. For the recursive case, - // the path is the result of filepath.Join, which calls filepath.Clean. - // The initial case is not Cleaned, though, so we do this explicitly. - // - // This converts a path like "./io/" to "io". Without this step, running - // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io - // package, because prepending the prefix "./" to the unclean path would - // result in "././io", and match("././io") returns false. - path = filepath.Clean(path) - } - - // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". - _, elem := filepath.Split(path) - dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." - if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { - return filepath.SkipDir - } - - name := prefix + filepath.ToSlash(path) - if !match(name) { - return nil - } - if _, err = build.ImportDir(path, 0); err != nil { - if _, noGo := err.(*build.NoGoError); !noGo { - log.Print(err) - } - return nil - } - pkgs = append(pkgs, name) - return nil - }) - return pkgs -}*/ - -// stringList's arguments should be a sequence of string or []string values. -// stringList flattens them into a single []string. -func stringList(args ...interface{}) []string { - var x []string - for _, arg := range args { - switch arg := arg.(type) { - case []string: - x = append(x, arg...) - case string: - x = append(x, arg) - default: - panic("stringList: invalid argument") - } - } - return x -} - -// toFold returns a string with the property that -// strings.EqualFold(s, t) iff toFold(s) == toFold(t) -// This lets us test a large set of strings for fold-equivalent -// duplicates without making a quadratic number of calls -// to EqualFold. Note that strings.ToUpper and strings.ToLower -// have the desired property in some corner cases. -func toFold(s string) string { - // Fast path: all ASCII, no upper case. - // Most paths look like this already. - for i := 0; i < len(s); i++ { - c := s[i] - if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { - goto Slow - } - } - return s - -Slow: - var buf bytes.Buffer - for _, r := range s { - // SimpleFold(x) cycles to the next equivalent rune > x - // or wraps around to smaller values. Iterate until it wraps, - // and we've found the minimum value. - for { - r0 := r - r = unicode.SimpleFold(r0) - if r <= r0 { - break - } - } - // Exception to allow fast path above: A-Z => a-z - if 'A' <= r && r <= 'Z' { - r += 'a' - 'A' - } - buf.WriteRune(r) - } - return buf.String() -} - -// foldDup reports a pair of strings from the list that are -// equal according to strings.EqualFold. -// It returns "", "" if there are no such strings. -func foldDup(list []string) (string, string) { - clash := map[string]string{} - for _, s := range list { - fold := toFold(s) - if t := clash[fold]; t != "" { - if s > t { - s, t = t, s - } - return s, t - } - clash[fold] = s - } - return "", "" -}