DEV Community

George Calianu
George Calianu

Posted on

Conditional compilation and runtime module names in Go

#go

Conditional compilation in Go language is a very nice feature but what if we want to know at runtime what modules we just compiled (do not confuse with the new Go modules!). We can define a file as a particular module of our application. So, knowing that if the files are compiled or not can be interesting but we haven't a standard mechanism to do this at runtime.

Let supose we have three dummy files main.go, module1.go and module2.go doing nothing and part of package main.

main.go file

package main

func main() {
}

module1.go file

//+build module1

package main

module2.go file

//+build module2

package main

Build arguments are given at compile time as tags, eg.:

 go build -tags="module1 module2" -o test

Unfortunately the build args are no more accesible after the compiling process, nowhere are writen what was compiled and what not. If we want for example at runtime to print the efective compiled files we simply can't.

Here it come the tricky part. In Go runtime package we have the Caller() function who knows the current invocation file and can be used to identify if the file is compiled or not. So, our dummy program become like following.

main.go file

package main

import (
    "fmt"
    "path/filepath"
    "runtime"
    "strings"
)

var mods []string

// include this in every init() function to detect linked modules
func _init() {
    _, file, _, _ := runtime.Caller(1)
    mods = append(mods, strings.TrimSuffix(filepath.Base(file), ".go"))

}
func init() {
    _init()
}

func main() {
    fmt.Println(mods)
}

module1.go file

//+build module1

package main

func init() {
    _init()
}

module2.go file

//+build module2

package main

func init() {
    _init()
}

We has defined _init() function which we will include in every init() functions from every module. At runtime init() functions will update an array of names which can be easy printed. The Caller() parameter mean the nesting level.

Running again.

go build -tags="module1 module2" -o test
./test
[main module1 module2]
go build -tags="module1" -o test
[main module1]

This technique suppose that you will have a consistency between the file name and build tags, so, be care to this aspect to avoid confusion.

Enjoy.

Top comments (0)