Golang编译选项使用简介

使用Golang编译选项可以根据不同的选项进行不同代码逻辑的编译,这样可以按需编译,减少运行时的代码。使用方式可以在文件头部编写//go:build *这类标记。

新建两个文件,foo.gobar.go:

//go:build foo

// this is foo.go
package debug

import "fmt"

const FooEnabled = true

func Print(){
    fmt.Println("foo")
}
//go:build !foo

// this is bar.go

package debug

import "fmt"

const FooEnabled = false 

func Print(){
    fmt.Println("bar")
}

在main.go中调用并运行代码可以发现默认的情况时使用!foo这个编译选项。

package main

import "./debug"

func main(){
    debug.Print()
}

运行之后可以看到控制台打印了bar

$ GO111MODULE=off go run main.go
bar

-tags携带指定参数来确定编译foo.go文件:

$ GO111MODULE=off go run -tags foo main.go
foo

如上所示,可以通过设置不同的编译选项来编译不同的文件,实现按需编译。但是每段逻辑都创建不同文件的话,可能粒度会比较大,在一些需要粒度控制较小的逻辑下不是很适用,有没有可能类似C语言那样在Golang中通过一个常量来决定编译某部分代码吗?

答案是可以的,有兴趣的可以参考Golang本身支持的race竞态检测的实现。下面还是继续利用foo.gobar.go文件,分别添加两个常量,上面的文件可以看到FooEnabled这个常量,然后在main.go
根据这个常量编写不同的流程。

func main() {
    if debug.FooEnabled {
        fmt.Println("foo flow")
    }
}

由于在main.go文件加入了判断语句,那会不会运行时去执行判断呢?可以看看生成的代码,没有带指定编译选项的时候,也就是debug.FooEnabledfalse时:

$ GO111MODULE=off go build  -gcflags=-S main.go

# command-line-arguments
"".main STEXT nosplit size=1 args=0x0 locals=0x0 funcid=0x0 align=0x0
    0x0000 00000 (main.go:9)    TEXT    "".main(SB), NOSPLIT|ABIInternal, $0-0
    0x0000 00000 (main.go:9)    FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (main.go:9)    FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (main.go:13)   RET
    0x0000 c3                                               .

可以看到main逻辑中,并没有判断逻辑和打印语句的逻辑部分。继续看看携带特定编译参数生成的代码:

$ GO111MODULE=off go build -tags foo -gcflags=-S main.go
# command-line-arguments
"".main STEXT size=103 args=0x0 locals=0x40 funcid=0x0 align=0x0
    0x0000 00000 (main.go:9)    TEXT    "".main(SB), ABIInternal, $64-0
    0x0000 00000 (main.go:9)    CMPQ    SP, 16(R14)
    0x0004 00004 (main.go:9)    PCDATA  $0, $-2
    0x0004 00004 (main.go:9)    JLS 92
    0x0006 00006 (main.go:9)    PCDATA  $0, $-1
    0x0006 00006 (main.go:9)    SUBQ    $64, SP
    0x000a 00010 (main.go:9)    MOVQ    BP, 56(SP)
    0x000f 00015 (main.go:9)    LEAQ    56(SP), BP
    0x0014 00020 (main.go:9)    FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0014 00020 (main.go:9)    FUNCDATA    $1, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB)
    0x0014 00020 (main.go:9)    FUNCDATA    $2, "".main.stkobj(SB)
    0x0014 00020 (main.go:11)   MOVUPS  X15, ""..autotmp_8+40(SP)
    0x001a 00026 (main.go:11)   LEAQ    type.string(SB), DX
    0x0021 00033 (main.go:11)   MOVQ    DX, ""..autotmp_8+40(SP)
    0x0026 00038 (main.go:11)   LEAQ    ""..stmp_0(SB), DX
    0x002d 00045 (main.go:11)   MOVQ    DX, ""..autotmp_8+48(SP)
    0x0032 00050 (<unknown line number>)  NOP
    0x0032 00050 ($GOROOT/src/fmt/print.go:274) MOVQ    os.Stdout(SB), BX
    0x0039 00057 ($GOROOT/src/fmt/print.go:274) LEAQ    go.itab.*os.File,io.Writer(SB), AX
    0x0040 00064 ($GOROOT/src/fmt/print.go:274) LEAQ    ""..autotmp_8+40(SP), CX
    0x0045 00069 ($GOROOT/src/fmt/print.go:274) MOVL    $1, DI
    0x004a 00074 ($GOROOT/src/fmt/print.go:274) MOVQ    DI, SI
    0x004d 00077 ($GOROOT/src/fmt/print.go:274) PCDATA  $1, $0
    0x004d 00077 ($GOROOT/src/fmt/print.go:274) CALL    fmt.Fprintln(SB)
    0x0052 00082 (main.go:13)   MOVQ    56(SP), BP
    0x0057 00087 (main.go:13)   ADDQ    $64, SP
    0x005b 00091 (main.go:13)   RET
    0x005c 00092 (main.go:13)   NOP
    0x005c 00092 (main.go:9)    PCDATA  $1, $-1
    0x005c 00092 (main.go:9)    PCDATA  $0, $-2
    0x005c 00092 (main.go:9)    NOP
    0x0060 00096 (main.go:9)    CALL    runtime.morestack_noctxt(SB)
    0x0065 00101 (main.go:9)    PCDATA  $0, $-1
    0x0065 00101 (main.go:9)    JMP 0

可以看到这部分生成了打印语句的代码,综上所述,如果想根据编译选项来生成不同的代码,而不是在运行时进行判断,可以根据场景灵活地实现。

Golang编译选项使用简介》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

15 + 17 =