# Using CGO with Pkg-Config And Custom Dynamic Library Locations

- Published: August 31, 2013
- Author: William Kennedy
- URL: https://www.ardanlabs.com/blog/2013/08/using-cgo-with-pkg-config-and-custom.html

Earlier in the month I wrote a post about using <a href="https://www.ardanlabs.com/blog/2013/08/using-c-dynamic-libraries-in-go-programs.html" target="_blank">C Dynamic Libraries in Go Programs</a>. The article built a dynamic library in C and created a Go program that used it. The program worked but only if the dynamic library was in the same folder as the program.<br />
<br />
This constraint does not allow for the use of the <b>go get</b> command to download, build and install a working version of the program. I did not want to have any requirements to pre-install dependencies or run extra scripts or commands after the call to<b> go get</b>. The Go tool was not going to copy the dynamic library into the bin folder and therefore I would not be able to run the program once the <b>go get </b>command was complete. This was simply unacceptable and there had to be a way to make this work.<br />
<br />
The solution to this problem was twofold. First, I needed to use a package configuration file to specify the compiler and linker options to CGO. Second, I needed to set an environment variable so the operating system could find the dynamic library without needing to copy it to the bin folder.<br />
<br />
If you look, you will see that some of the standard libraries provide a package configuration (.pc) file. A special program called <b>pkg-config</b> is used by the build tools, such as gcc, to retrieve information from these files.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/Screen+Shot+2013-08-30+at+11.06.12+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/Screen+Shot+2013-08-30+at+11.06.12+PM.png)</a>

</div>
<br />
If we look in the standard locations for header files, <b>/usr/lib</b> or <b>/usr/local/lib,</b> you will find a folder called <b>pkgconfig</b>. Package configuration files that exist in these locations&nbsp;can be found by the&nbsp;<b>pkg-config</b>&nbsp;program by default.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/Screen+Shot+2013-08-30+at+11.11.20+PM.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/Screen+Shot+2013-08-30+at+11.11.20+PM.png)</a>

</div>
<br />
Look at the <b>libcrypto.pc</b> file and you can see the format and how it provides compiler and linker information.<br />
<br />
This particular file is nice to look at because it includes the bare minimum format and parameters that are required.<br />
<br />
To learn more about these files read this web page:&nbsp;<a href="http://www.freedesktop.org/wiki/Software/pkg-config" target="_blank">www.freedesktop.org/wiki/Software/pkg-config</a><br />
<br />
The prefix variable at the top of the file is very important. This variable specifies the base folder location where the library and include files are installed.<br />
<br />
Something very important to note is that you <u><b>can't use an environment variable to help specify a path location</b></u>. If you do, you will have problems with the build tools locating any of the files it needs. The environment variables end up being provided to the build tools as a literal string. Remember this for later because it is important.<br />
<br />
Run the <b>pkg-config</b> program from a Terminal session using these parameters:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
pkg-config --cflags --libs libcrypto
</div>
<br />
These parameters ask the <b>pkg-config</b> program to show the compiler and linker settings specified in the .pc file called libcrypto.<br />
<br />
This is what should be returned:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
<b>-l</b>crypto <b>-l</b>z
</div>
<br />
Let's look at one of the package configuration files from ImageMagick that I downloaded and installed under <b>/usr/local</b>&nbsp;for a project I am working on:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/Screen+Shot+2013-08-30+at+11.31.27+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/Screen+Shot+2013-08-30+at+11.31.27+PM.png)</a>

</div>
<br />
This file is a bit more complex. You will notice it specifies that the MagickCode library is also required and specifies more flags such as environmental variables.<br />
<br />
When I run the <b>pkg-config</b> program on this file I get the following information back:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
pkg-config --cflags --libs MagickWand<br />
<br />
<b>-f</b>openmp <b>-D</b>MAGICKCORE_HDRI_ENABLE=0 <b>-D</b>MAGICKCORE_QUANTUM_DEPTH=16<br />
<b>-I</b>/usr/local/include/ImageMagick-6 &nbsp;<b>-L</b>/usr/local/lib <b>-l</b>MagickWand-6.Q16<br />
<b>-l</b>MagickCore-6.Q16
</div>
<br />
You can see that the path locations for the header and library files are fully qualified paths. All of the other flags defined in the package configuration file are also provided.<br />
<br />
Now that we know a bit about package configuration files and how to use the <b>pkg-config</b> tool, let's take a look at the changes I made to the project for the&nbsp;<a href="https://www.ardanlabs.com/blog/2013/08/using-c-dynamic-libraries-in-go-programs.html" target="_blank">C Dynamic Libraries in Go Programs</a>&nbsp;post. This project is now using a package configuration file and new cgo parameters.<br />
<br />
Before we begin I must apologize. The dynamic library that I built for this project will only build on the Mac. Read the post I just mentioned to understand why. A pre-built version of the dynamic library already exists in version control. If you are not working on a Mac, the project will not build properly, however all the ideas, settings and constructs still apply.<br />
<br />
<div style="clear: both;">
</div>
Open a Terminal window and run the following commands:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
cd $HOME<br />
export GOPATH=$HOME/keyboard<br />
export PKG_CONFIG_PATH=$GOPATH/src/github.com/goinggo/keyboard/pkgconfig<br />
export DYLD_LIBRARY_PATH=$GOPATH/src/github.com/goinggo/keyboard/DyLib<br />
go get github.com/goinggo/keyboard
</div>
<br />
After you run these commands, you will have all the code from the GoingGo keyboard repository downloaded under a subfolder called keyboard inside your home directory.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/Screen+Shot+2013-08-31+at+4.42.51+AM.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/Screen+Shot+2013-08-31+at+4.42.51+AM.png)</a>

</div>
<br />
<br />
You will notice the Go tool was able to download, build and install the keyboard program. Even though the header file and dynamic library was not located in the default&nbsp;<b>/usr</b>&nbsp;or&nbsp;<b>/usr/local</b>&nbsp;folders.<br />
<br />
In the bin folder we have the single executable program without the dynamic library. The dynamic library is only located in the DyLib folder.<br />
<br />
There is a new folder in the project now called pkgconfig. This folder contains the package configuration file that makes this all possible.<br />
<br />
The main.go source code file has been changed to take advantage of the new package configuration file.<br />
<div style="clear: both;">
</div>
<br />
If we immediately switch to the bin folder and run the program, we will see that it works.<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
cd $GOPATH/bin<br />
./keyboard
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">

<a href="../../../images/goinggo/Screen+Shot+2013-08-20+at+3.34.39+PM.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/Screen+Shot+2013-08-20+at+3.34.39+PM.png)</a>

</div>
<br />
When you start the program, it immediately asks you to enter some keys. Type a few letters and then hit the letter q to quit the program.<br />
<br />
This is only possible if the OS can find all the dynamic libraries this program is dependent on.<br />
<br />
<br />
Let's take a look at the code changes that make this possible. Look at the main.go source code file to see how we reference the new package configuration file.<br />
<br />
This is the original code from the first post. In this version I specified the compiler and linker flags directly. The location of the header and dynamic library are referenced with a relative path.<br />
<br />
<div style="clear: both;">
</div>
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
package main<br />
<br />
/&#42;<br />
#cgo CFLAGS: -I../DyLib<br />
#cgo LDFLAGS: -L. -lkeyboard<br />
#include &lt;keyboard.h&gt;<br />
&#42;/<br />
import &quot;C&quot;
</div>
<br />
This is the new code. Here I tell CGO to use the <b>pkg-config</b> program to find the compiler and linker flags. The name of the package configuration file is specified at the end.<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
package main<br />
<br />
/&#42;<br />
<b>#cgo pkg-config: --define-variable=prefix=. GoingGoKeyboard</b><br />
#include &lt;keyboard.h&gt;<br />
&#42;/<br />
import &quot;C&quot;
</div>
<br />
Notice the use of the <b>pkg-config</b> program option <b>--define-variable</b>. This option is the trick to making everything work. Let's get back to that in a moment.<br />
<br />
Run the&nbsp;<b>pkg-config</b> program against our new package configuration file:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
pkg-config --cflags --libs GoingGoKeyboard<br />
<br />
-I<span style="color: #990000;"><b>$GOPATH</b></span>/src/github.com/goinggo/keyboard/DyLib<br />
-L<span style="color: #990000;"><b>$GOPATH</b></span>/src/github.com/goinggo/keyboard/DyLib -lkeyboard</div>
<br />

<a href="../../../images/goinggo/Screen+Shot+2013-08-31+at+12.20.41+AM.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;" target="_blank"><span class="sr-only">View linked image: Screen Shot</span>![Screen Shot](../../../images/goinggo/Screen+Shot+2013-08-31+at+12.20.41+AM.png)</a>

<br />
If you look closely at the output from the call,<b>&nbsp;</b>you will see something that I told you was wrong. The $GOPATH environment variable is being provided.<br />
<br />
Open the package config file which is located in the pkgconfig folder and you will see the&nbsp;<b>pkg-config</b>&nbsp;program doesn't lie. Right there at the top I am setting the prefix variable to a path using $GOPATH. So why is everything working?<br />
<br />
<div style="clear: both;">
</div>
Now run the command again using the same&nbsp;<b>--define-variable</b>&nbsp;option we are using in main.go:<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
pkg-config --cflags --libs GoingGoKeyboard <b>--define-variable=prefix=</b><span style="color: #990000;"><b>.</b></span><br />
<br />
-I./DyLib<br />
-L./DyLib -lkeyboard&nbsp;</div>
<br />
Do you see the difference? In the first call to the <b>pkg-config</b> program we get back paths that have the literal $GOPATH string because that is how the prefix variable is set. In the second call we override the value of the prefix variable to the current directory. What we get back is exactly what we need.<br />
<br />
Remember this environment variable that we set prior to using the Go tool?<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
PKG_CONFIG_PATH=$GOPATH/src/github.com/goinggo/keyboard/pkgconfig
</div>
<br />
The PKG_CONFIG_PATH environment variable tells the&nbsp;<b>pkg-config</b>&nbsp;program where it can find package configuration files that are not located in any of the default locations. This is how the <b>pkg-config</b> program is able to find our GoingGoKeyboard.pc file.<br />
<br />
The last mystery to explain is how the OS can find the dynamic library when we run the program. Remember this environment variable that we set prior to using the Go tool?<br />
<br />
<div style="background-color: #efefef; font-family: 'Courier New', Courier, monospace; font-size: small; overflow-x: scroll; padding: 10px; white-space: nowrap;">
export DYLD_LIBRARY_PATH=$GOPATH/src/github.com/goinggo/keyboard/DyLib
</div>
<br />
The DYLD_LIBRARY_PATH environment variable tells the OS where else it can look for dynamic libraries.<br />
<br />
<div style="clear: both;">
</div>
Installing your dynamic libraries in the <b>/usr/local </b>folder keeps things simple. All of the build tools are configured to look in this folder&nbsp;by default. However, using the default locations for your custom or third party libraries require extra steps of installation prior to running the Go tools. By using a package configuration file and passing the <b>pkg-config</b> program the options it needs, Go with CGO can deploy builds that will install and be ready to run instantly.<br />
<br />
Something else I didn't mention is that you can use this technique to install 3rd party libraries that you may be trying out in a temp location. This makes it real easy to remove the library if you decide you don't want to use it.<br />
<br />
If you want to play with the code or concepts on a Windows or Ubuntu machine, read&nbsp;<a href="https://www.ardanlabs.com/blog/2013/08/using-c-dynamic-libraries-in-go-programs.html" target="_blank">C Dynamic Libraries in Go Programs</a>&nbsp;to learn how to build your own dynamic libraries that you can experiment with.
