项目目录结构如何组织,一般语言都是没有规定。但Go语言这方面做了规定,这样可以保持一致性
1、一般的,一个Go项目在GOPATH下,会有如下三个目录:
1|--bin 2|--pkg 3|--src
其中,bin存放编译后的可执行文件;pkg存放编译后的包文件;src存放项目源文件。一般,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。
对于pkg目录,曾经有人问:我把Go中的包放入pkg下面,怎么不行啊?他直接把Go包的源文件放入了pkg中。这显然是不对的。pkg中的文件是Go编译生成的,而不是手动放进去的。(一般文件后缀.a)
对于src目录,存放源文件,Go中源文件以包(package)的形式组织。通常,新建一个包就在src目录中新建一个文件夹。
2、举例说明
比如:我新建一个项目,test,开始的目录结构如下:
test--|--src
为了编译方便,我在其中增加了一个install文件,目录结构:
1test/ 2|-- install 3`-- src
其中install的内容如下:(linux下)
1#!/usr/bin/env bash 2 3if [ ! -f install ]; then 4echo 'install must be run within its container folder' 1>&2 5exit 1 6fi 7 8CURDIR=`pwd` 9OLDGOPATH="$GOPATH" 10export GOPATH="$CURDIR" 11 12gofmt -w src 13 14go install test 15 16export GOPATH="$OLDGOPATH" 17 18echo 'finished'
之所以加上这个install,是不用配置GOPATH(避免新增一个GO项目就要往GOPATH中增加一个路径)
接下来,增加一个包:config和一个main程序。目录结构如下:
1test 22 |-- install 33 `-- src 44 |-- config 55 | `-- config.go 66 `-- test 77 `-- main.go
1
注意,config.go中的package名称必须最好和目录config一致,而文件名可以随便。main.go表示main包,文件名建议为main.go。(注:不一致时,生成的.a文件名和目录名一致,这样,在import 时,应该是目录名,而引用包时,需要包名。例如:目录为myconfig,包名为config,则生产的静态包文件是:myconfig.a,引用该包:import “myconfig”,使用包中成员:config.LoadConfig())
config.go和main.go的代码如下:
config.go代码
1
package config
2
3
func LoadConfig() {
4
5
}
main.go代码
1
package main
2
3
import (
4
"config"
5
"fmt"
6
)
7
8
func main() {
9
config.LoadConfig()
10
fmt.Println("Hello, GO!")
11
}
接下来,在项目根目录执行./install
这时候的目录结构为:
1
test
2
|-- bin
3
| `-- test
4
|-- install
5
|-- pkg
6
| `-- linux_amd64
7
| `-- config.a
8
`-- src
9
|-- config
10
| `-- config.go
11
`-- test
12
`-- main.go
13
(linux_amd64表示我使用的操作系统和架构,你的可能不一样)
其中config.a是包config编译后生成的;bin/test是生成的二进制文件
这个时候可以执行:bin/test了。会输出:Hello, GO!
3、补充说明
1)包可以多层目录,比如:net/http包,表示源文件在src/net/http目录下面,不过源文件中的包名是最后一个目录的名字,如http
而在import包时,必须完整的路径,如:import “net/http”
2)有时候会见到local import(不建议使用),语法类似这样:
import “./config”
当代码中有这样的语句时,很多时候都会见到类似这样的错误:local import “./config” in non-local package
我所了解的这种导入方式的使用是:当写一个简单的测试脚本,想要使用go run命令时,可以使用这种导入方式。
比如上面的例子,把test/main.go移到src目录中,test目录删除,修改main.go中的import “config”为import “./config”,然后可以在src目录下执行:go run main.go
可见,local import不依赖于GOPATH
4、Windows下的install.bat
1@echo off 2setlocal 3 4if exist install.bat goto ok 5echo install.bat must be run from its folder 6goto end 7 8: ok 9 10set OLDGOPATH=%GOPATH% 11set GOPATH=%~dp0 12 13gofmt -w src 14 15go install test 16 17:end 18echo finished
注,冒号和ok之间不应该有空格,但是放在一起总是会被wordpress转成一个表情。汗……
5、更新日志
1)2012-12-05 发布
2)2013-04-13 修正:目录名可以和包名不同,但建议一致;将make文件名改为install
======================================================
GOROOT、GOPATH、GOBIN、project目录
前言:我觉得java程序员学golang很容易上手。关于GOROOT、GOPATH、GOBIN这些环境变量的设置,我隐约感觉到了java的影子(尽管我是一个C++程序员),唯一和java不同的是不能设置“.”。
另外,golang的设计也很明显的透露着“约定优于配置”的原则。这在java很多框架里面很常见。golang的环境变量设计也是如此。从android和golang我觉得google喜欢java。
不了解java的C++程序员,可以尝试全新去理解golang。
GOROOT
golang安装路径。
GOPATH
官方解释,请google。go工作环境中常常用到的一个很重要的环境变量(这种设计类似java)。具体用途:go命令常常需要用到的,如go run,go install, go get等。允许设置多个路径,和各个系统环境多路径设置一样,windows用“;”,linux(mac)用“:”分隔。
在linux(Mac)下,为了方便,一般配置在~/.bash_profile中。
book:~ wukebing$ vi ~/.bash_profile //编辑
book:~ wukebing$ source ~/.bash_profile //编辑完成后,使立即生效
例如:我的GOPATH设置(MAC下)
1export GOPATH=$HOME/workspace/go 2export PATH=$PATH:${GOPATH//://bin:}/bin 3export GOBIN=
其中,“export PATH=$PATH:${GOPATH//://bin:}/bin”为Linux(Mac)下把每个GOPATH下的bin都加入到PATH中。
当存在多个路径时,好像优先采用第一个路径。这个无关紧要了,只有需要的第三方包(库)都能正确下载和使用就ok了。
GOBIN
go install编译存放路径。不允许设置多个路径。可以为空。为空时则遵循“约定优于配置”原则,可执行文件放在各自GOPATH目录的bin文件夹中(前提是:package main的main函数文件不能直接放到GOPATH的src下面。
go环境查看
用go env 可查看当前go环境变量。
GOPATH目录结构
1goWorkSpace // (goWorkSpace为GOPATH目录) 2 -- bin // golang编译可执行文件存放路径,可自动生成。 3 -- pkg // golang编译的.a中间文件存放路径,可自动生成。 4 -- src // 源码路径。按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。
go目录结构1:
1project1 // (project1添加到GOPATH目录了) 2 -- bin 3 -- pkg 4 -- src 5 -- models // package 6 -- controllers // package 7 -- main.go // package main[注意,本文所有main.go均指包main的入口函数main所在文件] 8 9project2 // (project2添加到GOPATH目录了) 10 -- bin 11 -- pkg 12 -- src 13 -- models // package 14 -- controllers // package 15 -- main.go // package main
package main文件直接在GOPATH目录到src下。
使用go build可以在src文件夹下编译生成名为“src”的可执行文件。这是golang默认约定。一般我个人不怎么用这个命令。因为它会生成可执行文件在src目录下面。
我一般用:go get 和 go install。
go get [main.go所在路径]
参数 [main.go所在路径]:可选。相对GOPATH/src路径。 缺省是.(src自己)。可指定src下面的子文件夹路径。
go get会做2件事:1. 从远程下载需要用到的包。2.执行go install。(从这里也可以看出golang处处为了简洁而遵循的“约定优于配置”原则)
go install [main.go所在路径]
参数 [main.go所在路径]:可选。 相对GOPATH/src路径。缺省是.(src自己)。可指定src下面的子文件夹。
go install编译生成名称为[main.go父文件夹名]的可执行文件,放到GOBIN路径下。当GOBIN为空时,默认约定是:生成的可执行文件放到GOPATH/bin文件夹中。产生的中间文件(.a)放在project/pkg中(没有变化时,不重新生成.a)。
我们再看此go目录结构1,执行go install(以及go get的第二阶段go install)会报错:
**注意:**如果不用额外方式改变环境变量(公司目前用的是sh脚本编译),是编译不过的。报错:can’t load package: package .: no buildable Go source files in *
解决方法1:
曾经我也因为这个错误感到迷惑,认为所有都环境变量都没有问题。网上也没怎么看到直接明确都解答。看了一些帖子后,触类旁通,设置了GOBIN环境变量后解决。(好吧,我至今也没有完整读过英文官方文档。这种默认约定,官方文档上应该有。)
此解决方法有个弊端,多个project会导致多个GOPATH目录。多个project下的目录结构和包一致的话,直接编译会导致编译问题。因为go优先使用第一个GOPATH目录,导致编译冲突。(当然,你也可以每次手工或脚步修改GOPATH环境变量,感觉很麻烦。)不建议多个project直接设置到茫茫多的GOPATH中。当然有解决方法2,我认为是标准合理的解决方法,就是下面go目录结构2了。
go目录结构2:
1goWorkSpace // goWorkSpace为GOPATH目录 2 -- bin 3 -- myApp1 // 编译生成 4 -- myApp2 // 编译生成 5 -- myApp3 // 编译生成 6 -- pkg 7 -- src 8 -- common 1 9 -- common 2 10 -- common utils ... 11 -- myApp1 // project1 12 -- models 13 -- controllers 14 -- others 15 -- main.go 16 -- myApp2 // project2 17 -- models 18 -- controllers 19 -- others 20 -- main.go 21 -- myApp3 // project3 22 -- models 23 -- controllers 24 -- others 25 -- main.go
一个solution里面的多个project或工具组件都并列放在GOPATH的src下,如myApp1,myApp2,myApp3,common utils。
这时,GOBIN可以为空,编译时,可以如下:
go install myApp1 或 go get myApp1
go install myApp2 或 go get myApp2
go install myApp3 或 go get myApp3
这时才是大家都认为的,把可执行程序myApp1、myApp2、myApp3生成在goWorkSpace/bin下面。多个GOPATH也就有了上面的“把每个GOPATH下的bin都加入到PATH中”。
提示:相同结构的project下同名包怎么办?
有同事在初学时,习惯按照go目录结构1,了解到go目录结构2后(以为仅仅是把main放到了子文件夹,其他controllers等包结构不变),有这样的疑惑。他们原来就有这样的问题,同时把go目录加入到GOPATH后,编译就出现问题,因为包名和路径相同(相对GOPATH下的src),go只会优先查找第一个符合的GOPATH)。只会每次编译时手工修改GOPATH,或写脚本编译。(我看着就觉得累,还徒增脚本维护烦恼。)解决方案就是:除了通用的,公有的工具、组件外,属于各个project自己的东西,统统随着main.go一起移到project目录下。go目录结构2就是这样的。
导入各个project下的controllers方法:import myApp1/controllers,import myApp2/controllers这样的。go的import查找的是包的路径,并不是包名。你只用告诉go,你的包都放在哪了,go会从这些路径下查找有没有所需要的包。只是大家一般习惯包名和文件夹名相同,容易误解。只需要注意,一个文件夹下只允许有一个包名,允许有子文件夹定义不同的包。
import 采用的是相对路径写法:路径是 相对GOROOT和GOPATH下的src。
也可以设置GOBIN,而且这时,由于可执行文件名称不同,也不大容易产生覆盖(需要避免的时多个GOPATH用相同的“myApp”project名称。)
具体的还是看个人喜好和实际情况。我个人本地的环境大致是:
1dir 2 -- goWorkSpace1 // 主要是为了区分自己的鼓捣的一些东西和工作上的项目 3 -- goWorkSpace2 4 -- bin 5 -- pkg 6 -- src 7 -- myApp1 8 -- .git 9 -- models 10 -- controllers 11 -- main.go 12 -- myApp2 13 -- .git 14 -- models 15 -- controllers 16 -- main.go 17 -- myApp3 18 -- .git 19 -- models 20 -- controllers 21 -- main.go
也就是我本地是有多个GOPATH路径的dir/goWorkSpace1、dir/goWorkSpace2。
注意:GOPATH目录和GOPATH下的src不应该添加到源代码管理中,而是各个project目录myApp1、myApp2、myApp3各自时独立的进行源代码管理
我一般不设置GOBIN,把每个GOPATH下的bin都加入到PATH中。
个人理解,有任何问题,欢迎指正。