How to resolve circular dependencies between libraries at linking with GCC-Toolchain (building native binaries)?

Starting the GCC-linker with command line options “–start-group (list of binaries to link) --end-group” resolves my problem. But what is todo therefore in “build.gradle” ?

I know I can define linker arguments (e.g. linker.withArguments { args -> args << “-Wl,’-rpath=./’”

args << “-ggdb3” } ) in build.gradle. However all the arguments are added behind.

As result I need a linker-options-file (e.g. .\build\tmp\linkMyMainCompExecutable\options.txt) generated by gradle like: -o C:\myPath\build\binaries\myMainCompExecutable\myMainComp -Wl,–start-group C:\myPath\build\objs\myMainCompExecutable\myMainCompC\df8h1vdz03smcte0jep0mvqwy\main.o C:\myPath\build\objs\myMainCompExecutable\myMainCompCpp\clz0s1o76hq3zitmgjk9ad13o\task1.o C:\myPath\build\binaries\cpputilsStaticLibrary\libcpputils.a C:\myPath\build\binaries\osacStaticLibrary\libosac.a -lpthread -Wl,–end-group -Wl,’-rpath=./’ -ggdb3

Important: the object- and library-files determined by gradle must be between “-Wl,–start-group” and “-Wl,–end-group” !

You can modify the list of arguments in withArguments any way you like, not just appending to the end.

linker.withArguments { args ->
    List origArgs = new ArrayList(args)
    args.clear()
    // do magic to recalcuate the argument list
    List newArgs = ["test"]
    args.addAll(newArgs)
}

I think this is going to make your build pretty fragile/sensitive to the argument ordering that Gradle produces. I think it would be better if you could avoid circular dependencies (maybe by sub-dividing your library more?).

Hallo Sterling Greene,

thank you very much for your beneficial response.

Of course I know, to avoid circular dependencies is the best way. But the existing software library has grown over many years. My job is to replace the current used build tool ‘make’ with (e.g) Gradle.

A workaround to group the files for linking, based on your advise looks like this :

linker.withArguments { args ->
    List origArgs = new ArrayList(args)
    List newArgs = new ArrayList()
    origArgs.eachWithIndex { obj, i ->
         newArgs.add(obj)
        if (i == 1) {
// behind the 2.argument insert ‘Start a group’
            newArgs.add("-Wl,--start-group")
        }
    }
    newArgs.add("-lpthread")
    newArgs.add("-Wl,--end-group")
 // insert ‘End a group’
    newArgs.add("-Wl,'-rpath=./'")
    newArgs.add("-ggdb3")
    args.clear()
    args.addAll(newArgs)
}

But as you see, I assume that the first and second argument is “-o” and the binary destination. And the following arguments in ‘origArgs’ are the object and library files supplied by Gradle.

The result will be as I posted yesterday in this topic.

Can I assume the arguments ‘origArgs’ always come in this order ? Or better:

  • Is it possible to get the output file and the list of files to link separately from Gradle ? - Is there a better possibility to surround the object and library files for the linking with “–start-group” and “–end-group” ?

    Best regards Christian

Sorry, the ordering isn’t guaranteed.

You can hook into the LinkExecutable task. I think you could see what the list of objects (and final output) would be. I’m not sure that helps.

The only other idea that comes off the top of my head would be to meld the sources for your two libraries into the executable. Just set them up as additional source sets.

Hallo Sterling Greene,

thank you very much for the help. Because I am a beginner with Gradle one further question: How can I hook into the LinkExecutable task ?

Best regards Christian

You can select it by type:

tasks.withType(LinkExecutable) {
   // configure the LinkExecutable task
 }

How to access to the properties / methods of LinkExecutable ? E.g. If i want to get the property ‘outputFile’ as defined in documentation for LinkExecutable

tasks.withType(LinkExecutable) {
    println "In LinkExecutable ..."
    def test = LinkExecutable.outputFile
}

and start gradle build, I get the error:

Exception thrown while executing model rule: org.gradle.nativeplatform.plugins.NativeComponentModelPlugin$Rules#createNativeBinaries(org.gradle.platform.base.BinaryContainer, org.gradle.api.NamedDomainObjectSet<org.gradle.nativeplatform.NativeComponentSpec>, org.gradle.language.base.internal.LanguageRegistry, org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal, org.gradle.platform.base.internal.PlatformResolver, org.gradle.nativeplatform.BuildTypeContainer, org.gradle.nativeplatform.FlavorContainer, org.gradle.internal.service.ServiceRegistry, java.io.File)

No such property: outputFile for class: org.gradle.nativeplatform.tasks.LinkExecutable Possible solutions: outputFile

: BUILD FAILED

And as i see via the println output, this task is called very early (before compile libraries, … ).

Is it possible to configure the LinkExecutable task at this time ?

You configure it the same way as you do other tasks you’ve defined, e.g.:

task link(type: LinkExecutable) {
  // configuration here
  outputFile = file('to/somewhere/else.exe')
}
  tasks.withType(LinkExecutable) {
  // configuration here
  outputFile = file('to/somewhere/else.exe')
}

An important difference is that it configures all tasks (for that project) with type LinkExecutable instead of just one. You can put this at the top level (outside any other block), but I think you can also put it inside the component’s block to limit it to only tasks of that component (I didn’t try that before posting).

The task isn’t being called early. It’s being configured. The closure is a configuration block and it’s run during Gradle’s configuration phase.

Hello Sterling,

now I come forward. Thank you very much for the support.

Best regards Christian