Native plugin: custom linker args per NativeExecutableSpec or GccLinkerArgsTransformer

hello gradle folks,

I’ve done extensive research and tryal-and-error-coding on this one, so help would be greatly appreciated.

I’m moving a large C codebase from custom scripts (no make) to gradle. So far I’ve managed to configure and build all libraries.

Now I’m stuck on two problems building the executables:

1.) One executable uses a textfile of symbol names to dynamically export as symbols. (linker Option “-E”) which has to be supplied before the -o Option and the list of object-files to link. e.g.
gcc -Wl,-E symbols.exp -o myExe.o -L libraries

2.) Unfortunately there is cyclic dependencies between the libraries sigh, so that i will have to whether supply libraries multiple times as parameters or will have to surround them by
–start-group --end-group
options.

I cannot figure out a standard way to do this, because this discussion entry seems not to work (for me/any more) and i could not find any other method myself of messing with the linker arguments. (I know it is not a standard nor pretty thing to do).

So I dug into the gradle source code a bit where i discovered GccLinkerArgsTransformer (in File GccLinker.java) which is exactly the place where I would like to start messing with in order to satisfy my requirements. Is there any way of replacing/extending that part without registering a whole new toolchain?

Of course I would be grateful for any other thoughts and hints to solve those problems (apart from getting rid of the cyclic dependencies which unfortunately seems to be a lost cause :confused:)

1 Like

hello gradle folks,

asking here helped me after all, though not in the way I expected (yet).

In the documentation Example 56.31. Reconfigure tool arguments there is an example of how to mess with the arguments (along with the hint that it is not a good idea compared to cleanly modeling the dependencies).

Gradle provides a hook that allows the build author to control the exact set of arguments passed to a tool chain executable. This enables the build author to work around any limitations in Gradle, or assumptions that Gradle makes. The arguments hook should be seen as a ‘last-resort’ mechanism, with preference given to truly modelling the underlying domain.

I stumbled upon that before and it did not work. In retrospective I think I missed the surounding tag:

model { 
}

So my current (ugly and very brittle) solution to the above stated problems looks like this:

model {
    toolChains {
        gcc(Gcc) {
            eachPlatform {
                linker.withArguments { args ->
                    List origArgs = new ArrayList(args)
                    List newArgs = new ArrayList()

                    // add the symbol list to the linker if "SrvExecutable" gets linked
                    if (name.contains("SrvExecutable")) {
                        newArgs.add("-Wl,-E")
                        newArgs.add("$generatedSourceAbsolutePath/srvall.exp")
                    }

                    // add --start-group und --end-group in order to allow the linker to resolve circular dependencies.
                    boolean addStartGroup = false;
                    boolean startGroupAdded = false;
                    boolean endGroupAdded = false;
                    origArgs.eachWithIndex { obj, i ->
                        String param = obj as String
                        // insert ‘End a group’ after the libraries (the first argument is always "-o")
                        if(startGroupAdded == true
                        && endGroupAdded == false
                        && param.startsWith("-") ) {
                            newArgs.add("-Wl,--end-group")
                            endGroupAdded = true
                        }

                        // add parameter from original list
                        newArgs.add(param)

                        // add --start-group right after the first "-o", "<fileName>"
                        if (startGroupAdded == false && addStartGroup == true) {
                            newArgs.add("-Wl,--start-group")
                            startGroupAdded = true
                        }

                        if(param.equalsIgnoreCase("-o")) {
                            // The parameter "-o" signals that the next parameter will be the name of the outfile. Right after that follows a list of libraries.
                            addStartGroup = true
                        }
                    }

                    args.clear()
                    args.addAll(newArgs)
                }
            }
        }
    }
}

So this solution is pretty much what @sterling and @Christian2 mentioned in How to resolve circular dependencies between libraries at linking with GCC-Toolchain . The only piece that was missing (for me) was the surounding statements

model {
    toolChains {
        gcc(Gcc) {
            eachPlatform {
            }
        }
    }
}