## Go-lang and CMake

22-06-2016

At QMUL, we are a cmake shop. I personally love cmake, and how easy it is to integrate into other things like test frameworks, jenkins and the like. Go makes a really good attempt to force you into proper management of your projects but it's a bit of a pain to force into your workflow sometimes. Fortunately, we've managed to get cmake to play nicely with golang's various commands.

I found this project: https://github.com/cpconduce/go_cmake. It consists of a couple of useful modules that basically setup a temporary GO_PATH directory, where all the compilation and install takes place. A CMakeLists.txt file would look something like this:

    cmake_minimum_required(VERSION 2.8)
project(prm)

include(cmake/GolangSimple.cmake)

#Tests
include(cmake/GoTests.cmake)
set_target_for_tests("tests.xml" "coverage.xml")


The buildinfo bit is quite interesting. It contains another CMakeLists.txt file that looks like this:

    add_custom_command(OUTPUT buildinfo
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
-DDEST_DIR=${GOPATH}/src/pass.hpc.qmul.ac.uk/prm -P${CMAKE_SOURCE_DIR}/cmake/PrmBuildInfo.cmake)


This creates a command called buildinfo. The actual command looks like this

    #based on http://stackoverflow.com/questions/3780667/use-cmake-to-get-build-time-svn-revision
find_package(Git REQUIRED)
exec_program(${GIT_EXECUTABLE}${SOURCE_DIR}
ARGS "describe --dirty"
OUTPUT_VARIABLE GIT_REVISION)

set(PRM_REVISION "${GIT_REVISION}") #get the build host site_name(PRM_BUILD_HOST) #get the build timestamp in the default format string(TIMESTAMP BUILD_TIMESTAMP) # write relevant information into template header file(WRITE${CMAKE_CURRENT_BINARY_DIR}/buildinfo.go.txt
"package prm

func GetVersionString() string {
return \"${PRM_REVISION}${PRM_BUILD_HOST} ${BUILD_TIMESTAMP}\" } ") # copy the file to the final header only if the data changes (currently it will be every time due to the times 24 tamp data) execute_process(COMMAND${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/buildinfo.go.txt${D     25 EST_DIR}/version.go)


What we are doing here is automagically generating some go code that is compiled into our final executable, that displays the current version as generated by git. It's this sort of funky glue that really appeals to me with CMake.

One of the problems I had with our system is the executable also relies on another module that lives inside the same project directory as the main module. Basically, the top directory pass.qmul.ac.uk has two subdirs, one is the main executable module and the other is a set of functions that can be used independently but still need to be compiled. To get around this, I aktered the GolangSimple.cmake file I downloaded to have a module style command

    set(GOPATH "${CMAKE_CURRENT_BINARY_DIR}/go") file(MAKE_DIRECTORY${GOPATH})

function(GO_GET TARG)
add_custom_target(${TARG} env GOPATH=${GOPATH} go get ${ARGN}) endfunction(GO_GET) function(GO_COPY MODULE_NAME MODULE_SRC) get_filename_component(MODULE_SRC_ABS "../../${MODULE_SRC}" ABSOLUTE)

message(STATUS "Copying Local Module ${MODULE_SRC_ABS}") add_custom_target(${MODULE_NAME}
COMMAND ${CMAKE_COMMAND} -E copy_directory${MODULE_SRC_ABS} ${GOPATH}/src/${MODULE_SRC})

endfunction(GO_COPY)

get_filename_component(MAIN_SRC_ABS ${MAIN_SRC} ABSOLUTE) add_custom_target(${NAME} ALL DEPENDS ${NAME}) add_custom_command(TARGET${NAME}
COMMAND env GOPATH=${GOPATH} go build -o "${CMAKE_CURRENT_BINARY_DIR}/${NAME}"${CMAKE_GO_FLAGS} ${MAIN_SRC} WORKING_DIRECTORY${CMAKE_CURRENT_LIST_DIR}
DEPENDS ${MAIN_SRC_ABS}) foreach(DEP${ARGN})
add_dependencies(${NAME}${DEP})
endforeach()



Finally, automating our tests so that Jenkins can do it's thing is a big part of this. We have a cmake file that looks a little like this:

    GO_GET(go-junit-report github.com/jstemmer/go-junit-report)
GO_GET(gocov github.com/axw/gocov/gocov)
GO_GET(gocov-xml github.com/AlekSi/gocov-xml)

FUNCTION(SET_TARGET_FOR_TESTS output_test_file output_coverage_file)

DEPENDS go-junit-report gocov gocov-xml

#Run the tests
COMMAND env GOPATH=${GOPATH} go test -v pass.hpc.qmul.ac.uk/prm > tests.out #Run the conversion to xUnit format COMMAND cat${CMAKE_BINARY_DIR}/tests.out | ${GOPATH}/bin/go-junit-report >${CMAKE_BINARY_DIR}/${output_test_file} #Run the coverage tests COMMAND env GOPATH=${GOPATH} ${GOPATH}/bin/gocov test pass.hpc.qmul.ac.uk/prm > gocov.txt #Run the conversion to xml COMMAND cat${CMAKE_BINARY_DIR}/gocov.txt | ${GOPATH}/bin/gocov-xml >${CMAKE_BINARY_DIR}/${output_coverage_file} WORKING_DIRECTORY${CMAKE_BINARY_DIR}
COMMENT "Running tests."
)

# Show info where to find the report
COMMENT "Test report saved in ${output_test_file} and coverage report saved in${output_coverage_file}"