Golang实践:语言项目编译构建及包管理

发布于 2017-09-19 · 本文总共 10495 字 · 阅读大约需要 30 分钟

Go自带100多个包,可以为大多数应用程序提供基础; Go社区鼓励包设计、共享、重用以及改进,已经发布了很多的包,可以在http://godoc.org 找到可以搜索的索引;

包和go工具

导入路径

对于准备共享或公开的包,导入路径要全局唯一;

除了标准库中的包之外,其他包的导入路径应该以互联网域名(组织机构拥有的域名或用于存放包的域名)作为路径开始,这样也方便查找包;

包的声明

在每个Go源文件的开头都需要进行包声明;主要目的是当该包被其他包引入的时候作为其默认的标识符(包名);

导入声明

import:一个Go源文件可以在package声明的后面和第一个非导入声明语句前面包含零个或多个import声明;

重命名导入:如果需要把两个名字一样的包导入第三个包中,导入声明必须至少为其中的一个指定一个替代名字来避免冲突;

空导入

如果导入的包没有在文件中引用,就会产生一个编译错误;import _ xxxx

包及其命名

尽可能保持可读性和无歧义;util—-ioutil

包名通常使用统一的形式;

避免使用有歧义的包名;

go工具

go工具将不同种类的工具集合并为一个命名集; 它是一个包管理器,可以查询包的作者,计算它们的依赖关系,从远程版本控制系统下载它们;

也是一个构建系统,可计算文件依赖,调用编译器、汇编和链接器;

go
Go is a tool for managing Go source code.

Usage:

	go <command> [arguments]

The commands are:

	bug         start a bug report
	build       compile packages and dependencies
	clean       remove object files and cached files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         update packages to use new APIs
	fmt         gofmt (reformat) package sources
	generate    generate Go files by processing source
	get         add dependencies to current module and install them
	install     compile and install packages and dependencies
	list        list packages or modules
	mod         module maintenance
	run         compile and run Go program
	test        test packages
	tool        run specified go tool
	version     print Go version
	vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

	buildmode   build modes
	c           calling between Go and C
	cache       build and test caching
	environment environment variables
	filetype    file types
	go.mod      the go.mod file
	gopath      GOPATH environment variable
	gopath-get  legacy GOPATH go get
	goproxy     module proxy protocol
	importpath  import path syntax
	modules     modules, module versions, and more
	module-get  module-aware go get
	module-auth module authentication using go.sum
	module-private module configuration for non-public modules
	packages    package lists and patterns
	testflag    testing flags
	testfunc    testing functions

Use "go help <topic>" for more information about that topic.

工作空间的组织

GOPATH环境变量指定工作空间的根: export GOPATH=$HOME/xxxx

GOPATH有三个子目录:

src:包含源文件,每一个包放在一个目录中

pkg:构建工具存储编译后的包的位置;

bin:放置可执行程序

GOROOT环境变量指定Go发行版的根目录;其中提供所有标准库的包; 用户无须设置GOROOT,默认情况下go工具使用它的安装路径;

go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/xxxx"
GOPROXY=""
GORACE=""
GOROOT="/usr/lib/golang"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build350351294=/tmp/go-build -gno-record-gcc-switches"

包的下载

go get xxxx

如果指定-u开关,go get将确保它访问的所有包(包括它们的依赖性)更新到最新版本,然后再构建和安装; 如果没有这个标记,已经存在于本地的包不会更新;

go get -u命令通常获取每个包的最新版本,在刚刚开始的时候很方便;但是在需要部署的项目中就不太适合使用(发布版本需要精准的版本控制); 通常的解决方案是加一层vendor目录;构建一个关于所有必需依赖的本地副本;然后更新这个副本;

包的构建

go build 命令编译每一个命令行参数中的包;

如果包的名字是main,go build调用链接器在当前目录中创建可执行程序,可执行程序的名字取自包的导入路径的最后一段;

go install命令和go build非常相似;区别是它会保存每一个包的编译代码和命令,而不是把它们丢弃; 编译后的包保存在$GOPATH/pkg目录中,它对应于存放源文件的src目录,可执行的命令存放在$GOPATH/bin目录中;

go doc go/build

go doc go/build
package build // import "go/build"

Package build gathers information about Go packages.


Go Path

The Go path is a list of directory trees containing Go source code. It is
consulted to resolve imports that cannot be found in the standard Go tree.
The default path is the value of the GOPATH environment variable,
interpreted as a path list appropriate to the operating system (on Unix, the
variable is a colon-separated string; on Windows, a semicolon-separated
string; on Plan 9, a list).

Each directory listed in the Go path must have a prescribed structure:

The src/ directory holds source code. The path below 'src' determines the
import path or executable name.

The pkg/ directory holds installed package objects. As in the Go tree, each
target operating system and architecture pair has its own subdirectory of
pkg (pkg/GOOS_GOARCH).

If DIR is a directory listed in the Go path, a package with source in
DIR/src/foo/bar can be imported as "foo/bar" and has its compiled form
installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a" (or, for gccgo,
"DIR/pkg/gccgo/foo/libbar.a").

The bin/ directory holds compiled commands. Each command is named for its
source directory, but only using the final element, not the entire path.
That is, the command with source in DIR/src/foo/quux is installed into
DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped so that you can add
DIR/bin to your PATH to get at the installed commands.

Here's an example directory layout:

    GOPATH=/home/user/gocode

    /home/user/gocode/
        src/
            foo/
                bar/               (go code in package bar)
                    x.go
                quux/              (go code in package main)
                    y.go
        bin/
            quux                   (installed command)
        pkg/
            linux_amd64/
                foo/
                    bar.a          (installed package object)


Build Constraints

A build constraint, also known as a build tag, is a line comment that begins

    // +build

that lists the conditions under which a file should be included in the
package. Constraints may appear in any kind of source file (not just Go),
but they must appear near the top of the file, preceded only by blank lines
and other line comments. These rules mean that in Go files a build
constraint must appear before the package clause.

To distinguish build constraints from package documentation, a series of
build constraints must be followed by a blank line.

A build constraint is evaluated as the OR of space-separated options; each
option evaluates as the AND of its comma-separated terms; and each term is
an alphanumeric word or, preceded by !, its negation. That is, the build
constraint:

    // +build linux,386 darwin,!cgo

corresponds to the boolean formula:

    (linux AND 386) OR (darwin AND (NOT cgo))

A file may have multiple build constraints. The overall constraint is the
AND of the individual constraints. That is, the build constraints:

    // +build linux darwin
    // +build 386

corresponds to the boolean formula:

    (linux OR darwin) AND 386

During a particular build, the following words are satisfied:

    - the target operating system, as spelled by runtime.GOOS
    - the target architecture, as spelled by runtime.GOARCH
    - the compiler being used, either "gc" or "gccgo"
    - "cgo", if ctxt.CgoEnabled is true
    - "go1.1", from Go version 1.1 onward
    - "go1.2", from Go version 1.2 onward
    - "go1.3", from Go version 1.3 onward
    - "go1.4", from Go version 1.4 onward
    - "go1.5", from Go version 1.5 onward
    - "go1.6", from Go version 1.6 onward
    - "go1.7", from Go version 1.7 onward
    - "go1.8", from Go version 1.8 onward
    - "go1.9", from Go version 1.9 onward
    - "go1.10", from Go version 1.10 onward
    - "go1.11", from Go version 1.11 onward
    - any additional words listed in ctxt.BuildTags

If a file's name, after stripping the extension and a possible _test suffix,
matches any of the following patterns:

    *_GOOS
    *_GOARCH
    *_GOOS_GOARCH

(example: source_windows_amd64.go) where GOOS and GOARCH represent any known
operating system and architecture values respectively, then the file is
considered to have an implicit build constraint requiring those terms (in
addition to any explicit constraints in the file).

To keep a file from being considered for the build:

    // +build ignore

(any other unsatisfied word will work as well, but ``ignore'' is
conventional.)

To build a file only when using cgo, and only on Linux and OS X:

    // +build linux,cgo darwin,cgo

Such a file is usually paired with another file implementing the default
functionality for other systems, which in this case would carry the
constraint:

    // +build !linux,!darwin !cgo

Naming a file dns_windows.go will cause it to be included only when building
the package for Windows; similarly, math_386.s will be included only when
building the package for 32-bit x86.

Using GOOS=android matches build tags and files as for GOOS=linux in
addition to android tags and files.


Binary-Only Packages

It is possible to distribute packages in binary form without including the
source code used for compiling the package. To do this, the package must be
distributed with a source file not excluded by build constraints and
containing a "//go:binary-only-package" comment. Like a build constraint,
this comment must appear near the top of the file, preceded only by blank
lines and other line comments and with a blank line following the comment,
to separate it from the package documentation. Unlike build constraints,
this comment is only recognized in non-test Go source files.

The minimal source code for a binary-only package is therefore:

    //go:binary-only-package

    package mypkg

The source code may include additional Go code. That code is never compiled
but will be processed by tools like godoc and might be useful as end-user
documentation.

var ToolDir = getToolDir()
func ArchChar(goarch string) (string, error)
func Import(path, srcDir string, mode ImportMode) (*Package, error)
func ImportDir(dir string, mode ImportMode) (*Package, error)
func IsLocalImport(path string) bool
type Context struct{ ... }
    var Default Context = defaultContext()
type ImportMode uint
    const FindOnly ImportMode = 1 << iota ...
type MultiplePackageError struct{ ... }
type NoGoError struct{ ... }
type Package struct{ ... }

文档化

Go风格强烈鼓励有良好的包API文档;每一个导出的包成员的声明以及包声明自身应该立刻使用注释来描述它的目的和用途;

文档注释总是完整的语句,使用声明的包作为开头的第一句注释通常是总结;

比较长的包注释可以使用一个单独的注释文件;

go doc

go doc工具输出在命令行上指定的内容的声明和整个文档注释;

go doc time

go doc time.Since
func Since(t Time) Duration
    Since returns the time elapsed since t. It is shorthand for
    time.Now().Sub(t).

go doc time.Duration.Seconds
func (d Duration) Seconds() float64
    Seconds returns the duration as a floating point number of seconds.

godoc

提供相互链接的HTML页面服务,进而提供不少于go doc命令的信息

如果想浏览自己的包,可以在工作空间运行一个godoc实例:

godoc -http :8000
http://localhost:8000/pkg/

http://localhost:8000/pkg/go_practice

加上-analysis=type和-analysis=pointer标记使文档内容丰富,同时提供源代码的高级静态分析结果

内部包

没有导出的标识符只能在同一个包内访问,导出的标识符可以在世界任何地方访问;

这种方式定义标识符可以被一个小的可信任的包集合访问,但不是所有人可以访问;

go build工具会特殊对待导入路径中包含路径片断internal的情况,这些包叫内部包;

包的查询

go list工具上报可用包的信息; go list判断一个包是否存在于工作空间中;

go list github.com/go-sql-driver/mysql

go list github.com/go-sql-driver/mysql
github.com/go-sql-driver/mysql

go list …mysql…

go list ...mysql...
github.com/go-sql-driver/mysql

go list -json xxx

go list -json hash
{
        "Dir": "/usr/lib/golang/src/hash",
        "ImportPath": "hash",
        "Name": "hash",
        "Doc": "Package hash provides interfaces for hash functions.",
        "Target": "/usr/lib/golang/pkg/linux_amd64/hash.a",
        "Root": "/usr/lib/golang",
        "Match": [
                "hash"
        ],
        "Goroot": true,
        "Standard": true,
        "GoFiles": [
                "hash.go"
        ],
        "Imports": [
                "io"
        ],
        "Deps": [
                "errors",
                "internal/bytealg",
                "internal/cpu",
                "internal/race",
                "io",
                "runtime",
                "runtime/internal/atomic",
                "runtime/internal/sys",
                "sync",
                "sync/atomic",
                "unsafe"
        ]
}

-f标记可以让用户通过text/template包提供的模板语言来定制输出格式

go list -f '\{\{join .Deps " "\}\}' strconv

go list -f '' strconv
errors internal/cpu math math/bits unicode/utf8 unsafe

go list -f ' -> ' compress/...

 go list -f ' -> ' compress/...

compress/bzip2 -> bufio io sort
compress/flate -> bufio fmt io math math/bits sort strconv sync
compress/gzip -> bufio compress/flate encoding/binary errors fmt hash/crc32 io time
compress/lzw -> bufio errors fmt io
compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 io

工程

一个GO工程中主要包含以下三个目录:

src:源代码文件

pkg:包文件

bin:相关bin文件

注意

如果main包下有多个go文件,应该使用go run a.go b.go c.go 或 go run *.go来运行,编译同理

因为mian包里,使用go run main.go,编译器只会加载main.go这个文件,不会加载main包里的其他文件, 只有非main包里的文件才会通过依赖去自动加载。所以你需要输入多个文件作为参数。

如果需要编译为多个程序,可以加入cmd文件夹:

.
├── .gitignore
├── README.md
├── cmd
│   ├── kube-proxy
│   │   └── kube-proxy.go
│   └── kubeadm
│       └── kubeadm.go
└── pkg
    ├── proxy
    │   └── xxxx.go

vendor目录问题

1.vendor目录已经存在

如果已经执行了composer update/install,需要先删除vendor目录 执行:

rm -rf vendor
git add -A
git commit -m "remove vendor"
composer update --prefer-dist
git add . -A
git commit -m "recover vendor"

2.vendor目录不存在

composer update --prefer-dist
git add . -A
git commit -m "recover vendor"

Notice: composer update –prefer-dist 优先从缓存取,不携带组件内的.git目录




本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。
声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:邱文奇(qiuwenqi)的博客;
内容系本人学习、研究和总结,如有雷同,实属荣幸!
阅读次数:

文章评论

comments powered by Disqus


章节列表