Chapter 54. C++ Support

The Gradle C++ support is currently incubating. Please be aware that the DSL and other configuration may change in later Gradle versions.

The C++ plugins add support for building software comprised of C++ source code, and managing the process of building “native” software in general. While many excellent build tools exist for this space of software development, Gradle offers C++ developers it's trademark power and flexibility together with the dependency management practices more traditionally found in the JVM development space.

Gradle offers the ability to execute the same build using different tool chains. At present, you control which tool chain will be used to build by changing the operating system PATH to include the desired tool chain compiler. The following tool chains are supported:

Operating SystemTool ChainNotes
LinuxGCC
Mac OS XGCCUsing GCC distributed with XCode.
WindowsGCCWindows XP and later, using GCC distributed with Cygwin.
WindowsVisual C++Windows XP and later, Visual C++ 2010 and later.
WindowsMinGWWindows XP and later.

Currently, there is no direct support for creating multiple variants of the same binary (e.g. 32 bit vs. 64 bit) and there is no direct support for cross platform source configuration (à la autoconf) at this time. Support for different compiler chains, managing multiple variants and cross platform source configuration will be added over time, making Gradle a fully capable build tool for C++ (and other “native” language) projects.

54.1. Source code locations

A C++ project may define a number of source sets, each of which may contain source files and header files. By default, a named CppSourceSet contains .cpp and .c source files in src/${name}/cpp, and header files in src/${name}/headers.

Example 54.1. Defining 'cpp' source sets

build.gradle

cpp {
    sourceSets {
        exe {}
        lib {}
    }
}

For a library named 'main', files in src/main/headers are considered the “public” or “exported” headers. Header files that should not be exported (but are used internally) should be placed inside the src/main/cpp directory (though be aware that such header files should always be referenced in a manner relative to the file including them).

While the cpp plugin defines these default locations for each CppSourceSet, it is possible to extend or override these defaults to allow for a different project layout.

54.2. Component model

A C++ project defines a set of Executable and Library components, each of which Gradle maps to a number of NativeBinary outputs. Each executable or library is associated with a particular CppSourceSet, which contains C++ source files as well as header files.

To build either a static or shared native library binary, a Library component is added to the libraries container and associated with a CppSourceSet. Each library component can produce at least one SharedLibraryBinary and at least one StaticLibraryBinary.

Example 54.2. Defining a library component

build.gradle

libraries {
    hello {
        source cpp.sourceSets.lib
    }
}

To build an executable binary, an Executable component is added to the executables container and associated with a CppSourceSet.

Each executable component added can produce at least one ExecutableBinary. If the executable requires a library for compiling and/or linking, then that library can be provided directly to a binary.

Example 54.3. Defining executable components

build.gradle

executables {
    main {
        source cpp.sourceSets.exe
        binaries.all {
            // Each executable binary produced uses the 'hello' shared library binary
            lib libraries.hello.shared
        }
    }
}

Alternatively, the library dependency can be specified at the level of the the CppSourceSet associated with an executable component:

Example 54.4. Specifying a source-level library

build.gradle

cpp.sourceSets.exe.lib libraries.hello

54.3. Plugins

All build scripts DSLs, model elements and tasks used to manage C++ projects are added by the cpp plugin.

Example 54.5. Applying the 'cpp' plugin

build.gradle

apply plugin: 'cpp'

The cpp plugin allows you to configure any number of libraries and executables for your project. However, at times it is more convenient to use either the cpp-lib or cpp-exe plugins that sit on top of the cpp plugin and pre-configure the project to build a single native library or executable respectively.

Example 54.6. Using the 'cpp-exe' plugin

build.gradle

apply plugin: "cpp-exe"

Example 54.7. Using the 'cpp-lib' plugin

build.gradle

apply plugin: "cpp-lib"

The cpp-exe plugin configures the project to build a single executable named main, and the cpp-lib plugin configures the project to build a shared and static version of a single library named main.

In both cases, a single source set (src/main) containing C++ sources is assumed to exist.

54.4. Tasks

For each NativeBinary that can be produced by a build, the cpp plugin creates a single lifecycle task that can be used to create that binary, together with a set of sub-tasks that do the actual work of compiling, linking or assembling the binary.

Component TypeNative Binary TypeLifecycle taskLocation of created binary
ExecutableExecutableBinarymainExecutable$buildDir/binaries/$project.name
LibrarySharedLibraryBinarymainSharedLibrary$buildDir/binaries/lib$project.name.so
LibraryStaticLibraryBinarymainStaticLibrary$buildDir/binaries/$project.name.a

54.5. Building

54.5.1. Compiling on UNIX

The UNIX C++ support is currently based on the g++ tool which must be installed and on the PATH for the Gradle process.

54.5.2. Compiling on Windows

The Windows C++ support can use either the MinGW g++ or the Microsoft Visual C++ cl tool, either of which must be installed and on the PATH for the Gradle process. Gradle searches first for Microsoft Visual C++, and then MinGW.

54.6. Configuring the compiler and linker

Each binary to be produced is associated with a set of compiler and linker settings, which include command-line arguments as well as macro definitions. These settings can be applied to all binaries, an individual binary, or selectively to a group of binaries based on some criteria.

Example 54.8. Settings that apply to all binaries

build.gradle

binaries.all {
    // Define a preprocessor macro for every binary
    define "NDEBUG"

    // Define toolchain-specific compiler and linker options
    if (toolChain == toolChains.gcc) {
        compilerArgs "-O2", "-fno-access-control"
        linkerArgs "-S"
    }
    if (toolChain == toolChains.visualCpp) {
        compilerArgs "/Z7"
        linkerArgs "/DEBUG"
    }
}

Each binary is associated with a particular ToolChain, allowing settings to be targeted based on this value.

It is easy to apply settings to all binaries of a particular type:

Example 54.9. Settings that apply to all shared libraries

build.gradle

// For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
binaries.withType(SharedLibraryBinary) {
    if (toolChain == toolChains.visualCpp) {
        define "DLL_EXPORT"
    }
}

Furthermore, it is possible to specify settings that apply to all binaries produces for a particular executable or library component:

Example 54.10. Settings that apply to all binaries produced for the 'main' executable component

build.gradle

executables {
    main {
        binaries.all {
            // Define a preprocessor macro
            define "NDEBUG"
            // Add some additional compiler arguments
            if (toolChain == toolChains.gcc) {
                compilerArgs "-fno-access-control", "-fconserve-space"
            }
        }
    }
}

The above example will apply the supplied configuration to all executable binaries built.

Similarly, settings can be specified to target binaries for a component that are of a particular type: eg all shared libraries for the main library component.

Example 54.11. Settings that apply only to shared libraries produced for the 'main' library component

build.gradle

libraries {
    main {
        binaries.withType(SharedLibraryBinary) {
            // Define a preprocessor macro that only applies to shared libraries
            define "DLL_EXPORT"
        }
    }
}

54.7. Working with shared libraries

For each executable binary produced, the cpp plugin provides an install${binary.name} task, which creates a development install of the executable, along with the shared libraries it requires. This allows you to run the executable without needing to install the shared libraries in their final locations.

54.8. Dependencies

Dependencies for C++ projects are binary libraries that export header files. The header files are used during compilation, with the compiled binary dependency being used during the linking.

54.8.1. External Dependencies

External dependencies (i.e. from a repository, not a subproject) must be specified using the following syntax:

Example 54.12. Declaring dependencies

build.gradle

cpp {
    sourceSets {
        main {
            dependency group: "some-org", name: "some-lib", version: "1.0"
        }
    }
}

Each dependency must be specified with the dependency method as above and must be declared as part of the source set. The group, name and version arguments must be supplied.

For each declared dependency, two actual dependencies are created. One with the classifier “headers” and extension “zip” which is a zip file of the exported headers, and another with the classifier “so” and extension “so” which is the compiled library binary to link against (which is supplied as a direct input to the g++ link operation).

54.8.2. Project Dependencies

The notation for project dependencies is slightly different.

Example 54.13. Declaring project dependencies

build.gradle

project(":lib") {
    apply plugin: "cpp-lib"
}

project(":exe") {
    apply plugin: "cpp-exe"
    evaluationDependsOn(":lib")

    cpp {
        sourceSets {
            main {
                lib project(":lib").libraries.main
            }
        }
    }
}

54.9. Publishing

The cpp-exe and cpp-lib plugins configure their respective output binaries to be publishable as part of the archives configuration. To publish, simply configure the uploadArchives task as per usual.

Example 54.14. Uploading exe or lib

build.gradle

group = "some-org"
archivesBaseName = "some-lib"
version = 1.0

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri("${buildDir}/repo"))
        }
    }
}

The cpp-exe plugin publishes a single artifact with extension “exe”. The cpp-lib plugin publishes two artifacts; one with classifier “headers” and extension “zip”, and one with classifier “so” and extension “so” (which is the format used when consuming dependencies).

Currently, there is no support for publishing the dependencies of artifacts in POM or Ivy files. Future versions will support this.