Gradle C++ compile notes

Notes from working with the Gradle C++ plugin
Posted: Tue 07 Feb, 2017, 15:49

Ensure precompiler definitions are correctly specified

For example this work not work

    cppCompiler.args "/D NCURSES_STATICLIB"
    

Due to the space between the /D and definition. There must not be any spaces, as shown here:

    cppCompiler.args "/DNCURSES_STATICLIB"
    

Building a dependency on execution stage tasks

In order to build a dependency between the C++ compile jobs and jobs that copy in dependencies, then use the following command structure:

    tasks.withType(CppCompile) { dependsOn tasks.withType(Copy) }
    

Without this, the C++ code would compile before all the dependencies had been copied in.

Beware of parallel compilation

If you have declared any precompile header file then there may be an issue with multiple compiler process simultaneously accessing this file when running on Windows. By default, the plugin will attempt to parallel compilations. You can switch this off using --max-workers.

What is the difference between shared and static linkage

This one I had definitely forgotten. Static linkage is where the objects in the library file are linked at the linker stage and then embedded into executable. The static libraries are then not needed at runtime. Shared libraries are where the objects are linked and then not included in the executable, meaning they have to be available at runtime. As described here.

Interpretting unresolved external symbol

My default response to the dreaded LNK2019: unresolved external symbol error is to be very confused. The reason for this is because the message is very obtuse for some reason but there is a pattern within it that makes sense.

    <File being linked> : error LNK2019: unresolved external symbol "<object the linker could not find>" 
    (<index to this object - not important and very confusing>) 
    referenced in function 
    "<the function that referenced this symbol>"
    (<the index to the function that referenced the symbol - again not very useful and makes it less readable)
    

Make sure all .lib files are referenced during linking

For static compilation only some files maybe required, but during it may be necessary to define all the accompanying .lib files to avoid all LNK2019 issues.

Include Windows .lib files as required

By increasing the Tools/Options --> Projects/Solutions -> Build and Run project output to Detailed, it is possible to retrieve the Visual Studio linker command. When compared to the default gradle generated command, you may find that some core libraries are missing e.g. WinSock32.lib or ws2_32.lib. If so, then just add then to the linker args by doing:

    linker.args "WSockl32.lib"
    linker.args "ws2_32.lib"
    

Finally, you may get configuration that looks something like this:

    model {
    platforms {
        x64 {
            architecture "x86_64"
        }
    }
    buildTypes {
        release
    }
    binaries {
        withType(SharedLibraryBinarySpec) {
            if (toolChain in VisualCpp) {
                cCompiler.args "/Zi"
                cCompiler.define "DLL_EXPORT"
            }
        }
    }
    repositories {
        libs(PrebuiltLibraries) {
            protobuf {
                headers.srcDir "include/ncurses"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${rootProject.projectDir}/lib/ncurses/ncurses.lib")
                }
            }

        ....

        // Shared lib
        dioDllLibrary(NativeLibrarySpec) {
            targetPlatform "x64"
            sources {
                cpp {
                    source {
                        srcDirs "dioDir"
                        include "**/dio_shared.cpp"
                    }
                    exportedHeaders {
                        srcDirs "dioDir"
                        include "**/dio.h", "**/dio_extra.h"
                    }
                    lib library: 'ncurses', linkage: 'static'
                }
            }
            binaries {
                all {
                    cppCompiler.args "/MD"
                    cppCompiler.args "/EHsc"
                    cppCompiler.args "/SUBSYSTEM:WINDOWS"
                    cppCompiler.args "/DLIB_EXPORTS"
                    linker.args "WSock32.lib"
                    linker.args "ws2_32.lib"
                    linker.args "wldap32.lib"
                    linker.args "advapi32.lib"
                }
            }
            binaries.withType(StaticLibraryBinarySpec) {
                buildable = false
            }
        }

        // Executble
        dio(NativeExecutableSpec) {
            targetPlatform "x64"
            sources {
                cpp {
                    source {
                        srcDirs "dio"
                        include "**/dio.cpp"
                    }
                    exportedHeaders {
                        srcDirs "dio/src/cpp"
                        include "**/dio.h"
                    }
                    lib library: 'ncurses', linkage: 'static'
                }
            }
            binaries {
                all {
                    cppCompiler.args "/MD"
                    cppCompiler.args "/EHsc"
                }
            }
        }
    ....