1-11. go条件编译

  • go支持两种条件编译的实现方式

    1. 编译标签(build tags)

      //go:build !windows// +build !windows

    2. 文件后缀

  • 条件编译的应用场景

    1. 平台适配:不同平台的操作系统和 CPU 架构可能有不同的特性和限制,需要针对不同平台编写不同的代码。通过条件编译,可以根据不同平台选择不同的代码进行编译,从而提高程序的性能和稳定性。
    2. 调试信息:在开发和调试过程中,需要添加一些调试信息来辅助开发人员定位问题。但是,在发布版本时这些信息不应该包含在内。使用条件编译,可以在开发和调试阶段包含调试信息,在发布版本中去除这些信息。
    3. 功能开关:有些功能可能只在特定的场景下使用,而在其他场景下不需要。使用条件编译可以根据编译标记开启或关闭这些功能,从而减少程序的代码量和复杂度。
    4. 性能优化:有些代码可能只在特定的环境下才能发挥最优性能。使用条件编译,可以根据编译标记选择不同的代码实现,从而提高程序的性能。

1 编译标签(build tags)

1.1 1. //go:build // +build 的区别

  • // +build编译条件的注释和package 语句之间一定要隔一行。不然无法识别编译条件,编译条件支持“非”逻辑,比如某个文件在非 windows 环境下编译

  • //go:build是Go 1.17中引入的新的条件编译指令,旨在取代// +build指令,因为新语法带来了一些关键改进:

    • 与其他现有Go指令和pragma的一致性,例如//go:generate
    • 支持标准布尔表达式,例如//go:build foo && bar,而旧的// +build注释的语法不那么直观。例如,AND用逗号// +build foo,bar和或空格// +build foo bar表示
    • 它由go fmt支持,它将自动修复指令在源文件中的错误位置,从而避免常见错误,如在指令和包语句之间不留空行。
  • 在Go 1.N中:

    • 生成将开始优先选择//go:build行进行文件选择。如果文件中没有//go:build,那么任何// +build行仍然适用。
    • 如果Go文件包含//go:build而没有// +build,则构建将不再失败。
    • 如果Go或程序集文件中包含//go:build太晚,则生成将失败。Gofmt将把错位的//go:build和//+build行移到文件中的正确位置。
    • Gofmt将使用与其他Go布尔表达式(所有&&||运算符周围的空格)相同的规则格式化//go:build行中的表达式。
    • 如果文件只包含// +build行,则gofmt将在其上方添加一个等效的//go:build行。
    • 如果一个文件同时包含//go:build// +build行,则gofmt将考虑//go:build是真理的来源,并更新// +build行以匹配,从而保持与早期版本的Go的兼容性。Gofmt还将拒绝被认为太复杂而无法转换为// +build格式的//go:build行,尽管这种情况很少见。(注意此项目符号开头的“If”。Gofmt不会将// +build行添加到只有//go:build.的文件中)
    • buildtags签入go vet将添加对//go:build约束的支持。当Go源文件包含具有不同含义的//go:build// +build行时,它将失败。如果检查失败,可以运行gofmt -w

1.2 2. 使用说明

  • 基础语法

    1
    2
    3
    4
    5
    6
    
    // +build <tag1> <tag2> <tag3> ...
    或者是
    //go:build <tag1> <tag2> <tag3> ...
    
    编译的时候
    go build -tag1 mytag2 mytag3
    • <tag> 可以是操作系统、CPU 架构、编译标记等,多个标记之间用空格分隔或者都好分隔。
      • 编译标签由空格分隔的编译选项(options)以”或”的逻辑关系组成
      • 每个编译选项由逗号分隔的条件项以逻辑”与”的关系组成
      • 每个条件项的名字用字母+数字表示,在前面加!表示否定的意思
      • 不同tag域之间用空格区分,他们是OR关系
      • 同一tag域之内不同的tag用都好区分,他们是AND关系
      • 每一个tag都由字母和数字构成,!开头表示条件“非”
    1
    2
    
    // +build linux darwin
    // +build 386
    
    • 一个源文件里可以有多个编译标签,多个编译标签之间是逻辑”与”的关系

    • 关系

      1
      2
      3
      4
      
      空格表示AND 
      逗号表示OR 
      !表示NOT 
      换行表示AND
  • 内置 tag

    • 指定操作系统:如 darwinlinuxwindows 等,对应 runtime.GOOS 的值。

    • 指定CPU 架构:如 amd64arm386 等,对应 runtime.GOARCH 的值。

    • 指定编译器:例如:gccgo、gc,是否开启CGO,cgo。

    • 指定Go 版本:例如:go1.19、go1.20 等。

    • // +build ignore,编译时自动忽略该文件

    • demo

      1
      2
      3
      
      // +build linux,arm !darwin
      
      表示该文件在 Linux 平台且 ARM 架构下编译但不在 Darwin 平台下编译
  • 自定义tag, // +build mytag 编译的时候go build -tags mytag才会编译此文件

1.3 3. 注意事项

  • // +build 或者 //go:build 指令必须放在文件的开头。
  • // +build 或者 //go:build 指令只能出现一次,且只能针对整个文件进行编译,不能对单独的函数或变量进行编译。
  • 标记中的操作系统和 CPU 架构必须使用官方定义的名称,否则编译器无法识别。
  • 在编写代码时,应该尽量避免使用条件编译,以保持代码的简洁和易读性。
  • 在使用编译标记时,应该尽量使用官方定义的标记,避免与其他库或框架的标记冲突。

2 文件后缀

  • 这个方法通过改变文件名的后缀来提供条件编译,这种方案比编译标签要简单很多

  • go/build可以在不读取源文件的情况下就可以决定哪些文件不需要参与编译。

  • 简单来说,就是源文件包含后缀:$GOOS.go,那么这个源文件只会在这个平台下编译,$GOARCH.go也是如此。

  • demo

    1
    2
    
    mygo_freebsd_arm.go // only builds on freebsd/arm systems
    mygo_plan9.go       // only builds on plan9
    
Buy me a coffee~
Fred 支付宝支付宝
Fred 微信微信
0%