golang 构建工具之 Makefile

可能是因为编译太简单了,golang 并没有一个官方的构建工具(类似于 java 的 maven 和 gradle之类的),但是除了编译,我们可能还需要下载依赖,运行测试,甚至像 easyjson,protobuf,thrift 这样的工具下载和代码生成,如果没有构建工具,这些工作就会非常麻烦

为了解决这个问题,之前写过一个 everything.sh 的脚本,把所有的操作都封装在这个脚本里面,只需要执行类似于 sh everything.sh dependency 的命令就可以完成对应的工作,大大简化了构建过程,但是也有一个问题,shell 脚本本身的可读性并不是很好,而且对于各个操作之间的依赖不好描述

一次偶然的机会,在 github 上看到有人用 Makefile,就尝试了一下,发现真的非常合适,Makefile 本身就是用来描述依赖的,可读性非常好,而且与强大的 shell 结合在一起,基本可以实现任何想要的功能

下面是我在实际项目中使用的一个 Makefile,支持的功能包括

  • make build: 编译
  • make vendor: 下载依赖
  • make api: 生成协议代码
  • make json: easyjson 代码生成
  • make test: 运行单元测试
  • make benchmark: 运行性能测试
  • make stat: 代码复杂度统计,代码行数统计
  • make clean: 清理 build 目录
  • make deep_clean: 清理所有代码以外的其他文件
  • make third: 下载所有依赖的第三方工具
  • make protoc: 下载 protobuf 工具
  • make glide: 下载 glide 依赖管理工具
  • make golang: 下载 golang 环境
  • make cloc: 下载 cloc 统计工具
  • make gocyclo: 下载 gocyclo 圈复杂度计算工具
  • make easyjson: 下载 easyjson 工具
export PATH:=${PATH}:${GOPATH}/bin:$(shell pwd)/third/go/bin:$(shell pwd)/third/protobuf/bin:$(shell pwd)/third/cloc-1.76

.PHONY: all
all: third vendor api json build test stat

build: cmd/rta_server/*.go internal/*/*.go scripts/version.sh Makefile vendor api json
    @echo "编译"
    @rm -rf build/ && mkdir -p build/bin/ && \
    go build -ldflags "-X 'main.AppVersion=`sh scripts/version.sh`'" cmd/rta_server/main.go && \
    mv main build/bin/rta_server && \
    cp -r configs build/configs/

vendor: glide.lock glide.yaml
    @echo "下载 golang 依赖"
    glide install

api: vendor
    @echo "生成协议文件"
    @rm -rf api && mkdir api && \
    cd vendor/gitlab.mobvista.com/vta/rta_proto.git/ && \
    protoc --go_out=plugins=grpc:. *.proto && \
    cd - && \
    cp vendor/gitlab.mobvista.com/vta/rta_proto.git/* api/

json: internal/rcommon/rta_common_easyjson.go

internal/rcommon/rta_common_easyjson.go: internal/rcommon/rta_common.go Makefile
    easyjson internal/rcommon/rta_common.go

.PHONY: test
test: vendor api json
    @echo "运行单元测试"
    go test -cover internal/rranker/*.go
    go test -cover internal/rserver/*.go
    go test -cover internal/rworker/*.go
    go test -cover internal/rloader/*.go
    go test -cover internal/rrecall/*.go
    go test -cover internal/rmaster/*.go
    go test -cover internal/rsender/*.go

benchmark: benchmarkloader benchmarkall

.PHONY: benchmarkloader
benchmarkloader: vendor api json
    @echo "运行 loader 性能测试"
    go test -timeout 2h -bench BenchmarkS3Loader_Load -benchmem -cpuprofile cpu.out -memprofile mem.out -run=^$$ internal/rloader/*
    go tool pprof -svg ./rloader.test cpu.out > cpu.benchmarkloader.svg
    go tool pprof -svg ./rloader.test mem.out > mem.benchmarkloader.svg

.PHONY: benchmarkserver
benchmarkserver: vendor api json
    @echo "运行 server 性能测试"
    go test -timeout 2h -bench BenchmarkServer -benchmem -cpuprofile cpu.out -memprofile mem.out -run=^$$ internal/rserver/*
    go tool pprof -svg ./rserver.test cpu.out > cpu.benchmarkserver.svg
    go tool pprof -svg ./rserver.test mem.out > mem.benchmarkserver.svg

.PHONY: benchmarkall
benchmarkall: vendor api json
    @echo "运行 server 性能测试"
    go test -timeout 2h -bench BenchmarkAll -benchmem -cpuprofile cpu.out -memprofile mem.out -run=^$$ internal/rserver/*
    go tool pprof -svg ./rserver.test cpu.out > cpu.benchmarkall.svg    
    go tool pprof -svg ./rserver.test mem.out > mem.benchmarkall.svg

.PHONY: benchmarkcache
benchmarkcache: vendor api json
    @echo "测试 redis 集群性能"
    go test -timeout 5m -bench BenchmarkRtaCacheBatch -benchmem -cpuprofile cpu.out -memprofile mem.out -run=^$$ internal/rserver/*

.PHONY: stat
stat: cloc gocyclo
    @echo "代码行数统计"
    @ls internal/*/* scripts/* configs/* Makefile | xargs cloc --by-file
    @echo "圈复杂度统计"
    @ls internal/*/* | grep -v _test | xargs gocyclo
    @ls internal/*/* | grep -v _test | xargs gocyclo | awk '{sum+=$$1}END{printf("总圈复杂度: %s", sum)}'

.PHONY: clean
clean:
    rm -rf build

.PHONY: deep_clean
deep_clean:
    rm -rf vendor api build third

third: protoc glide golang cloc gocyclo easyjson

.PHONY: protoc
protoc: golang
    @hash protoc 2>/dev/null || { \
        echo "安装 protobuf 代码生成工具 protoc" && \
        mkdir -p third && cd third && \
        wget https://github.com/google/protobuf/releases/download/v3.2.0/protobuf-cpp-3.2.0.tar.gz && \
        tar -xzvf protobuf-cpp-3.2.0.tar.gz && \
        cd protobuf-3.2.0 && \
        ./configure --prefix=`pwd`/../protobuf && \
        make -j8 && \
        make install && \
        cd ../.. && \
        protoc --version; \
    }
    @hash protoc-gen-go 2>/dev/null || { \
        echo "安装 protobuf golang 插件 protoc-gen-go" && \
        go get -u github.com/golang/protobuf/{proto,protoc-gen-go}; \
    }

.PHONY: glide
glide: golang
    @mkdir -p $$GOPATH/bin
    @hash glide 2>/dev/null || { \
        echo "安装依赖管理工具 glide" && \
        curl https://glide.sh/get | sh; \
    }

.PHONY: golang
golang:
    @hash go 2>/dev/null || { \
        echo "安装 golang 环境 go1.10" && \
        mkdir -p third && cd third && \
        wget https://dl.google.com/go/go1.10.linux-amd64.tar.gz && \
        tar -xzvf go1.10.linux-amd64.tar.gz && \
        cd .. && \
        go version; \
    }

.PHONY: cloc
cloc:
    @hash cloc 2>/dev/null || { \
        echo "安装代码统计工具 cloc" && \
        mkdir -p third && cd third && \
        wget https://github.com/AlDanial/cloc/archive/v1.76.zip && \
        unzip v1.76.zip; \
    }

.PHONY: gocyclo
gocyclo: golang
    @hash gocyclo 2>/dev/null || { \
        echo "安装代码圈复杂度统计工具 gocyclo" && \
        go get -u github.com/fzipp/gocyclo; \
    }

.PHONY: easyjson
easyjson: golang
    @hash easyjson 2>/dev/null || { \
        echo "安装 json 编译工具 easyjson" && \
        go get -u github.com/mailru/easyjson/...; \
    }

转载请注明出处
本文链接:http://www.hatlonely.com/2018/04/11/golang-构建工具之-Makefile/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,510评论 25 707
  • C++少说也用了十年了,从简单的Hello World到200万行的游戏项目,编译和构建的工具也经历了各种升级。最...
    davidpp阅读 7,964评论 4 16
  • 瓯江水畔,碧波暖堤,笙歌锁住风声。 仲秋敲落炎热,欲镇离声。 撕心折枝送别,印唇边、含恨啼声。 不忍忘,那些年深爱...
    厉雄阅读 326评论 1 0
  • 文/安若木槿 博士生论文答辩结束那天,福贵连夜回乡,拿着户口本和身份证去改了个听起来特有文化的名字,杨修文。 1....
    蜉蝣舟阅读 407评论 5 4
  • 12月21日 星期四 晴 亲子日记第473天 忙碌且充实的一天,早上送儿子到学校后,直接出发了去了单位,在单位里...
    浩硕妈阅读 88评论 0 0