![[Pasted image 20231201224140.png]] Go项目(project)一般有两类:library和executable。library是以构建库为目的的Go项目,而executable则是以构建二进制可执行文件为目的的Go项目。 + library对应上图的package + executable对应上图的command ## 1.1 basic package 基本库 ```text project-root-directory/ ├── go.mod ├── modname.go └── modname_test.go 或 project-root-directory/ ├── go.mod ├── modname.go ├── modname_test.go ├── auth.go ├── auth_test.go ├── hash.go └── hash_test.go ``` 你的代码要依赖该module,直接通过下面import语句便可以将该module导入: ```text import "github.com/someuser/modname" ``` ## 1.2 package with supporting packages 库库调用 稍复杂或规模稍大的一些package类项目,会将很多功能分拆到supporting packages中,并且通常项目作者是不希望导出这些supporting packages的,这样这些supporting packages便可以不作为暴露的API的一部分,后续重构和优化起来十分方便,对package的用户也是无感的。这样Go官方建议将这些supporting packages放入[internal目录](https://link.zhihu.com/?target=https%3A//tonybai.com/2015/07/10/some-changes-in-go-1-5/) ```text project-root-directory/ ├── go.mod ├── modname.go ├── modname_test.go └── internal/ ├── auth/ │ ├── auth.go │ └── auth_test.go └── hash/ ├── hash.go └── hash_test.go ``` modname.go或modname_test.go可以通过下面导入语句使用internal下面的包: ```text import "github.com/someuser/modname/internal/auth" ``` ## 1.3 multiple packages 多库 作为一个库项目,作者可能要暴露不止一个package,可能是多个packages。这不会给Go项目目录布局带来过多复杂性,我们只需多建立几个导出package的目录就ok了。下面是一个multiple packages的示例: ```text project-root-directory/ ├── go.mod ├── modname.go ├── modname_test.go ├── auth/ │ ├── auth.go │ ├── auth_test.go │ └── token/ │ ├── token.go │ └── token_test.go ├── hash/ │ ├── hash.go │ └── hash_test.go └── internal/ └── trace/ ├── trace.go └── trace_test.go ``` 我们看到这个示例在repo(以托管在[http://github.com/user/modname](https://link.zhihu.com/?target=http%3A//github.com/user/modname)下为例)顶层放置了多个导出包: ```text github.com/user/modname github.com/user/modname/auth github.com/user/modname/hash ``` 并且顶层的auth目录下还有一个二级的导出包token,其导入路径为: ```text github.com/user/modname/auth/token ``` ## 2.1 basic command 基本项目 ```text project-root-directory/ ├── go.mod └── main.go 或 project-root-directory/ ├── go.mod ├── main.go ├── auth.go ├── auth_test.go ├── hash.go └── hash_test.go ``` 从示例我们可以看到,basic command类项目的repo下面只可构建出一个可执行文件,main函数放在main.go中,其他源文件也在repo根目录下,并同样放在main包中。 ## 2.2 command with supporting packages 项目调库 ```text project-root-directory/ ├── go.mod ├── main.go └── internal/ ├── auth/ │ ├── auth.go │ └── auth_test.go └── hash/ ├── hash.go └── hash_test.go ``` 和package with supporting packages不同的是,main.go使用的包名为main,这样Go编译器才能将其构建为command。 ## 2.3 multiple commands 多项目 ```text project-root-directory/ ├── go.mod ├── prog1/ │ └── main.go ├── prog2/ │ └── main.go └── internal/ └── trace/ ├── trace.go └── trace_test.go ``` 这个示例将每个command放置在一个单独的目录下(比如prog1、prog2等),supporting packages和之前的建议一样,统一放到internal下面。这样我们可以通过下面步骤来编译command: ```text $go build github.com/someuser/modname/prog1 $go build github.com/someuser/modname/prog2 ``` command的用户通过下面步骤可以安装这些命令 ```text $go install github.com/someuser/modname/prog1@latest $go install github.com/someuser/modname/prog2@latest ``` ## 3.1 packages and commands 库与项目混合 在同一个项目下面,既有多个可导出的packages,又有多个commands。下面是一个此类复杂项目的示例 ```text project-root-directory/ ├── go.mod ├── modname.go ├── modname_test.go ├── auth/ │ ├── auth.go │ ├── auth_test.go │ └── token/ │ ├── token.go │ └── token_test.go ├── hash/ │ ├── hash.go │ └── hash_test.go ├── internal/ │ └── trace/ │ ├── trace.go │ └── trace_test.go └── cmd/ ├── prog1/ │ └── main.go └── prog2/ └── main.go ``` 为了区分导出package和command,这个示例增加了一个专门用来存放command的cmd目录,prog1和prog2两个command都放在这个目录下。这也是Go语言的一个惯例 可以参考[GitHub - go-nunu/nunu: A CLI tool for building Go applications.](https://github.com/go-nunu/nunu/tree/main) 项目