前言
命令行工具 go 相关的代码在 <go-src>/src/cmd/go,目录结构
<go-src>/src/cmd/go internal testdata alldocs.go go11.go go_test.go go_unix_test.go go_windows_test.go main.go mkalldocs.sh note_test.sh note_test.go vendor_test.go
internal 目录下基本上按照 go "子命令" 进行组织,可以看到常用的子命令比如 help,list,run .etc
<go-src>/src/cmd/go internal base bug get help list run ...
main.go
main.go 是 go 命令的入口,为了优雅的支持各种 "子命令",main.go 将各个子命令对象保存在数组里,通过遍历数组找到具体的子命令,然后调用各个子命令的 run 方法
for _,cmd := range base.Commands { if cmd.Name() == args[0] && cmd.Runnable() { 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) base.Exit() return } }
Command struct
A command is an implementation of a go command
type Command struct { // Run runs the command. // The args are the arguments after the command name. Run func(cmd *Command,args []string) // UsageLine is the one-line usage message. // The first word in the line is taken to be the command name. UsageLine string // Short is the short description shown in the 'go help' output. Short string // Long is the long message shown in the 'go help <this-command>' output. 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 }
go 支持的所有 Command 存放在 base package 的 Command 数组 Command,并在 main.go 的 init 函数中初始化
func init() { base.Commands = []*base.Command{ work.CmdBuild,clean.CmdClean,doc.CmdDoc,envcmd.CmdEnv,bug.CmdBug,fix.CmdFix,fmtcmd.CmdFmt,generate.CmdGenerate,get.CmdGet,work.CmdInstall,list.CmdList,run.CmdRun,test.CmdTest,tool.CmdTool,version.CmdVersion,vet.CmdVet,.etc } }
go build
build command 的相关代码在 <go-src>/src/cmd/go/internal/work/build.go,在讨论具体的流程之前,我们先来看一些和 build 相关的数据结构
toolchain interface
toolchain 接口定义了在编译过程中一些通用的方法
type toolchain interface { // gc runs the compiler in a specific directory on a set of files // and returns the name of the generated output file. gc(b *Builder,p *load.Package,archive,obj string,asmhdr bool,importArgs []string,gofiles []string) (ofile string,out []byte,err error) // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *Builder,objdir,ofile,cfile string) error // asm runs the assembler in a specific directory on specific files // and returns a list of named output files. asm(b *Builder,sfiles []string) ([]string,error) // pkgpath builds an appropriate path for a temporary package file. Pkgpath(basedir string,p *load.Package) string // pack runs the archive packer in a specific directory to create // an archive from a set of object files. // typically it is run in the object directory. pack(b *Builder,objDir,afile string,ofiles []string) error // ld runs the linker to create an executable starting at mainpkg. ld(b *Builder,root *Action,out string,allactions []*Action,mainpkg string,ofiles []string) error // ldShared runs the linker to create a shared library containing the pkgs built by toplevelactions ldShared(b *Builder,toplevelactions []*Action,allactions []*Action) error compiler() string linker() string }
noToolchain
goToolchain
gccgoToolchain
Action
An action represents a single action in the action graph
Action 结构封装了 build 过程中的一个阶段性的"动作",Deps 字段描述它依赖哪些 Action,triggers 字段描述哪些 Action 依赖它,通过这两个字段,所有的 Action 组成一个 action graphic
type Action struct { ... Deps []*Action triggers []*Action ... }
Builder
A Builder holds global state about a build. It does not hold per-package state,because we build packages in parallel,and the builder is shared.
type Builder struct { ... ready actionQueue }