使用bazel编译go项目

5,808 阅读4分钟

前言

这篇文章主要介绍如果通过bazel来构建和管理go的项目。这里是一个最简单的实例。

bazel是什么

bazel是一个可以快速构建和测试任意规模软件的工具(官方介绍),能够用来编译Java,C++,Go,TS,iOS,Android等大部分的语言。

1. 创建工作空间

在使用bazel的时候,需要先创建一个WORKSPACE文件在你的项目根目录,表示这是bazel的工作空间,后续所有的配置都是基于这个目录来的。

那我们就先创建一个目录,然后添加一个这样的文件。

然后创建一个WORKSPACE文件 添加以下内容:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "io_bazel_rules_go",
    urls = [
        "https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/0.19.0/rules_go-0.19.0.tar.gz",
        "https://github.com/bazelbuild/rules_go/releases/download/0.19.0/rules_go-0.19.0.tar.gz",
    ],
    sha256 = "9fb16af4d4836c8222142e54c9efa0bb5fc562ffc893ce2abeac3e25daead144",
)

load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")

go_rules_dependencies()
go_register_toolchains()
 

这里面的内容不需要手写啊,你去https://github.com/bazelbuild/rules_go/releases 复制过来就好了。

2. 走一个hello go

配置了上面的东西之后,我们在项目目录下创建一个main.go的文件,内容如下:

package main;

import (
	"fmt"
)
func main()  {
	fmt.Println("hello go!");
}

然后创建一个BUILD.bazel文件,添加一下内容:

load("@io_bazel_rules_go//go:def.bzl", "go_binary")

go_binary(
    name = "go_default_library",
    srcs = ["main.go"],
    importpath = "test",
    visibility = ["//visibility:private"],
)

完成上面的工作之后,看下目前的目录结构:

非常简单,只有3个文件,然后我们看下怎么用bazel来运行这个go程序。

在项目的根目录下执行一下命令:


如果你是第一次跑的话可能会需要一点时间,当然你还可能报错,报错的原因一般都是网络问题,然后你懂得。

然后我们看到了hello go的输出了,而且我们没有配置gopath什么的,是不是很方便?

3. 使用gazelle自动生成BUILD.bazel文件

上面看到了,我们需要手写BUILD.bazel?那如果大项目包很多的话,不是要写到吐血?下面就来说下,怎么用gazelle这个工具来自动给每个包生成一个BUILD.bazel的文件。

为了演示,我们需要重新创建2个包。

在项目根目录下新建一个文件,叫做p1吧。

然后进到p1目录,添加一个文件p1.go。

内容如下:

package p1;

import (
	"fmt"
)
func TestP1() {
	fmt.Println("from p1")
}

p2也是一样,就不贴上来了。

之后的目录结构如下:

下面我们需要添加gazelle这个工具到我们的WORKSPACE文件中。

修改跟目录下的BUILD.bazel文件内容如下:

load("@bazel_gazelle//:def.bzl", "gazelle")

# gazelle:prefix bazel-go
gazelle(name = "gazelle")

 

划重点:

# gazelle:prefix bazel-go 

这行注释必须要的啊,gazelle:prefix后面跟你的项目名称,这里我写的就是bazel-go。

具体可以参考:github.com/bazelbuild/…

然后运行以下命令:

bazel run //:gazelle

然后输出应该像下面这样,就是正确的。

看下现在的p1,p2目录下:

发现已经给我们生成了对应的文件,打开一个看下内容:

load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
    name = "go_default_library",
    srcs = ["p1.go"],
    importpath = "bazel-go/p1",
    visibility = ["//visibility:public"],
)

这里的参数可以参考:github.com/bazelbuild/…

这个时候我们看下根目录下的BUILD.bazel文件的内容:

load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@bazel_gazelle//:def.bzl", "gazelle")

# gazelle:prefix bazel-go
gazelle(name = "gazelle")

go_library(
    name = "go_default_library",
    srcs = ["main.go"],
    importpath = "bazel-go",
    visibility = ["//visibility:private"],
    deps = ["//p2:go_default_library"],
)

go_binary(
    name = "bazel-go",
    embed = [":go_default_library"],
    visibility = ["//visibility:public"],
)

发现跟上面的有点不一样了,自动添加了一些声明。

4. 使用上面生成的包

在上面我们定义了两个包,那怎么import进来使用呢?

这里我们再main.go这里来导入p2这个包,并且执行下里面的函数。

看下main.go的代码:

package main;

import (
	"fmt"
	p2 "bazel-go/p2"
)
func main()  {
	p2.TestP2()
	fmt.Println("hello go!");
}
p2 "bazel-go/p2"

这里我们导入了p2这个包,并且用p2代替。为什么后面是"bazel-go/p2"呢? 看下p2的BUILD.bazel文件里面的声明,有个importpath,对的,我们用这个值就行了。

p2.TestP2()

这句是调用了p2这个包里面的TestP2()这个函数。 然后我们在根目录运行以下命令:

bazel run //:bazel-go

看下结果:

可以看到,正确输出了函数中打印的字符串。

总结

到这里,我们基本上讲解了如何使用bazel搭建一个go项目,使用bazel初始化项目,使用gazelle自动生成BUILD文件,以及如何引用其他的包等。