Gradle NativeExecutable from linked libs only

Hello everyone.

What I’m trying to achieve (and actually have but with a “hack” I will mention below) is to create a native executable that does not have source code of its own but instead is created by linking several libs.

Before using gradle the executable was being created with a makefile. In the makefile the following two lines were creating the executables I wanted:

link.exe <linker flags> lib1.obj lib2.obj lib3.obj /out:my_executable_1.exe
link.exe <linker flags> lib1.obj lib4.obj /out:my_executable_2.exe

In my gradle configuration I have the following to create the libs and executables:

model {

    components {

        lib1(NativeLibrarySpec) {}

        lib2(NativeLibrarySpec) {}

        lib3(NativeLibrarySpec) {}

        lib4(NativeLibrarySpec) {}

        my_executable_1(NativeExecutableSpec) {
            sources {
                cpp {
                    lib library: 'lib1', linkage: 'static'
                    lib library: 'lib2', linkage: 'static'
                    lib library: 'lib3', linkage: 'static'
                }
            }
        }

       my_executable_2(NativeExecutableSpec) {
           sources {
               cpp {
                    lib library: 'lib1', linkage: 'static'
                    lib library: 'lib4', linkage: 'static'
                }
            }
       }
   }
}

If I have nothing under the my_executable_1/cpp directory then gradle skips the task saying that it is up to date and no executables are being created.

Although if I place a dummy file (dummy.cpp) in the my_executable_1/cpp directory, then everything work perfectly fine and my executables are being created with no issue.

Is there a way to make this work without the dummy file? If no, do you believe this should be added as a feature (I don’t know how common this scenario can be to be honest)?

Note: If you google my issue, you will see that I have also posted it in stackoverflow. I know I should have posted it here in the first place and that is my mistake.

You could tell Gradle that the task is never up-to-date, even if there is no input file.
See Section 14.9 of the user guide, and TaskOutputs#upToDateWhen

Thank you for your response. I have tried that but with no luck. Maybe the way I’m doing it is wrong (??). What I used is the following (very broad I know but it is just temporary like that to make sure it works).

tasks.all { task ->
  if (task.name.endsWith("Executable")) {
    task.outputs.upToDateWhen { false }
  }
}
if (task.name.endsWith("Executable")) {
  println "upToDate false for ${task}.name"
  task.outputs.upToDateWhen { it -> false }
}

You may try the above snippet
the upToDateWhen closure shall receive a parameter (the task). It should work like you wrote though …
plus maybe you can print a debug message just to check that the condition in your ‘if’ is respected.

The issue here is that the link task for the executable is driven off of the source object files for the executable component itself. If there aren’t any object files for the executable, then it won’t try to link anything. So, one way to approach this is to move the “executable” source files for each executable you are trying to produce into the source of each executable and have every library be pure library code. This would be the approach that most closely aligns with how the model is intended to work.

Having said that, and understanding that approach may not be feasible for various reasons, what you can do is trick the executable linker into considering the output of the library compile tasks (ie the input of the library link tasks) to be its source object files:

    my_executable_1(NativeExecutableSpec) {
        tasks.withType(LinkExecutable) { link ->
           lib1.binaries.withType(StaticLibraryBinarySpec).each { binary ->
               binary.tasks.withType(ObjectFilesToBinary).each { libLink -> 
                   link.source libLink.getSource() 
               }
           }
        }
    }

You would probably want to make this a convenience method to keep this as clean as possible. If you have multiple variants, you would also have to include some logic to filter the library binaries to the appropriate variant for the executable binaries, but that shouldn’t be too difficult.