Skip to main content

Cmake for Custom Library Build System in Go

In the previous post, I implemented a Go shim layer that enables c++ codes to use Go functionalities. This post dives a little bit deeper into CMake build system for this interaction.

The following CMakeLists.txt provides a binary compilation altogether with compiling Go based static library.

cmake_minimum_required(VERSION 3.0)
project(test)

set(TARGET_OUT test.out)
set(TARGET_LIB test.lib)

# Go configurations

set(GO_SRCS
  test.go)
set(GO_LIBNAME libtest.a)

# Custom command for 'go build -buildmode=c-archive ...'
# to create a library from Go codes.
add_custom_command(OUTPUT ${GO_LIBNAME}
  DEPENDS ${GO_SRCS}
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMAND env go build -buildmode=c-archive
  -o "${CMAKE_CURRENT_BINARY_DIR}/${GO_LIBNAME}"
  ${CMAKE_GO_FLAGS} ${GO_SRCS}
  COMMENT "Building Go library")

# Add a custom target for the library.
add_custom_target(${TARGET_LIB} DEPENDS ${GO_LIBNAME})

# C++ configurations

set(CPP_SRCS
  test.cpp)

# A library and a header are generated in the binary directory.
include_directories(${CMAKE_CURRENT_BINARY_DIR})
link_directories(${CMAKE_CURRENT_BINARY_DIR})

add_executable(${TARGET_OUT} ${CPP_SRCS})
add_dependencies(${TARGET_OUT} ${TARGET_LIB})
target_link_libraries(${TARGET_OUT} test pthread)

We want to divide Go compilation from c++ build, hence regard them as seperate CMake projects.

Directory structure is as follows.

$ tree .
.
├── CMakeLists.txt
├── go
│   ├── CMakeLists.txt
│   ├── go.mod
│   └── test.go
└── test.cpp

Two CMakeLists.txt look:

# c++ cmake configuration
cmake_minimum_required(VERSION 3.0)
project(test)

add_subdirectory(go)

set(TARGET test.out)
set(SRCS test.cpp)

add_executable(${TARGET} ${SRCS})
add_dependencies(${TARGET} goshim)
target_link_libraries(${TARGET} goshim pthread)
# Go cmake configuration
cmake_minimum_required(VERSION 3.0)
project(test_go)
find_library(golang REQUIRED)

set(TARGET shim_go)
set(GOPATH ${CMAKE_CURRENT_BINARY_DIR})

set(SRCS test.go)
set(LIB libgoshim.a)

add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LIB}
  DEPENDS ${SRCS}
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMAND env GOPATH=${GOPATH} go build -buildmode=c-archive
  -o "${CMAKE_CURRENT_BINARY_DIR}/${LIB}"
  ${CMAKE_GO_FLAGS} ./...
  COMMENT "Building Go library")

add_custom_target(${TARGET} DEPENDS ${LIB} ${HEADER})

add_library(goshim STATIC IMPORTED GLOBAL)
add_dependencies(goshim ${TARGET})
set_target_properties(goshim
  PROPERTIES
  IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${LIB}
  INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR})

CMakeLists.txt for Go is based on the post in Stackoverflow1. The key part is add_library(IMPORTED)2, to tell the library is not generated by CMake system and use the library located outside the project. With IMPORTED_LOCATION and INTERFACE_INCLUDE_DIRECTORIES, CMake can detect where the library (.a or .so) and headers (.h) are.

The library (libgoshim.a) and the header (libgoshim.h) are generated in ${CMAKE_CURRENT_BINARY_DIR}/go, but CMakeLists.txt for c++ does not have to explicitly include this directory. Therefore, CMakeLists.txt is kept simple as above, but at the same time can use target_link_libraries(${TARGET} goshim from C++ cmake, thanks to the command add_library(IMPORTED) from Go cmake.

$ make
Scanning dependencies of target shim_go
[ 33%] Building Go library
[ 33%] Built target shim_go
Scanning dependencies of target test.out
[ 66%] Building CXX object CMakeFiles/test.out.dir/test.cpp.o
[100%] Linking CXX executable test.out
[100%] Built target test.out
$ ./test.out 
Hello from c++!
Hello from Go!

  1. custom target as a target library in cmake: https://stackoverflow.com/q/31274577 ↩︎

  2. Imported Libraries: https://cmake.org/cmake/help/latest/command/add_library.html#imported-libraries ↩︎