使用Golang编译选项可以根据不同的选项进行不同代码逻辑的编译,这样可以按需编译,减少运行时的代码。使用方式可以在文件头部编写//go:build *
这类标记。
新建两个文件,foo.go
与bar.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.go
与bar.go
文件,分别添加两个常量,上面的文件可以看到FooEnabled
这个常量,然后在main.go
中
根据这个常量编写不同的流程。
func main() {
if debug.FooEnabled {
fmt.Println("foo flow")
}
}
由于在main.go
文件加入了判断语句,那会不会运行时去执行判断呢?可以看看生成的代码,没有带指定编译选项的时候,也就是debug.FooEnabled
为false
时:
$ 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
可以看到这部分生成了打印语句的代码,综上所述,如果想根据编译选项来生成不同的代码,而不是在运行时进行判断,可以根据场景灵活地实现。
6