diff --git a/README.md b/README.md index 1fbf8532f..16d0d1912 100644 --- a/README.md +++ b/README.md @@ -5,30 +5,7 @@ gopm - Go Package Manager Gopm(Go Package Manager) is a Go package manage tool for search, install, update and share packages in Go. -Current Version: **v0.5.5** - -# Requirement - -- Go Development Environment >= 1.1. -- Command `ln -s` support on Mac OS and Unix-like systems. -- Command `mklink -j` support on Windows( **Windows Vista and later** ). - -# Installation - -Because we do NOT offer binaries for now, so before you install the gopm, you should have already installed Go Development Environment with version 1.1 and later. - -``` -go get github.com/gpmgo/gopm -``` - -The executable will be produced under `$GOPATH/bin` in your file system; for global use purpose, we recommand you to add this path into your `PATH` environment variable. - -# Features - -- No requirement for installing any version control system tool like `git`, `svn` or `hg` in order to download packages(although you have to install git for installing gopm though `go get` for now). -- Download, install or build your packages with specific revisions. -- When build program with `gopm build` or `gopm install`, everything just happen in its own GOPATH and do not bother anything you've done. -* Put your Go project on anywhere you want. +**[Documentation](https://github.com/gpmgo/docs)** # Commands @@ -40,13 +17,14 @@ USAGE: gopm [global options] command [command options] [arguments...] VERSION: - 0.5.5.1111 + 0.5.6.1130 COMMANDS: get fetch remote package(s) and dependencies to local repository + bin download and link dependencies and build executable binary gen generate a gopmfile according current go project run link dependencies and go run - build link dependencies and go build + build link dependencies and go build install link dependencies and go install help, h Shows a list of commands or help for one command diff --git a/cmd/bin.go b/cmd/bin.go new file mode 100644 index 000000000..9bee3d772 --- /dev/null +++ b/cmd/bin.go @@ -0,0 +1,153 @@ +// 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 ( + "os" + "path" + "strings" + + "github.com/Unknwon/com" + "github.com/codegangsta/cli" + + "github.com/gpmgo/gopm/doc" + "github.com/gpmgo/gopm/log" +) + +var CmdBin = cli.Command{ + Name: "bin", + Usage: "download and link dependencies and build executable binary", + Description: `Command bin downloads and links dependencies according to gopmfile, +and build executable binary to work directory + +gopm bin @[:] +gopm bin @[:] + +Can only specify one each time, and only works for projects that +contains main package`, + Action: runBin, + Flags: []cli.Flag{ + cli.BoolFlag{"dir", "build binary to given directory(second argument)"}, + }, +} + +func runBin(ctx *cli.Context) { + if len(ctx.Args()) == 0 { + log.Error("Bin", "Fail to start command") + log.Fatal("", "No package specified") + } + + doc.LoadPkgNameList(doc.HomeDir + "/data/pkgname.list") + + installRepoPath = doc.HomeDir + "/repos" + + // Check arguments. + num := 1 + if ctx.Bool("dir") { + num = 2 + } + if len(ctx.Args()) != num { + log.Error("Bin", "Fail to start command") + log.Fatal("", "Invalid argument number") + } + + // Check if given directory exists. + if ctx.Bool("dir") && !com.IsDir(ctx.Args()[1]) { + log.Error("Bin", "Fail to start command") + log.Fatal("", "Given directory does not exist") + } + + // Parse package version. + info := ctx.Args()[0] + pkgName := info + ver := "" + var err error + if i := strings.Index(info, "@"); i > -1 { + pkgName = info[:i] + _, ver, err = validPath(info[i+1:]) + if err != nil { + log.Error("Bin", "Fail to parse version") + log.Fatal("", err.Error()) + } + } + + // Check package name. + if !strings.Contains(pkgName, "/") { + name, ok := doc.PackageNameList[pkgName] + if !ok { + log.Error("Bin", "Invalid package name: "+pkgName) + log.Fatal("", "No match in the package name list") + } + pkgName = name + } + + // Get code. + com.ExecCmd("gopm", "get", ctx.Args()[0]) + + // Check if previous steps were successful. + pkgPath := installRepoPath + "/" + pkgName + if len(ver) > 0 { + pkgPath += "." + ver + } + if !com.IsDir(pkgPath) { + log.Error("Bin", "Fail to continue command") + log.Fatal("", "Previous steps weren't successful") + } + + wd, err := os.Getwd() + if err != nil { + log.Error("Bin", "Fail to get work directory") + log.Fatal("", err.Error()) + } + + // Change to repository path. + log.Log("Changing work directory to %s", pkgPath) + err = os.Chdir(pkgPath) + if err != nil { + log.Error("Bin", "Fail to change work directory") + log.Fatal("", err.Error()) + } + + // Build application. + com.ExecCmd("gopm", "build") + defer func() { + // Clean files. + os.RemoveAll(pkgPath + "/vendor") + }() + + // Check if previous steps were successful. + binName := path.Base(pkgName) + if !com.IsFile(binName) { + log.Error("Bin", "Fail to continue command") + log.Fatal("", "Previous steps weren't successful or the project does not contain main package") + } + + // Move binary to given directory. + movePath := wd + if ctx.Bool("dir") { + movePath = ctx.Args()[1] + } + _, err = com.Move(binName, movePath+"/"+binName) + if err != nil { + log.Error("Bin", "Fail to move binary") + log.Fatal("", err.Error()) + } + os.Chmod(movePath+"/"+binName, os.ModePerm) + + log.Log("Changing work directory back to %s", wd) + os.Chdir(wd) + + log.Success("SUCC", "Bin", "Command execute successfully!") +} diff --git a/cmd/build.go b/cmd/build.go index aa4690cbb..bdb763238 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -15,8 +15,11 @@ package cmd import ( + //"os" + "github.com/codegangsta/cli" + "github.com/gpmgo/gopm/doc" "github.com/gpmgo/gopm/log" ) @@ -31,6 +34,7 @@ gopm build `, } func runBuild(ctx *cli.Context) { + doc.LoadLocalNodes() genNewGoPath(ctx, false) log.Trace("Building...") diff --git a/cmd/gen.go b/cmd/gen.go index 93710280c..a8b222dcc 100644 --- a/cmd/gen.go +++ b/cmd/gen.go @@ -27,7 +27,7 @@ import ( var CmdGen = cli.Command{ Name: "gen", - Usage: "generate a gopmfile according current go project", + Usage: "generate a gopmfile according current Go project", Description: `Command gen gets dependencies and generates a gopmfile gopm gen @@ -35,7 +35,7 @@ gopm gen Make sure you run this command in the root path of a go project.`, Action: runGen, Flags: []cli.Flag{ - cli.BoolFlag{"example", "download dependencies for example(s)"}, + cli.BoolFlag{"example", "check dependencies for example(s)"}, }, } diff --git a/cmd/get.go b/cmd/get.go index 33875f8a6..a9c0cc087 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "os" + "path" "path/filepath" "strings" @@ -31,6 +32,7 @@ import ( var ( installRepoPath string + installGopath string downloadCache map[string]bool // Saves packages that have been downloaded. downloadCount int failConut int @@ -51,6 +53,7 @@ Can specify one or more: gopm get beego@tag:v0.9.0 github.com/beego/bee If no argument is supplied, then gopmfile must be present`, Action: runGet, Flags: []cli.Flag{ + cli.BoolFlag{"gopath", "download package(s) to GOPATH"}, cli.BoolFlag{"force", "force to update pakcage(s) and dependencies"}, cli.BoolFlag{"example", "download dependencies for example(s)"}, }, @@ -61,15 +64,19 @@ func init() { } func runGet(ctx *cli.Context) { - hd, err := com.HomeDir() - if err != nil { - log.Error("get", "Fail to get current user") - log.Fatal("", err.Error()) - } - - doc.HomeDir = strings.Replace(doc.RawHomeDir, "~", hd, -1) doc.LoadPkgNameList(doc.HomeDir + "/data/pkgname.list") + if ctx.Bool("gopath") { + installGopath = com.GetGOPATHs()[0] + if !com.IsDir(installGopath) { + log.Error("Get", "Fail to start command") + log.Fatal("", "GOPATH does not exist: "+installGopath) + } + log.Log("Indicate GOPATH: %s", installGopath) + + installGopath += "/src" + } + installRepoPath = doc.HomeDir + "/repos" log.Log("Local repository path: %s", installRepoPath) @@ -80,6 +87,7 @@ func runGet(ctx *cli.Context) { default: getByPath(ctx) } + } func getByGopmfile(ctx *cli.Context) { @@ -134,27 +142,27 @@ func getByGopmfile(ctx *cli.Context) { func getByPath(ctx *cli.Context) { nodes := make([]*doc.Node, 0, len(ctx.Args())) for _, info := range ctx.Args() { - pkg := info - node := doc.NewNode(pkg, pkg, doc.BRANCH, "", true) + pkgName := info + node := doc.NewNode(pkgName, pkgName, doc.BRANCH, "", true) if i := strings.Index(info, "@"); i > -1 { - pkg = info[:i] + pkgName = info[:i] tp, ver, err := validPath(info[i+1:]) if err != nil { - log.Error("", "Fail to parse version") + log.Error("Get", "Fail to parse version") log.Fatal("", err.Error()) } - node = doc.NewNode(pkg, pkg, tp, ver, true) + node = doc.NewNode(pkgName, pkgName, tp, ver, true) } - // Cheeck package name. - if !strings.Contains(pkg, "/") { - name, ok := doc.PackageNameList[pkg] + // Check package name. + if !strings.Contains(pkgName, "/") { + name, ok := doc.PackageNameList[pkgName] if !ok { - log.Error("", "Invalid package name: "+pkg) + log.Error("Get", "Invalid package name: "+pkgName) log.Fatal("", "No match in the package name list") } - pkg = name + pkgName = name } nodes = append(nodes, node) @@ -174,6 +182,16 @@ func getByPath(ctx *cli.Context) { downloadCount, failConut) } +func copyToGopath(srcPath, destPath string) { + fmt.Println(destPath) + os.RemoveAll(destPath) + err := com.CopyDir(srcPath, destPath) + if err != nil { + log.Error("Download", "Fail to copy to GOPATH") + log.Fatal("", err.Error()) + } +} + // downloadPackages downloads packages with certain commit, // if the commit is empty string, then it downloads all dependencies, // otherwise, it only downloada package with specific commit only. @@ -182,7 +200,8 @@ func downloadPackages(ctx *cli.Context, nodes []*doc.Node) { for _, n := range nodes { // Check if it is a valid remote path. if doc.IsValidRemotePath(n.ImportPath) { - installPath := installRepoPath + "/" + doc.GetProjectPath(n.ImportPath) + gopathDir := path.Join(installGopath, n.ImportPath) + installPath := path.Join(installRepoPath, doc.GetProjectPath(n.ImportPath)) if len(n.Value) > 0 { installPath += "." + n.Value } @@ -192,8 +211,14 @@ func downloadPackages(ctx *cli.Context, nodes []*doc.Node) { if com.IsExist(installPath) { log.Trace("Skipped installed package: %s@%s:%s", n.ImportPath, n.Type, doc.CheckNodeValue(n.Value)) + + if ctx.Bool("gopath") { + copyToGopath(installPath, gopathDir) + } continue } + } else if !com.IsExist(installPath) { + doc.LocalNodes.SetValue(doc.GetProjectPath(n.ImportPath), "value", "") } if !downloadCache[n.ImportPath] { @@ -244,7 +269,11 @@ func downloadPackages(ctx *cli.Context, nodes []*doc.Node) { // Only save non-commit node. if len(nod.Value) == 0 && len(nod.Revision) > 0 { - doc.LocalNodes.SetValue(nod.ImportPath, "value", nod.Revision) + doc.LocalNodes.SetValue(doc.GetProjectPath(nod.ImportPath), "value", nod.Revision) + } + + if ctx.Bool("gopath") { + copyToGopath(installPath, gopathDir) } } } else { @@ -269,7 +298,7 @@ func downloadPackage(ctx *cli.Context, nod *doc.Node) (*doc.Node, []string) { // Mark as donwloaded. downloadCache[nod.ImportPath] = true - nod.Revision = doc.LocalNodes.MustValue(nod.ImportPath, "value") + nod.Revision = doc.LocalNodes.MustValue(doc.GetProjectPath(nod.ImportPath), "value") imports, err := doc.PureDownload(nod, installRepoPath, ctx) //CmdGet.Flags) if err != nil { diff --git a/cmd/gopath.go b/cmd/gopath.go index eff4975e7..b5a1b264b 100644 --- a/cmd/gopath.go +++ b/cmd/gopath.go @@ -49,10 +49,16 @@ func getGopmPkgs(dirPath string, isTest bool) (pkgs map[string]*doc.Pkg, err err } if !doc.IsGoRepoPath(name) { if builds != nil { - if dep, ok := builds[name]; ok { - // TODO: need version - pkgs[name] = &doc.Pkg{ImportPath: dep} - continue + if info, ok := builds[name]; ok { + // Check version. + if i := strings.Index(info, ":"); i > -1 { + pkgs[name] = &doc.Pkg{ + ImportPath: name, + Type: info[:i], + Value: info[i+1:], + } + continue + } } } pkgs[name] = doc.NewDefaultPkg(name) @@ -82,16 +88,21 @@ func getChildPkgs(ctx *cli.Context, cpath string, ppkg *doc.Pkg, cachePkgs map[s if !pkgInCache(name, cachePkgs) { var newPath string if !build.IsLocalImport(name) { + + suf := "." + pkg.Value + if len(suf) == 1 { + suf = "" + } newPath = filepath.Join(installRepoPath, pkg.ImportPath) if pkgName != "" && strings.HasPrefix(pkg.ImportPath, pkgName) { - newPath = filepath.Join(curPath, pkg.ImportPath[len(pkgName)+1:]) + newPath = filepath.Join(curPath, pkg.ImportPath[len(pkgName)+1:]+suf) } else { - if !com.IsExist(newPath) { - var t, ver string = doc.BRANCH, "" - node := doc.NewNode(pkg.ImportPath, pkg.ImportPath, t, ver, true) + if !com.IsExist(newPath + suf) { + node := doc.NewNode(pkg.ImportPath, pkg.ImportPath, + pkg.Type, pkg.Value, true) nodes := []*doc.Node{node} downloadPackages(ctx, nodes) - // should handler download failed + // TODO: Should handler download failed } } } else { @@ -214,8 +225,13 @@ func genNewGoPath(ctx *cli.Context, isTest bool) { os.RemoveAll(newGoPathSrc) os.MkdirAll(newGoPathSrc, os.ModePerm) - for name, _ := range cachePkgs { - oldPath := filepath.Join(installRepoPath, name) + for name, pkg := range cachePkgs { + suf := "." + pkg.Value + if len(suf) == 1 { + suf = "" + } + + oldPath := filepath.Join(installRepoPath, name) + suf newPath := filepath.Join(newGoPathSrc, name) paths := strings.Split(name, "/") var isExistP bool @@ -236,7 +252,7 @@ func genNewGoPath(ctx *cli.Context, isTest bool) { } if !isExistP { - log.Log("Linking %s", name) + log.Log("Linking %s", name+suf) err = autoLink(oldPath, newPath) if err != nil { log.Error("", "Fail to make link") diff --git a/doc/conf.go b/doc/conf.go index a20e00f28..fbc6dc783 100644 --- a/doc/conf.go +++ b/doc/conf.go @@ -35,6 +35,16 @@ var ( LocalNodes *goconfig.ConfigFile ) +func init() { + hd, err := com.HomeDir() + if err != nil { + log.Error("", "Fail to get current user") + log.Fatal("", err.Error()) + } + + HomeDir = strings.Replace(RawHomeDir, "~", hd, -1) +} + func NewGopmfile(dirPath string) *goconfig.ConfigFile { gf, err := goconfig.LoadConfigFile(dirPath + "/" + GopmFileName) if err != nil { diff --git a/docs/features_CN.md b/docs/features_CN.md deleted file mode 100644 index dd11e6c81..000000000 --- a/docs/features_CN.md +++ /dev/null @@ -1,199 +0,0 @@ -gopm -==== - -* [总体设计目标](#10) -* [程序结构](#11) -* [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, 等常见的源码托管服务下载 - - -# 最终程序只有一个,但是通过配置,可以有三种模式: - -1 独立服务器 -2 子服务器 -3 客户端(默认) - -## 独立服务器 - -独立服务器就是本身的包都是直接从源服务器中获取的。 - -## 子服务器 - -子服务器就是包是从所配置的独立服务器上获取的,而不是直接从github等源服务器获取,在一个局域网中,可以通过架设子服务器来加快包的分发。 - -## 客户端 - -默认下载即为客户端模式,客户端默认是从源服务器获取包,如果要从包服务器获取包,则可在配置文件中通过配置即可。 - - -#Go包版本说明 - -版本分为四种: - -* []: 表示的是当前最新版本即trunk -* branch: 表示的是某个分支 -* tag: 表示的是某个tag -* commit: 表示的是某个reversion - -#配置文件说明 - -默认没有配置文件,当系统第一次启动时检测homedir/.gopm/config,看是否存在,如果不存在则自动创建此配置文件。 -配置文件内容如下: -[sources] -http://gopm.io - -[repos] -~/.gopm/repos - -#数据库说明 -包信息数据采用goleveldb,这是一个key/value数据库。数据库存默认放在~/.gopm/repos下。数据存放规则如下: - -* "lastId" : "{lastId}" lastId中存放最大的Id,Id为自增 - -* "index:{packageName}": "{id}" index:中存放的是包名,value中存放的是这个包的不同版本的id,不同版本用逗号分隔 - -* “pkg:{id}” : "{pkg}" 某个包的名称 - -* “ver:{id}” : "{verString1}, {verString2}" 某个包版本对应的内容 - -* "desc:{id}" : "{desc}" 某个包的最新版本的描述 - -* "down:{id}" : "{down}" 某个包的下载url - -* "deps:{id}" : "{deps}" 某个包的最新版本的描述 - -* “key:{keyword}:{id}” : "" 关键词及其对应的版本 - -* “total” :"{total}" 包总数 - - -#各命令的目标和作用 - - -###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 [-e] {keyword} - -根据关键词查找包名或者包的描述,如果有-e开关,则完全匹配包名 - - -###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工程,并自动生成一个.gopmfile的文件依赖文档,如果未指定,则文件名为.gopmfile,如果指定了,则为指定的文件名 - - -###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/docs/images/arch.png b/docs/images/arch.png deleted file mode 100644 index 357deed35..000000000 Binary files a/docs/images/arch.png and /dev/null differ diff --git a/docs/usage.md b/docs/usage.md deleted file mode 100644 index 98f0fe0cc..000000000 --- a/docs/usage.md +++ /dev/null @@ -1,23 +0,0 @@ -gopm -==== - -gopm(Go Package Manager) is a Go package manage tool for search, install, update, share packages in Go. - -usage: - -gopm help show this document -gopm sources list all package source servers or add or rm a source -gopm list list all packages local or list all versions of a package -gopm get get a package or according to a gopmfile -gopm upgrade upgrade a package or all packages and gopm self -gopm rm remove a package -gopm search search a package according keywords -gopm doc show a package's document on console or web browser -gopm serve run as a package source server -gopm sync sync all packages from first avilable source server to local -gopm import import a package into local -gopm gen generate a .gopmspec file according current dir's source codes -gopm build build project according to gopmfile -gopm run build project according to gopmfile and run -gopm test test project like go test - diff --git a/gopm.go b/gopm.go index d2cc16d27..5a8dd4523 100644 --- a/gopm.go +++ b/gopm.go @@ -29,7 +29,7 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.5.5.1129" +const APP_VER = "0.5.7.1201" // //cmd.CmdSearch, // cmdClean, @@ -53,6 +53,7 @@ func main() { app.Version = APP_VER app.Commands = []cli.Command{ cmd.CmdGet, + cmd.CmdBin, cmd.CmdGen, cmd.CmdRun, cmd.CmdBuild,