How to pass arguments to gcc instead of ld during the linking step?


(michael.nikonov) #1

Good afternoon!

The problem I’ve recently ran into is related to latest nightly builds. Until recently, I’ve had working ‘build.gradle’:

// ...
model {
  toolChains {
    gcc(Gcc) {
      getCppCompiler().setExecutable 'arm-none-eabi-g++'
      getCCompiler().setExecutable 'arm-none-eabi-gcc'
      getAssembler().setExecutable 'arm-none-eabi-as'
      getLinker().setExecutable 'arm-none-eabi-gcc'
      getStaticLibArchiver().setExecutable 'arm-none-eabi-ar'
    }
  }
}
  // ...
  binaries.all {
    // ...
    linker.args '-mcpu=cortex-m3', '-mthumb', '-Wl,--gc-sections'
    linker.args '--specs=nano.specs'
    // ...
  }
}

Which is probably not elegant at all, but with lacking target architecture, I couldn’t find better way to call proper compiler. The problem in question is, starting with recent nightly builds, Gradle appears to be escaping arguments passed through ‘linker.args’ with ‘-Xlinker’, directing them to linker. Since ‘ld’ wouldn’t accept thumb emulation, nor specs file, I’m now stuck looking for a way to pass those arguments to ‘gcc’ instead of ‘ld’ during the linking. What would be the proper way to do so?


Trying to add a new native platform
#2

All released versions of Gradle have escaped linker args with ‘-Xlinker’. For a while the nightly builds didn’t do this, but that change was reverted for the 1.9 release, since we’re not 100% sure of the way to go here (and we didn’t want to change, then change back).

You have 2 options. Your best bet is probably to use ‘addPlatformConfiguration’ to add support for your target platform. Platform could be identified by name, or by architecture. Something like this (copied from org.gradle.nativebinaries.language.cpp.GccToolChainCustomisationIntegrationTest):

model {

toolChains {

crossCompiler(Gcc) {

addPlatformConfiguration(new ArmArchitecture())

}

} } targetPlatforms {

arm {

architecture “arm”

}

x64 {

architecture “x86_64”

} } class ArmArchitecture implements TargetPlatformConfiguration {

boolean supportsPlatform(Platform element) {

return element.getArchitecture().name == “arm”

}

List<String> getCppCompilerArgs() {

["-m32"]

}

List<String> getCCompilerArgs() {

["-m32"]

}

List<String> getAssemblerArgs() {

[]

}

List<String> getLinkerArgs() {

["-m32"]

}

List<String> getStaticLibraryArchiverArgs() {

[]

} }

An alternative is to use the new ‘withArguments’ hook to modify the linker args generated by the GccToolChain before they are passed to the compiler. Note that this feature is very experimental and everything about it could change in the future!
model {

toolChains {

gcc(Gcc) {

linker.withArguments { List<String> args ->

… do something with the args list (like removing ‘-Xlinker’)…

}





(michael.nikonov) #3

Many thanks! The first variant works fine for me, and it also feels quite coherent in the project; it came handy to move the platform-related arguments where they belong instead of cluttering binaries configuration with them, more so for a multi-platform build. Thank you for the answer.


(Jared Buletza) #4

Thank you very much for this post and answer. Daz, regarding the “experimental” nature of the 2nd solution above, I think that it should be kept, or at least something providing similar functionality should be created. Here’s why…

Sometimes the order of the arguments matters.

I came across this post because my c++ program wasn’t compiling via gradle, and I was getting strange errors (undefined reference CString)… I’m using the cpp-exe on centos6 and gcc/g++. looking at [project]/build/tmp/link[project]Executable/options.txt I saw:

<option1>
<option2>
<option3>
-o
<binary path/name>
<object file1>
<object file2>

I then manually typed:

g++ <option1> <option2> <option3> -o <binary path/name> <object file1> <object file2>

and got the same errors that the gradle build was producing.

I know that my code should compile because I have a Makefile for it that works. When it builds the g++ command looks like this:

g++ -o <binary path/name> <object file1> <object file2> <option 1> <option2> <option3>

So, the solution for me, was, as Daz suggested: “withArguments hooks” in his original response. I do an “add( )” for all my options/arguments there. When gradle runs, all of my arguments are now after the “-o [files]”, and things work for me.

This was really frustrating to discover, and I hope this helps someone else. I also hope Gradle will support appending arguments in a “permanent” fashion, or by keeping the hook in there. The only issue I have at the moment is that the “withArguments” is that I want to use different arguments for different binaries in the same gradle file.


#5

It’s experimental in that the API is likely to change. I think there will always be a need for a low-level hook to tweak the command-line just prior to execution, but the hope is that this will only be required for very unusual edge cases.

A goal of Gradle is to model more things at a higher level, and to correctly translate these concepts into command-line arguments in the correct order. One example is better modelling of library dependencies, so it’s very unusual to specify ‘-L’ or ‘-l’ as args.

Out of interest, what are <option1> <option2> <option3> in your case?


(Jared Buletza) #6

Bad Order:

g++ -mt -dynamic -lm -pthread -lnsl -lc -L[path/to/ourLib1] -L[path/to/ourLib2] -l[ourLib1] -l[ourLib2] -g -o [binary name] [file1.o] [file2.o]

Works:

g++ -o [binary name] [file1.o] [file2.o]
-mt -dynamic -lm -pthread -lnsl -lc -L[path/to/ourLib1] -L[path/to/ourLib2] -l[ourLib1] -l[ourLib2] -g