-->

Modify Variables In Go Binaries During Build

Posted by : at

Category : golang


Image by Percy Bolmér. Gopher by Takuya Ueda, Original Go Gopher by Renée French (CC BY 3.0)

Have you ever used hardcoded version numbers, or tried passing configurations to a binary that states the version of the software?

I have, and it works, but it is very error-prone. It is easy to forget to change that configuration etc or maintain the version across multiple merge requests. 

I finally found a great solution, we can modify variables inside the binaries during the build time by adding build flags. This allows us to set up the CI/CD to pass the git commit as a version etc, or have different releases specify the runner ID based on the deployment. 

Using this approach makes it easier to maintain and control the specific values since we can easily place them inside the CI/CD. We can do this by leveraging the Linker flags.

If you would rather watch this tutorial as a video, you can find it below.

Linker & Ldflags

The go build tool allows us to pass options to the Linker, which is the component responsible for assembling the binary.

We can pass options to the Linker by using the --ldflags flag to the build tool. There are very many options you can pass, but in this article, we will focus on only one of them. --ldflags accepts a string of configurations, so the input will be enclosed by "".

You can view all the options by running the build with the --help option.

go build --ldflags="--help"

All the linker options available to use in the build tool

The option we are interested in using is the -X option. This is the definition from the help print.

-X definition        add string value definition of the form importpath.name=value

As you can see, we can use the -X flag and target the variable we want to modify by using the import path (the path you use to import a package) and using .variableName to target a certain variable using its name, and then the value we want.

Let us try this out with a simple example project.

-X, Setting The Variable Value

Begin by creating a new project, I use the module name programmingpercy.tech/buildflags. This is important to note because it will later be used as an import path.

mkdir buildflags
go mod init programmingpercy.tech/buildflags
touch main.go

We will fill the main.go with a simple function that prints the version and client name.

main.go - The simple program that prints the variabel values.

You can try viewing the output by building and running the program.

go build -o main && ./main
// Outputs
2022/03/08 19:23:28 Starting runner client-1.0.0 version 0.0.1

Let us try rebuilding the binary, but this time we will use the --ldflags and apply the -X command to set the runner into client-2.0.0. The package we work in is main so that will be the import path.

go build -o main --ldflags="-X 'main.runner=client-2.0.0'"

Try running the program again, and view the output change the runner name.

./main
// Outputs
2022/03/08 19:27:21 Starting runner client-2.0.0 version 0.0.1

You can modify multiple flags, all you need to do is add a new -X flag inside the command.

go build -o main --ldflags="-X 'main.runner=client-2.0.0' -X 'main.version=0.0.2'"

You should try running the binary, no surprise, the version is also changed.

Even better, you can also target sub-packages if you want to. To try this, create a subfolder named version, and add a file named version.go which contains the Version variable instead of main.go.

version/version.go- The subpackage that we will be modifying

Make sure we update the main.go to use the amazing new version package.

I don’t actually recommend you having a version package, but you get the idea

main.go - The main package that now uses the build package.

Now we can try to rebuild the binary, and make sure to update the version number using the -X flag. This time, you can pass the full import path, which is the module name. 

go build -o main --ldflags="-X 'programmingpercy.tech/buildflags/version.Version=0.0.2' -X 'main.runner=client-1.0.1'"

Upon running that binary, you should see the values change.

Adding Git Commit Tag As Version

One cool thing is that now when we can modify values during the build, you can automate version handling by using the git commit, etc. 

In the folder of the project, we will init a git repository and commit the current files so we can get a commit hash.

git init
git add .
git commit -m "test"

To print a short commit hash, you can use the following git command.

git rev-parse --short HEAD

We can simply inject that into the -X flag, and set the value of that output.

go build -o main --ldflags="-X 'programmingpercy.tech/buildflags/version.Version=$(git rev-parse --short HEAD)' -X 'main.runner=client-1.0.1'"

Running the program with this binary for me now prints the following result

2022/03/08 20:54:35 Starting runner client-1.0.1 version f525917

Conclusion

The example we use is very simple, but you can do many things with this. I have seen versions being injected, feature flags, and more. One easy solution for feature flags that should differ between clients can be passing a true value for the enabled functions. This is probably the easiest feature flag solution to use, no need for frameworks to manage it.

The best place to have these flags is in the CI/CD according to me, to control different releases, versions, and client ids.

I hope you found this interesting, I found it very useful when I learned this.

Are you using ldflags already, if so, for what? And what are the use cases you can think of? I would love to hear from you regarding your thoughts about these flags.

If you like my writing, feel free to Buy Me Coffee.

Top 5 Reasons Why Programming Is A Good Career Choice »


About Programming Percy
Programming Percy

Hey, I'm Percy Bolmér. I'm a software engineer, writer and tech enthusiast. I've been professionally building software for 7+ years, and absoluetly love sharing any ideas I come across.

Email : programmingpercy@gmail.com

Website : https://programmingpercy.tech

About Percy Bolmér

Hey, I'm Percy Bolmér. I'm a software engineer, writer and tech enthusiast. I've been professionally building software for 7+ years, and absoluetly love sharing any ideas I come across.

Useful Links