gradle / gradle-native

The home of Gradle's support for natively compiled languages
https://blog.gradle.org/introducing-the-new-cpp-plugins
Apache License 2.0
91 stars 8 forks source link

CppComponent should allow C source file extensions/configuration #1048

Open lhunath opened 4 years ago

lhunath commented 4 years ago

Expected Behavior

When configuring the cpp-library or cpp-application plugin, it should be possible to configure the DefaultCppComponent to use C source files.

Current Behavior

Currently the source file extensions that are used to filter the source files in the source directory are hard-coded to cpp, c++, cc in DefaultCppComponent. This should be configurable, at the very least, or a better solution must be found to prevent DefaultCppComponent from excluding valid source files from the source file collection.

lhunath commented 4 years ago

Prior to:

commit ef46f762a851950faa89df1b478e68bdb4ff2ac1
Author: Lóránt Pintér <lorant.pinter@gmail.com>
Date:   Thu Jun 28 04:58:41 2018

    Use immutable file collections whenever possible (#5797)

it was possible to use:

    components.withType( CppComponent ) {
        cppSource.from fileTree( 'src' )
    }

To work around this issue.

michaelzangl commented 4 years ago

Wokaround for now (kotlin)

tasks.withType(CppCompile::class.java).configureEach {
    source.from(files("src/main/c/").asFileTree.matching { include("**/*.c")})
}
lacasseio commented 4 years ago

Be mindful this workaround is all about compiling C sources with a C++ compiler. Generally the outcome will be the same but the code is subject to the C++ standard as opposed to the C standard.

michaelzangl commented 4 years ago

There are quite some differences. I am just using it to compile some old JNI code. I gave up compiling real C code with gradle.

At first, I intended to use this to compile AVR C files with gradle (For Atmel AVR, the same controllers Arduino uses). My first Idea was quite simple: Set the compiler executable to 'avr-gcc' instead of 'gcc', set the correct src directory, add the -mmcu compiler flag, set output type to .elf and then write custom tasks to pipe the generated files in AVRDude to flash the microcontroller.

But then I just kept running into problems and eventually gave up.

  1. The compiler executable name is hardcoded in private methods. It cannot be set without hacks / casts
  2. The plugin always uses C++ compilation, there is just no Compile C option. It could be done by setting the input files to a different pattern, adjusting the executable and fixing the flags
  3. Output seems to be meant to be a library (.so) or an executable, not an .elf
  4. No IDE support

At first, I tried working around this by extending the current Gcc with custom classes, but there are just too many dependencies to gradle internals which make it very hard and hacky to hook into. I would have had to re-write most of the tasks myself. In the end, I ended up using cmake

dickensas commented 2 years ago

I did come across this problem, I wanted to compile fortran and produce a .dll and call that dll from Kotlin Native,

so practically I wanted to override the gradle cpp-library which already has the capacity to create dll files inbuilt, which some trial and error and with some references from different forums, this is what I did.

toolChains.configureEach when it is GccCompatibleToolChain I added a gradle Action and set the exe name as

toolChain.getCppCompiler().setExecutable("gfortran")

inside tasks.withType(CppCompile::class.java).configureEach I have added .f as extension as below

source.from(fileTree("${project.rootDir}/${project.name}/src/main/fortran").matching {
   include("*.f")
})

I overridden the compiler arguments with toolChain.getCppCompiler().withArguments(CompiterArgumentsAction()) as below

class CompiterArgumentsAction : Action<MutableList<String>> {
    override fun execute(args: MutableList<String>) {
        args.clear()
        args.add("-v")
        args.add("-c")
        args.add("-fPIC")
        args.add("-shared")
        args.add("-o")
        args.add("${project.name}.dll")
        args.add("-Wl,--out-implib,${project.name}.dll.a")
        args.add("-Wl,--output-def,${project.name}.def")
        args.add("-Wl,--export-all-symbols")
        args.add("-Wl,--enable-auto-import")
    }
}

As you can see I use ${project.name} which refers to the project folder name

This is a one time work, which is a sub project in gradle kts script format, when the main project executes, automatically this project creates the library based on the project folder name. For example this project is named as libfort1, the script automatically creates

libfort1.def
libfort1.dll
libfort1.dll.a

if you change the name of the folder, then automatically the file names will change

This is the code Combine Kotlin and Fortran using kotlin gradle kts

so, practically we can call any exe and pass any arguments we want