本文转自  使用go build 进行条件编译 http://blog.csdn.net/varding/article/details/12675971

当我们编写的Go代码依赖特定平台或者cpu架构的时候,我们需要给出不同的实现

C语言有预处理器,可以通过宏或者#define包含特定平台指定的代码进行编译

但是go没有预处理器,他是通过 go/build包 里定义的tags和命名约定来让Go的包可以管理不同平台的代码

这篇文章将讲述Go的条件编译系统是如何实现的,并且通过实例来说明如何使用

1. 预备知识:go list命令的使用

在讲条件编译之前需要了解go list的简单用法

go list访问源文件里那些能够影响编译进程内部的数据结构

go list与go build ,test,install大部分的参数相同,但是go list不会执行编译操作。使用-f参数可以让我们提供的text/template里的代码在包含go/build.Package上下文的环境里正确执行(就是让go/build.Package里的上下文去格式化 text/template里这种格式 '{{.GoFiles}}'里的占位符,写过http server程序的同学看到应该很熟悉)

使用格式化参数,我们能通过go list获取将会被编译的文件名

  1. % go list -f '{{.GoFiles}}' os/exec
  2. [exec.go lp_unix.go]

上面这个例子里我们用go list来查看在Linux/arm平台下 os/exec包里有哪些文件将会被编译。

结果显示:exec.go包含了通用的代码在所有的平台下可用,lp_unix.go包含了*nix系统里的exec.LookPath

在windows系统下运行同样的命令,结果如下:

  1. C:\go> go list -f '{{.GoFiles}}' os/exec
  2. [exec.go lp_windows.go]

上面这个例子是Go 条件编译系统的两个部分,称之为:编译约束,下面将详细描述

2. 第一种条件编译的方法:编译标签

在源代码里添加标注,通常称之为编译标签( build tag)

编译标签是在尽量靠近源代码文件顶部的地方用注释的方式添加

go build在构建一个包的时候会读取这个包里的每个源文件并且分析编译便签,这些标签决定了这个源文件是否参与本次编译

编译标签添加的规则(附上原文):

1. a build tag is evaluated as the OR of space-separated options
2. each option evaluates as the AND of its comma-separated terms
3. each term is an alphanumeric word or, preceded by !, its negation

1). 编译标签由空格分隔的编译选项(options)以"或"的逻辑关系组成

2). 每个编译选项由逗号分隔的条件项以逻辑"与"的关系组成

3). 每个条件项的名字用字母+数字表示,在前面加!表示否定的意思

例子(编译标签要放在源文件顶部)

  1. // +build darwin freebsd netbsd openbsd

这个将会让这个源文件只能在支持kqueue的BSD系统里编译

一个源文件里可以有多个编译标签,多个编译标签之间是逻辑"与"的关系

  1. // +build linux darwin
  2. // +build 386

这个将限制此源文件只能在 linux/386或者darwin/386平台下编译

关于注释的说明

刚开始使用编译标签经常会犯下面这个错误

  1. // +build !linux
  2. package mypkg // wrong

这个例子里的编译标签和包的声明之间没有用空行隔开,这样编译标签会被当做包声明的注释而不是编译标签从而被忽略掉

下面这个是正确的标签的书写方式,标签的结尾添加一个空行这样标签就不会当做其他声明的注释

  1. // +build !linux
  2. package mypkg // correct

用go vet命令也可以检测到这个缺少空行的错误,初期可以用这个命令来避免缺少空行的错误

  1. % go vet mypkg
  2. mypkg.go:1: +build comment appears too late in file
  3. exit status 1

作为参考,下面的例子将licence声明,编译标签和包声明放在一起,请大家注意分辨

  1. % head headspin.go
  2. // Copyright 2013 Way out enterprises. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. // +build someos someotheros thirdos,!amd64
  6. // Package headspin implements calculates numbers so large
  7. // they will make your head spin.
  8. package headspin

3. 第二种条件编译方法:文件后缀

这个方法通过改变文件名的后缀来提供条件编译,这种方案比编译标签要简单,go/build可以在不读取源文件的情况下就可以决定哪些文件不需要参与编译

文件命名约定可以在go/build 包里找到详细的说明,简单来说如果你的源文件包含后缀:_$GOOS.go,那么这个源文件只会在这个平台下编译,_$GOARCH.go也是如此。这两个后缀可以结合在一起使用,但是要注意顺序:_$GOOS_$GOARCH.go,    不能反过来用:_$GOARCH_$GOOS.go

例子如下:

  1. mypkg_freebsd_arm.go // only builds on freebsd/arm systems
  2. mypkg_plan9.go       // only builds on plan9

源文件不能只提供条件编译后缀,还必须有文件名:

  1. _linux.go
  2. _freebsd_386.go

这两个源文件在所有平台下都会被忽略掉,因为go/build将会忽略所有以下划线或者点开头的源文件

4. 编译标签和文件后缀的选择

编译标签和文件后缀的功能上有重叠,例如一个文件名:mypkg_linux.go包含了// +build linux将会出现冗余

通常情况下,如果源文件与平台或者cpu架构完全匹配,那么用文件后缀,例如:

  1. mypkg_linux.go         // only builds on linux systems
  2. mypkg_windows_amd64.go // only builds on windows 64bit platforms

相反,如果这个源文件可以在超过一个平台或者超过一个cpu架构下可以使用或者需要去除指定平台,那么使用编译标签,例如下面的编译标签可以在所有*nix平台上编译:

  1. % grep '+build' $HOME/go/src/pkg/os/exec/lp_unix.go
  2. // +build darwin dragonfly freebsd linux netbsd openbsd

下面是可以在除了windows的所有平台下编译

  1. % grep '+build' $HOME/go/src/pkg/os/types_notwin.go
  2. // +build !windows

5. 总结

这篇文章主要关注所有可以被go tool编译的go源文件,编译标签和文件后缀名(也包括了.c 和.s文件)

Go的标准库里包含了很多的样例,特别是runtime,syscall,os和net包,读者可以通过这些包来学习

Test文件也支持编译标签和文件后缀条件编译,并且作用方式与go源文件相同。可以在不同平台下有条件的包含一些测试样例。同样,标准库也包含了大量的例子

最后,这篇文件是讲如何用go tool来达到条件编译,但是条件编译不限于go tool,你可以用go/build包编写自己的条件编译工具

转载于:https://www.cnblogs.com/brig17568/p/7300498.html

golang下的条件编译相关推荐

  1. Golang下的Log处理

    Golang下的Log处理 原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com 后端系统中的Log是相当重要的,做过高并发服务的同学都会认同这一点.相对而言,调试已经 ...

  2. golang下文件锁的使用

    前言 题目是golang下文件锁的使用,但本文的目的其实是通过golang下的文件锁的使用方法,来一窥文件锁背后的机制. 为什么需要文件锁 只有多线程/多进程这种并发场景下读写文件,才需要加锁, 场景 ...

  3. golang 下划线

    1.下划线在import中 在Golang里,import的作用是导入其他package,但是今天在看beego框架时看到了import 下划线,不知其意,故百度而解之. import 下划线(如:i ...

  4. windows下golang监控鼠标和键盘事件

    最近一直在研究golang下监听键盘和鼠标的操作事件功能, github上有也有非常优秀的开源库robotgo, 它地址如下, 有兴趣的同学可以去研究下: https://github.com/go- ...

  5. Golang websocket

    2019独角兽企业重金招聘Python工程师标准>>> 环境:Win10 + Go1.9.2 1.先下载并引用golang的websocket库 ①golang的官方库都在https ...

  6. 【Golang 基础】Go 语言的程序结构

    Go 语言的项目结构    一般,一个 Go 项目在 GOPATH 下,会有如下三个目录 . ├── bin // 存放编译后的可执行文件 ├── pkg // 存放编译后的包文件 └── src / ...

  7. golang执行多个linux命令,golang执行系统command

    golang下的os/exec包执行外部命令,它将os.StartProcess进行包装使得它更容易映射到stdin和stdout.这点和python下的command.os.system等功能是一样 ...

  8. 条件编译指令(符号),C语言条件编译指令完全攻略

    在 C 语言中,条件编译指令可以实现源代码的部分编译功能,可以根据表达式的值或者某个特定的宏来确定编译条件,以决定编译哪些代码,不编译哪些. 使用"#ifndef/#define/#endi ...

  9. 安利一个好用的Golang单元测试框架:gocheck

    Golang下如何写单元测试?官方提供的testing package略显简陋,不过好在我们有Gocheck. 什么是好的单元测试? 在进入正题前,先来温习下前人总结的单元测试几条原则: http:/ ...

最新文章

  1. 今年诺贝尔医学奖颁给丙肝病毒发现者:英美三位科学家加冕,最年长者85岁,都曾获拉斯克奖...
  2. 浅析关键词与搜索引擎之间不得不说的关系
  3. HDOJ 1233 还是畅通工程
  4. 假期ACM训练计划表
  5. php url无效,PHP解析URL并失去URL中的参数
  6. 《Windows核心编程》---Interlocked原子访问系列函数
  7. Ruby中require,load,和include的区别
  8. 阿里巴巴超大规模 Kubernetes 基础设施运维体系介绍
  9. 国盾量子等参与中国量子通信行业首批标准编制 量子通信商用再加速
  10. SOCK_STREAM和SOCK_DGRAM两种类型的区别
  11. 【数据结构算法】数据结构思维导图
  12. Python发邮件的小脚本
  13. ITIL 4知识系列之ITIL4的设计框架解析
  14. 大型网站技术架构思维导图
  15. Esp8266对接阿里云物联网远程实时温度监控{探讨帖}
  16. BAPI_PO_CREATE1 批量创建采购订单(可一个PO采购多个材料)
  17. [转]深度学习在目标跟踪中的应用
  18. 非视距微波传输抗干扰特性
  19. 快递鸟电⼦⾯单批量打印流程与注意事项
  20. 王姨劝我学HarmonyOS鸿蒙2.0系列教程之三Ability概述调用方法!

热门文章

  1. mysql一键部署_一键部署----mysql5.6--jue
  2. 热力图怎么做_LncRNA这么热,5分左右的LncRNA研究文章应该怎么做
  3. linux命令kill_什么是Linux中的kill命令?
  4. c语言中优先级队列_C ++中的优先级队列
  5. python对象_Python对象()
  6. 小议C++中函数的参数的传递
  7. Sql中的union和union all的讲解
  8. Python学习笔记-异常处理
  9. 新产品Wyn Enterprise 详解,立即预约公开课
  10. ISA Server 2004 SP2新特性(上)