为了示范,我们创建了一个名为 uc 的简单包,它含有一个 `UpperCase` 函数将字符串的所有字母转换为大写。当然这并不值得创建一个自己包,同样的功能已被包含在 `strings` 包里,但是同样的技术也可以应用在更复杂的包中。
## 9.8.1 自定义包的目录结构
下面的结构给了你一个好的示范(uc 代表通用包名, 名字为粗体的代表目录,斜体代表可执行文件):
~~~
/home/user/goprograms
ucmain.go (uc包主程序)
Makefile (ucmain的2-makefile)
ucmain
src/uc (包含uc包的go源码)
uc.go
uc_test.go
Makefile (包的1-makefile)
uc.a
_obj
uc.a
_test
uc.a
bin (包含最终的执行文件)
ucmain
pkg/linux_amd64
uc.a (包的目标文件)
~~~
将你的项目放在 goprograms 目录下(你可以创建一个环境变量 GOPATH,详见第 2.2/3 章节:在 .profile 和 .bashrc 文件中添加 `export GOPATH=/home/user/goprograms`),而你的项目将作为 src 的子目录。uc 包 中的功能在 uc.go 中实现。
示例 9.6 [uc.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/examples/chapter_9/uc.go):
~~~
package uc
import "strings"
func UpperCase(str string) string {
return strings.ToUpper(str)
}
~~~
包通常附带一个或多个测试文件,在这我们创建了一个 uc_test.go 文件,如第 9.8 节所述。
示例 9.7 [test.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/examples/chapter_9/uc.go)
~~~
package uc
import "testing"
type ucTest struct {
in, out string
}
var ucTests = []ucTest {
ucTest{"abc", "ABC"},
ucTest{"cvo-az", "CVO-AZ"},
ucTest{"Antwerp", "ANTWERP"},
}
func TestUC(t *testing.T) {
for _, ut := range ucTests {
uc := UpperCase(ut.in)
if uc != ut.out {
t.Errorf("UpperCase(%s) = %s, must be %s", ut.in, uc,
ut.out)
}
}
}
~~~
通过指令编译并安装包到本地:`go install src/uc`, 这会将 uc.a 复制到 pkg/linux_amd64 下面。
另外,使用 make,通过以下内容创建一个包的 Makefile(1) 在 src/uc 目录下:
~~~
include $GOROOT/src/Make.inc
TARG=uc
GOFILES=\
uc.go\
include $(GOROOT)/scr/Make.pkg
~~~
在该目录下的命令行调用: gomake
这将创建一个 _obj 目录并将包编译生成的存档 uc.a 放在该目录下。
这个包可以通过 go test 测试。
创建一个 ud.a 的测试文件在目录下,输出为 PASS 时测试通过。
在第 13.8 节我们将给出另外一个测试例子并进行深入研究。
备注:有可能你当前的用户不具有足够的资格使用 go install(没有权限)。这种情况下,选择 root 用户 su。确保 Go 环境变量和 Go 源码路径也设置给 su,同样也适用你的普通用户(详见第 2.3 节)。
接下来我们创建主程序 ucmain.go:
示例 9.8 [ucmain.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/examples/chapter_9/ucmain.go):
~~~
package main
import (
"fmt"
"./uc/uc"
)
func main() {
str1 := "USING package uc"
fmt.Println(uc.UpperCase(str1))
}
~~~
然后在这个目录下输入 `go install`。
另外复制 uc.a 到 uc 目录并创建一个 Makefile(2) 并写入文本:
~~~
include $GOROOT/src/Make.inc
TARG=ucmain
GOFILES=\
ucmain.go\
include $GOROOT/src/Make.cmd
~~~
执行 gomake 编译 `ucmain.go` 到 ucmain 目录
运行 `./ucmain` 显示: `USING package uc!`。
## 9.8.2 本地安装包
本地包在用户目录下,使用给出的目录结构,以下命令用来从源码安装本地包:
~~~
go install /home/user/goprograms/src/uc # 编译安装uc
cd /home/user/goprograms/uc
go install ./uc # 编译安装uc(和之前的指令一样)
cd ..
go install . # 编译安装ucmain
~~~
安装到 `$GOPATH` 下:
如果我们想安装的包在系统上的其他 Go 程序中被使用,它一定要安装到 `$GOPATH` 下。 这样做,在 .profile 和 .bashrc 中设置 `export GOPATH=/home/user/goprograms`。
然后执行 go install uc 将会复制包存档到 `$GOPATH/pkg/LINUX_AMD64/uc`。
现在,uc 包可以通过 `import "uc"` 在任何 Go 程序中被引用。
## 9.8.3 依赖系统的代码
不同操作系统上运行不同的程序是非常少见的:绝大多数情况下语言和标准库解决了大部分的可移植性问题。
你有一个很好的理由去写平台平台特定的代码,例如汇编语言。这种情况下,按照下面的约定是合理的:
~~~
prog1.go
prog1_linux.go
prog1_darwin.go
prog1_windows.go
~~~
prog1.go 定义了不同操作系统通用的接口,并将系统特定的代码写到 prog1_os.go 中。 对于 Go 工具你可以指定`prog1_$GOOS.go` 或 `prog1_$GOARCH.go` 或在平台 Makefile 中:`prog1_$(GOOS).go\` 或 `prog1_$(GOARCH).go\`。
- 前言
- 第一部分:学习 Go 语言
- 第1章:Go 语言的起源,发展与普及
- 1.1 起源与发展
- 1.2 语言的主要特性与发展的环境和影响因素
- 第2章:安装与运行环境
- 2.1 平台与架构
- 2.2 Go 环境变量
- 2.3 在 Linux 上安装 Go
- 2.4 在 Mac OS X 上安装 Go
- 2.5 在 Windows 上安装 Go
- 2.6 安装目录清单
- 2.7 Go 运行时(runtime)
- 2.8 Go 解释器
- 第3章:编辑器、集成开发环境与其它工具
- 3.1 Go 开发环境的基本要求
- 3.2 编辑器和集成开发环境
- 3.3 调试器
- 3.4 构建并运行 Go 程序
- 3.5 格式化代码
- 3.6 生成代码文档
- 3.7 其它工具
- 3.8 Go 性能说明
- 3.9 与其它语言进行交互
- 第二部分:语言的核心结构与技术
- 第4章:基本结构和基本数据类型
- 4.1 文件名、关键字与标识符
- 4.2 Go 程序的基本结构和要素
- 4.3 常量
- 4.4 变量
- 4.5 基本类型和运算符
- 4.6 字符串
- 4.7 strings 和 strconv 包
- 4.8 时间和日期
- 4.9 指针
- 第5章:控制结构
- 5.1 if-else 结构
- 5.2 测试多返回值函数的错误
- 5.3 switch 结构
- 5.4 for 结构
- 5.5 Break 与 continue
- 5.6 标签与 goto
- 第6章:函数(function)
- 6.1 介绍
- 6.2 函数参数与返回值
- 6.3 传递变长参数
- 6.4 defer 和追踪
- 6.5 内置函数
- 6.6 递归函数
- 6.7 将函数作为参数
- 6.8 闭包
- 6.9 应用闭包:将函数作为返回值
- 6.10 使用闭包调试
- 6.11 计算函数执行时间
- 6.12 通过内存缓存来提升性能
- 第7章:数组与切片
- 7.1 声明和初始化
- 7.2 切片
- 7.3 For-range 结构
- 7.4 切片重组(reslice)
- 7.5 切片的复制与追加
- 7.6 字符串、数组和切片的应用
- 第8章:Map
- 8.1 声明、初始化和 make
- 8.2 测试键值对是否存在及删除元素
- 8.3 for-range 的配套用法
- 8.4 map 类型的切片
- 8.5 map 的排序
- 8.6 将 map 的键值对调
- 第9章:包(package)
- 9.1 标准库概述
- 9.2 regexp 包
- 9.3 锁和 sync 包
- 9.4 精密计算和 big 包
- 9.5 自定义包和可见性
- 9.6 为自定义包使用 godoc
- 9.7 使用 go install 安装自定义包
- 9.8 自定义包的目录结构、go install 和 go test
- 9.9 通过 Git 打包和安装
- 9.10 Go 的外部包和项目
- 9.11 在 Go 程序中使用外部库
- 第10章:结构(struct)与方法(method)
- 10.1 结构体定义
- 10.2 使用工厂方法创建结构体实例
- 10.3 使用自定义包中的结构体
- 10.4 带标签的结构体
- 10.5 匿名字段和内嵌结构体
- 10.6 方法
- 10.8 垃圾回收和 SetFinalizer
- 第11章:接口(interface)与反射(reflection)
- 11.1 接口是什么
- 11.2 接口嵌套接口
- 11.3 类型断言:如何检测和转换接口变量的类型
- 11.4 类型判断:type-switch
- 11.5 测试一个值是否实现了某个接口
- 11.6 使用方法集与接口
- 11.7 第一个例子:使用 Sorter 接口排序
- 11.8 第二个例子:读和写
- 11.9 空接口
- 11.10 反射包
- 第三部分:Go 高级编程
- 第12章 读写数据
- 12.1 读取用户的输入
- 12.2 文件读写
- 12.3 文件拷贝
- 12.4 从命令行读取参数
- 12.5 用buffer读取文件
- 12.6 用切片读写文件
- 12.7 用 defer 关闭文件
- 12.8 使用接口的实际例子:fmt.Fprintf
- 12.9 Json 数据格式
- 12.10 XML 数据格式
- 12.11 用 Gob 传输数据
- 12.12 Go 中的密码学
- 第13章 错误处理与测试
- 13.1 错误处理
- 13.2 运行时异常和 panic
- 13.3 从 panic 中恢复(Recover)
- 13.4 自定义包中的错误处理和 panicking
- 13.5 一种用闭包处理错误的模式
- 13.6 启动外部命令和程序
- 13.7 Go 中的单元测试和基准测试
- 13.8 测试的具体例子
- 13.9 用(测试数据)表驱动测试
- 13.10 性能调试:分析并优化 Go 程序
- 第14章:协程(goroutine)与通道(channel)
- 14.1 并发、并行和协程
- 14.2 使用通道进行协程间通信
- 14.3 协程同步:关闭通道-对阻塞的通道进行测试
- 14.4 使用 select 切换协程
- 14.5 通道,超时和计时器(Ticker)
- 14.6 协程和恢复(recover)
- 第15章:网络、模版与网页应用
- 15.1 tcp服务器
- 15.2 一个简单的web服务器
- 15.3 访问并读取页面数据
- 15.4 写一个简单的网页应用
- 第四部分:实际应用
- 第16章:常见的陷阱与错误
- 16.1 误用短声明导致变量覆盖
- 16.2 误用字符串
- 16.3 发生错误时使用defer关闭一个文件
- 16.5 不需要将一个指向切片的指针传递给函数
- 16.6 使用指针指向接口类型
- 16.7 使用值类型时误用指针
- 16.8 误用协程和通道
- 16.9 闭包和协程的使用
- 16.10 糟糕的错误处理
- 第17章:模式
- 17.1 关于逗号ok模式
- 第18章:出于性能考虑的实用代码片段
- 18.1 字符串
- 18.2 数组和切片
- 18.3 映射
- 18.4 结构体
- 18.5 接口
- 18.6 函数
- 18.7 文件
- 18.8 协程(goroutine)与通道(channel)
- 18.9 网络和网页应用
- 18.10 其他
- 18.11 出于性能考虑的最佳实践和建议
- 附录