I’m trying to compile some cpp code using gradle, but I don’t understand how to add linker arguments. With g++ it complies and links in one step, but the gradle compiler seems to do it in two steps. So I need a way to pass the linker arguments, so it knows which libraries to use when linking
plugins {
id 'cpp-application'
}
application {
targetMachines.add(machines.linux.x86_64)
dependencies {
implementation project(':parser')
}
}
model {
toolChains {
gcc(Gcc) {
eachPlatform {
cppCompiler.withArguments { args ->
args << '-std=c++11'
args << '-DPIC64'
args << '-static'
args << '-pthread'
// This needs to be passed to the linker, not the compiler
args << '-ltiff'
args << '-llzma'
args << '-ljpeg'
args << '-ljbig'
args << '-lz'
args << '-lpiclx20'
}
}
}
}
platforms {
x64 {
architecture "x86_64"
}
}
}
As noted in the build.gradle file, the linker arguments are not passed on the g++ when linking the executable. How can I get gradle to use them when linking?
when I do
./gradlew linkRelease
I see:
3:22:19 PM: Executing task 'linkRelease'...
> Task :parser:compileReleaseCpp
> Task :extract_tiles:compileReleaseCpp
> Task :parser:linkRelease
> Task :parser:stripSymbolsRelease
> Task :extract_tiles:linkRelease FAILED
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/home/wsi/dev/workspace-pathology/lib/tiff_tools/parser/extract_tiles/build/obj/main/release/acyfrmxeqecjrfs2pp4llhfk3/extract_tiles.o: In function `wsim::tile_worker(void*)':
/home/wsi/dev/workspace-pathology/lib/tiff_tools/parser/extract_tiles/src/main/cpp/extract_tiles.cpp:50: undefined reference to `TIFFOpen'
/home/wsi/dev/workspace-pathology/lib/tiff_tools/parser/extract_tiles/src/main/cpp/extract_tiles.cpp:56: undefined reference to `TIFFSetDirectory'
/home/wsi/dev/workspace-pathology/lib/tiff_tools/parser/extract_tiles/src/main/cpp/extract_tiles.cpp:182: undefined reference to `_TIFFfree'
/home/wsi/dev/workspace-pathology/lib/tiff_tools/parser/extract_tiles/src/main/cpp/extract_tiles.cpp:110: undefined reference to `TIFFTileSize'
Good news, I am able to compile and link now. The problem was due to my ‘main()’ was inside my namespace declaration. Once I moved it out, everything worked well. Hope this helps future developers.
Thanks @craigtmoore for sharing your progress. With the software model native plugin, it is recommended to configure the compiler/linker flags on the component instead of the tool chain. At the tool chain level, you only want to configure generic platform flags.
Hi Daniel,Can you give an example of how I can add compiler/linker flags on the component level. I read through the gradle documents, but couldn’t find a good example of how to do this.
application { // or library
binaries.configureEach {
compileTask.get().compilerArgs.add('--foo-flag')
linkerTask.get().linkgerArgs.add('--bar-flag') // Only for executable and shared libraries
createTask.get().staticLibArgs.add('--foobar--flag') // Only for static libraries
}
}
How does using binaries.configureEach compare with using tasks.withType(LinkExecutable).configureEach etc.? Is binaries.configureEach the recommended approach?
Using tasks.withType(LinkExecutable).configureEach will configure all tasks in the project that are LinkExecutable. It becomes a problem when different variants of the same application exists. In this case, it will only be possible to know what variant you have by using the name of the task. We never recommend basing configuration on task names as it creates fragile code. Instead, it’s better to depend on the model. The binaries.configureEach route allows you do query the information about the variant itself as well as getting the associated LinkExecutable tasks. Both can be valid, we just try to add the configuration that is the most future-proof.
In the software model, the tasks.withType(LinkExecutable) was even more at a disadvantage as multiple independent component could be found inside the same project which easily broken the original intention of the author.
I’m also trying to set linker args, but when I do what’s in your code snippet (using library instead of application), I get Unresolved reference: linkerTask. Gradle version is 6.1. Is there some updated way to do this? compileTask appears to be working.
This is a common gotcha that would need to be handled in better ways, the binaries for a library contains both CppStaticLibrary and CppSharedLibrary. Only the CppSharedLibrary has the linkerTask as it links to an actual binary. The CppStaticLibrary has a createTask instead. You can find out what binaries will be created by looking at the configuration of linkage on the library. It’s always better to be precise in what you are trying to configure: binaries.configureEach(CppSharedLibrary) { ... }