Vous êtes sur la page 1sur 6

22/04/2019 Introduction into C++ Builds with Gradle - CodeProject

Introduction into C++ Builds with Gradle


thoughts-on-cpp, 10 Apr 2019

A short introduction into the modern build system gradle

Welcome back to a new post on thoughts-on-cpp.com. In today’s post, I would like to give an introduction to the build system
Gradle and how we can use it to build native applications and libraries. Gradle is originally coming from the Java world, but it’s also
supporting native build toolchains for quite a while. Gradle is becoming more and more popular in the Java world and is on a good
way to rule out the old bull Maven. This is because of two features which we can also benefit from in the native (C/C++, Objective-
C/C++, Assembly, and Windows resources) build world. These features are Gradle’s easy to maintain and very expressive Groovy (or
Kotlin if preferred) based DSL, and its capabilities of dependency resolving via online and on-premise library providers (such as
maven-central, Artifactory, Bintray, etc.) or local repositories.

Let’s start with a multi-project example we already know from my post Introduction into an Automated C++ Build Setup with
Jenkins and CMake. I have just slightly changed the example hello world application. This time, the main function is printing out
“Hello World!” onto console using a shared library, called greeter. The Greeter class itself is utilizing the external library
{fmt} to print “Hello World” onto the screen. If you’ve wondered about the Gradle directory and files, those are provided by the
Gradle wrapper which facilitates us to build the project without even installing Gradle upfront.

1 .
2 ├── app
3 │ ├── build.gradle
4 │ └── src
5 │ └── main
6 │ └── cpp
7 │ └── main.cpp
8 ├── build.gradle
9 ├── gradle
10 │ └── wrapper
11 │ ├── gradle-wrapper.jar
12 │ └── gradle-wrapper.properties
13 ├── gradlew
14 ├── gradlew.bat
15 ├── greeter
16 │ ├── build.gradle
17 │ └── src
18 │ └── main
19 │ ├── cpp
20 │ │ └── greeter.cpp
21 │ └── public
22 │ └── greeter.hpp
23 ├── LICENSE
24 ├── README.md
25 ├── settings.gradle
26 └── testLib
27 ├── build.gradle
28 └── src
29 └── test
30 └── cpp
31 └── greetertest.cpp

https://www.codeproject.com/Articles/1406275/Introduction-into-Cplusplus-Builds-with-Gradle?display=Print 1/6
22/04/2019 Introduction into C++ Builds with Gradle - CodeProject

The Gradle native build plugin is quite straight forward to configure. Every Gradle project needs a build.gradle file at its root
directory as an entry point and one at each subproject. In most cases, we will do general configurations in a build.gradle file located
at the root directory. But there is no need, it can also be empty. By default, Gradle is looking for sources in the directory
src/main/cpp. For libraries, public headers are defined in src/main/public and in case they should be used only library internal
(private) the default directory is src/main/headers. In case we define the headers also in src/main/cpp, the headers are treated as
private as well. If we like to overwrite the default source directories, we just need to define them according to this example. To be
able to resolve dependencies between subprojects, we need to define a settings.gradle file which is including our subprojects
include 'app', 'greeter', 'testLib'.

sources {
cpp {
source {
srcDirs "src/main/cpp", "src/shared/c++"
include "**/*.cpp"
}
exportedHeaders {
srcDirs "src/main/include", "src/shared/include"
}
}
}

As build definition of our root directory, we just simply define IDE support for each subproject. CLion, for example, has native Gradle
support, so importing Gradle projects works smooth as silk. Therefore, our root build.gradle file looks like the following:

allprojects {
apply plugin: 'xcode'
apply plugin: 'visual-studio'
}

The application build configuration is defined at the app directory starting with calling the cpp-application plugin which is
generating an executable file which can be found and executed at
app/build/install/main/{buildType}/{machine}. Project internal dependencies can be defined by the
dependencies clause with the implementation of the dependency defined as a project and the given name of the dependency.
By default, Gradle is assuming the current host as target machine. If we want to consider other target machines, we have to declare
them as we do in our example with the targetMachines statement.

1 plugins {
2 id 'cpp-application'
3 }
4
5 application {
6 dependencies {
7 implementation project(':greeter')
8 }
9
10 targetMachines = [
11 machines.windows.x86_64,
12 machines.macOS.x86_64,
13 machines.linux.x86_64
14 ]
15
16 baseName = "app"
17 }

The library build configuration is defined at the greeter directory starting with calling the cpp-library plugin and the type of
linkage, which can be STATIC and SHARED. Gradle is assuming we want SHARED libraries as default. A bit special is the way in
which we have to resolve the dependency to the header only library {fmt}. Unfortunately, Gradle is not supporting header only
libraries out of the box, but we can accomplish a workaround by adding the include path to the
includePathConfiguration of the resulting binary. All other dependencies can be defined as api, in case we want to
share the external dependency API with all consumers of our own defined library, or implementation in case we only want to
use the dependency API private with our own library. A good example can be found in Gradle’s example repository.

1 plugins {
2 id 'cpp-library'
https://www.codeproject.com/Articles/1406275/Introduction-into-Cplusplus-Builds-with-Gradle?display=Print 2/6
22/04/2019 Introduction into C++ Builds with Gradle - CodeProject

3 }
4
5 library {
6 linkage = [Linkage.SHARED]
7
8 targetMachines = [
9 machines.windows.x86_64,
10 machines.macOS.x86_64,
11 machines.linux.x86_64
12 ]
13
14 baseName = "greeter"
15 }
16
17 def fmtHeaders = file("$rootDir/../fmt/include")
18
19 components.main.binaries.whenElementFinalized { binary ->
20 project.dependencies {
21 if (binary.optimized) {
22 add(binary.includePathConfiguration.name, files(fmtHeaders))
23 } else {
24 add(binary.includePathConfiguration.name, files(fmtHeaders))
25 }
26 }
27 }

With Gradle, we can not only build applications and libraries, but we can also execute tests to check the resulting artifacts. A test can
be defined by the cpp-unit-test plugin which is generating a test executable. In principle, we could use any of the existing
big test libraries, such as googletest, but in my opinion, the out of the box solution is pretty neat and lightweight and can be
extended quite easily with external libraries.

1 plugins {
2 id 'cpp-unit-test'
3 }
4
5 unitTest {
6 dependencies {
7 implementation project(':greeter')
8 }
9
10 baseName = "greeterTest"
11 }

With this project setup, we can build all artifacts by the command ./gradlew assemble and run tests by ./gradlew
check. If we want to build and run all tests together, we can invoke ./gradlew build. In case we need a list of all available
tasks provided by Gradle and its plugins, we can simply list them including their description by the command ./gradlew
tasks. At GitHub, you can find the resulting repository.

1 [bmahr@localhost gradleNative]$ ./gradlew tasks


2
3 > Task :tasks
4
5 ------------------------------------------------------------
6 Tasks runnable from root project
7 ------------------------------------------------------------
8
9 Build tasks
10 -----------
11 assemble - Assembles the outputs of this project.
12 build - Assembles and tests this project.
13 clean - Deletes the build directory.
14
15 Build Setup tasks
16 -----------------
17 init - Initializes a new Gradle build.
18 wrapper - Generates Gradle wrapper files.
19
https://www.codeproject.com/Articles/1406275/Introduction-into-Cplusplus-Builds-with-Gradle?display=Print 3/6
22/04/2019 Introduction into C++ Builds with Gradle - CodeProject

20 Help tasks
21 ----------
22 buildEnvironment - Displays all buildscript dependencies declared in root project
'gradleNativ'.
23 components - Displays the components produced by root project 'gradleNativ'. [incubating]
24 dependencies - Displays all dependencies declared in root project 'gradleNativ'.
25 dependencyInsight - Displays the insight into a specific dependency
26 in root project 'gradleNativ'.
27 dependentComponents - Displays the dependent components of components in
28 root project 'gradleNativ'. [incubating]
29 help - Displays a help message.
30 model - Displays the configuration model of root project 'gradleNativ'. [incubating]
31 projects - Displays the sub-projects of root project 'gradleNativ'.
32 properties - Displays the properties of root project 'gradleNativ'.
33 tasks - Displays the tasks runnable from root project 'gradleNativ'
34 (some of the displayed tasks may belong to subprojects).
35
36 IDE tasks
37 ---------
38 cleanVisualStudio
39 cleanXcode - Cleans XCode project files (xcodeproj)
40 openVisualStudio - Opens the Visual Studio solution
41 openXcode - Opens the Xcode workspace
42 visualStudio
43 xcode - Generates XCode project files (pbxproj, xcworkspace, xcscheme)
44
45 Verification tasks
46 ------------------
47 check - Runs all checks.
48 runTest - Executes C++ unit tests.
49
50 Rules
51 -----
52 Xcode bridge tasks begin with _xcode. Do not call these directly.
53 Pattern: clean<TaskName>: Cleans the output files of a task.
54
55 To see all tasks and more detail, run gradlew tasks --all
56
57 To see more detail about a task, run gradlew help --task <task>
58
59 BUILD SUCCESSFUL in 0s
60 1 actionable task: 1 executed

https://www.codeproject.com/Articles/1406275/Introduction-into-Cplusplus-Builds-with-Gradle?display=Print 4/6
22/04/2019 Introduction into C++ Builds with Gradle - CodeProject

Did you like the post?

What are your thoughts?

Feel free to comment and share this post.

License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author


thoughts-on-cpp
Team Leader KISSsoft AG
Switzerland

Passionate C++ developer, mechanical engineer, Head of Software Development at KISSsoft AG and host of https://thoughs-on-
cpp.com

Comments and Discussions

https://www.codeproject.com/Articles/1406275/Introduction-into-Cplusplus-Builds-with-Gradle?display=Print 5/6
22/04/2019 Introduction into C++ Builds with Gradle - CodeProject

0 messages have been posted for this article Visit https://www.codeproject.com/Articles/1406275/Introduction-into-


Cplusplus-Builds-with-Gradle to post and view comments on this article, or click here to get a print view with messages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2019 by thoughts-on-cpp
Web04 | 2.8.190419.4 | Last Updated 10 Apr 2019 Everything else Copyright © CodeProject, 1999-2019

https://www.codeproject.com/Articles/1406275/Introduction-into-Cplusplus-Builds-with-Gradle?display=Print 6/6

Vous aimerez peut-être aussi