The client library, part 4: How to write a simple MySQL client in C using CMake and pkg-config


The client library — libmysqlclient — is part of the same source code repository as the server. It comes both as a dynamic link library (.so), also called a shared library, and as a static link library (.a). During the 5.7 development cycle, we’ve made several improvements to the library, which has resulted in a bump from ABI version 18 to version 20. This is the fourth and final part in a series of blog posts focusing on the client library. If you want to read the whole series, go back to the first post, called The API, the whole API and nothing but the API.

In my previous post, I covered a few changes we’ve made to the build process. This time, we’re going to use some of that to build a simple example application using modern tools.

Hello, MySQL World

We’re going to write a simple application in C that connects to a MySQL server and executes queries. The entire source code of this project can be downloaded from


We’re going to use the following tools:

The installation details are different for every OS, so it would take a whole new series of blog posts to explain how to install all these. I’m simply going to assume that they are installed.

The source code

The application itself is very simple: It asks for host name, port number, user name, password and database name. It then connects to the server and lets you execute queries. The full source code is in the file hello.c in the GitHub repo.

Our simple application will be dynamically linked with the client library.

Using CMake to build our application

CMake is a cross-platform build system. The goal of this blog post is to demonstrate how simple it is to add a libmysqlclient dependency to CMake projects using pkg-config, not to teach CMake basics, but I’ll explain a little about each line anyways.

Our CMake script looks like this:

Line 3 tells you which CMake version is required to run this script. I have 3.0.2, so I put 3.0 there. It might also work with older versions if you lower that number.

Line 4 says that this project is called hello-mysql-world and that it’s written in C. And since I’m using C99, I’ve added the C flag for that in line 5.

The next few lines is where the pkg-config magic happens. In line 7, we include the FindPkgConfig module, a CMake module that uses pkg-config to find libraries. Then, in line 8, we use the pkg_check_modules command provided by that module to find the libmysqlclient library. Let’s go through the parameters one by one.

The result of the pkg_check_modules command is that a number of variables will be set, and the first parameter to the command is the prefix for those variables. I’ve chosen LIBMYSQLCLIENT so that my variables will be called LIBMYSQLCLIENT_FOUND (which is set to 1 if the library is found), LIBMYSQLCLIENT_LIBRARIES (which is the list of libraries to link with), etc. A full list of variables can be found in the FindPkgConfig documentation.

The second parameter, REQUIRED, says that CMake should fail if the library isn’t found. Our application won’t build without the library, so I’ve chosen to fail fast if pkg-config can’t locate the library.

Third and last is the name of the library, or module, as it is called in pkg-config terms. Typically, this is the same as what you specify in the -l option on the linker command line, i.e., the name of the library without the “lib” prefix. In our case, that means “mysqlclient”.

The FindPkgConfig module will call pkg-config to find out everything it can about the library and set CMake variables for everything.

Lines 10–12 loop through the LIBMYSQLCLIENT_CFLAGS_OTHER list and append each flag to CMAKE_C_FLAGS. The list is created by pkg_check_modules and contains all the C flags that are needed to compile, except the flags for include directories and linking, which we will specify in a different way.

Line 14 tells CMake to tell the linker to look for libraries in the directory where pkg_check_modules found the libmysqlclient library.

Lines 16–18 specifies that hello.c will be compiled to make an executable called “hello”, and to build it we need to include header files from LIBMYSQLCLIENT_INCLUDE_DIR and link with the libraries listed in LIBMYSQLCLIENT_LIBRARIES. Both these variables are set by the pkg_check_modules command.

Building and running

Now that we have both the source code and the build script, we can try to build the application:

As we can see in line 18, CMake finds libmysqlclient version 20.0.9 and is able to build the application. Let’s try it out!

That’s it! We wrote a simple MySQL client and used CMake and pkg-config to build it.

In part 2 of this series, we talked about what the version number means and how the linker uses it to find the right library. Let’s look at the library dependencies for our hello application:

In line 3 we see that the application depends on, which is the 5.7 client library. This means that we’ve linked this applications with version 20 of the client library, and we only know that version 20 will work. In this case I happen to know that version 18, and maybe even older version, works, but the linker can’t take the chance on that. If I want to use another version, I have to recompile.


This concludes our four-part series of libmysqlclient blog posts. We’ve covered several topics relating to shared libraries in general and the MySQL client library in particular:

And in the end, we summed it all up in this final example. I hope you’ve learned something!

If you have any questions or comments, please use the comment field below.

About Norvald H. Ryeng

Norvald has been working as a software engineer on the MySQL Optimizer Team since 2011, where he mostly works on GIS. He is also the point of contact for package maintainers in Linux distributions. He holds a PhD in Computer and Information Science from the Norwegian University of Science and Technology.

10 thoughts on “The client library, part 4: How to write a simple MySQL client in C using CMake and pkg-config

  1. I would not call this an example of how write a good CMake.

    target_include_directories(foo ${LIBMYSQLCLIENT_INCLUDE_DIRS})
    target_link_libraries(foo ${LIBMYSQLCLIENT_LIBRARIES})

    shall be sufficient, if it is not, you pkgconfig stuff might be broken

    If MySQL it using external libraries and headers in CMake, it does not set any extra compile options just because it needs to link with an external library. Like, you do not want to set compile flags exactly the same way zlib was compiled, if you link with zlib. You just want its headers and libraries.

    1. So you’re saying lines 10-12 of CMakeLists.txt are unnecessary or maybe even wrong?

      I’m not a CMake or pkg-config expert, but the way I understand it, you’re supposed to add the C flags you get from pkg-config to your compiler flags when linking with the library (see and

      The LIBMYSQLCLIENT_CFLAGS_OTHER variable in line 11 contains all the necessary C flags except the include and link flags. Those are, as you say, added by target_include_directories and target_link_libraries. This is just the same as when using mysql_config. The output of pkg-config –cflags mysqlclient is the same as mysql_config –cflags.

      It may work just fine to skip this for libmysqlclient, but in the general case, I think they should be added.

  2. Yesm lines 10 to 14 are unneeded, and confuse the reader.
    Neither autotools, nor pkg-config, nor mysql_config recommendations have much relevance for CMake . Using external project means you need its headers and libraries, not the compiler flags. You as user have to decide which compiler flags you want, not let others dictate you which flags to use. In CMake, you do not play with -I is not a “compiler flag”, but you need to correctly set target_include_directories. You should not need link_directories, usually, target_link_libraries should be sufficient. So apart from headers and libraries, what else does one _need_ to link with mysqlclient ?

    1. Lines 10-12: The C flags are needed on some platforms. E.g., on Solaris SPARC, the client library is by default compiled with -m64. If you don’t include those lines, and don’t specify -m64 for your own project, the linking will fail:

      ld: fatal: file /tmp/foo/usr/local/mysql/lib/ wrong ELF class: ELFCLASS64

      I agree that in most cases this isn’t necessary, but as you can see, there are a few.

      1. So, linking with your library shall not automagically enforce client to compile 64 bit. People do want to be in control control about the architecture of the binary they compile . So, if pkgconfig does not find correct libraries for the compile architecture user has chosen, it is broken, is not it?

        1. It’s a good point that we should be able to provide the right .pc file for your architecture. I know it works correctly on Linux (the .pc file ends up in the right arch dependent directory), but I don’t know the details of multi arch on Solaris. Perhaps there’s something to be done wrt. .pc file placement that could improve things.

    2. Line 14: This one is needed if the library is not in a system include directory, e.g., if MySQL has been installed in /opt.

      On my computer with Ubuntu 15.04, libmysqlclient 18.1.0 (i.e., 5.6) is installed. I have installed 5.7 to a non-standard location to test the hello-mysql-world example. If I dont’ set link_directories, it picks the system installed library (18.1.0). I need to set it to get the 20.0.9 library. It has actually used the 20.0.9 header files and linked with 18.1.0.

      In most cases, the app will build correctly without setting link_directories, but in general it should be set to make sure the correct library is included. This is exactly the type of problem pkg-config is meant to solve, so I think it’s prudent to add these details, although they’re mostly not needed.

      1. Are you saying LIBMYSQLCLIENT_LIBRARIES are not full paths? If so, pkg-config support might be more broken that I thought, it would be totally un-CMakeish. Much easier it is be to copy FindMySQL.cmake from elsewhere, than using this kind of pkg-config support.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please enter * Time limit is exhausted. Please reload CAPTCHA.