How to use CUnit to test a native application with multiple components - gradle

I have a gradle project for a native c application in gradle (version 2.10) made from multiple components:
components {
component_1(NativeLibrarySpec)
component_2(NativeLibrarySpec)
...
component_n(NativeLibrarySpec)
main_component(NativeExecutableSpec){
sources {
c.lib library: "component_1", linkage: "static"
c.lib library: "component_2", linkage: "static"
...
c.lib library: "component_n", linkage: "static"
}
}
}
The main idea of having the application in this form was to made easier to test individual components. However, I'm having two big problems:
The main_component is an application, therefore has a main function and this generates this error: multiple definition of 'main' ... gradle_cunit_main.c:(.text+0x0): first defined here. This is something to expect and is mentioned in the documentation, but I'm wondering if there's way to avoid this issue, as for instance, prevent the main component to be included in the tests. This is related to the next question
The CUnit plugin (as of version 2.10 of gradle) complains when I try to define the components to be tested as follows:
testSuites {
component_1Test(CUnitTestSuiteSpec){
testing $.components.component_1
}
}
}
gradle complains:
Cannot create 'testSuites.component_1Test' using creation rule
'component_1Test(org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec)
{ ... } # build.gradle line 68, column 9' as the rule
'CUnitPlugin.Rules#createCUnitTestSuitePerComponent >
create(component_1Test)' is already registered to create this model
element
In summary, I would like to instruct the cunit plugin to test only some components and prevent the main component (with the main function) to be compiled for tests. Again, please notice I'm using gradle 2.10 and cannot upgrade to a more recent version as that would brake our CI tool chain.
Thanks in advance for your comments.

Here is how to structure the project to allow unit testing of the components, but prevent unit testing of the main program: split the components in a sub-project and apply the cunit plugin to the subproject, as shown below:
project(':libs'){
apply plugin: 'c'
apply plugin: 'cunit'
model{
repositories {
libs(PrebuiltLibraries) {
cunit {
headers.srcDir "/usr/include/"
binaries.withType(StaticLibraryBinary) {
staticLibraryFile = file("/usr/lib/x86_64-linux-gnu/libcunit.a")
}
}
}
}
components {
component_1(NativeLibrarySpec)
component_2(NativeLibrarySpec)
...
component_n(NativeLibrarySpec)
}
binaries {
withType(CUnitTestSuiteBinarySpec) {
lib library: "cunit", linkage: "static"
}
}
}
}
apply plugin: 'c'
model {
components{
myprogram(NativeExecutableSpec) {
sources.c {
lib project: ':libs', library: 'component_1', linkage: 'static'
lib project: ':libs', library: 'component_2', linkage: 'static'
lib project: ':libs', library: "component_n", linkage: 'static'
source {
srcDir "src/myprogram/c"
include "**/*.c"
}
}
}
}
}

Related

How to import code from outside into gradle build file?

My build files are large and messy, making them difficult to read. like below:
plugins {
...
id "com.google.protobuf" version "0.8.17"
}
dependencies {
implementation "androidx.datastore:datastore-core:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.18.0"
...
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.14.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
I want to define the above code into an external file, and then introduce it into the build file, how should I do it?
According to the Gradle documentation, as of now it is not possible to move the plugin block to other file than the project’s build script or settings.gradle file.
For the other sections, let's say dependencies or protobuf, then you can move these sections on a separate gradle files and import them by using the following statement:
apply from: "${project.rootDir}/your-gradle-file"
Of course the path of your-gradle-file should be adjusted according to the project's folder structure you decide.
If you want to split the dependencies into multiple gradle file you can do the following:
on your main gradle file:
dependencies {
apply from: "${project.rootDir}/depsGroup1.gradle"
apply from: "${project.rootDir}/depsGroup2.gradle"
}
and within each depsGroup file:
dependencies {
implementation xyz
}

How to add another sourceSet with a mod in Minecraft Forge (Forge Gradle 3)?

I have a library made in Forge dev environment, one is in the main source set, containing library code; another is in the testmod (or any other name) source set, containing testing code that needs to be loaded as a Forge mod.
Simply adding
sourceSets {
testmod {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
java {
srcDir "src/testmod/java"
}
resources {
srcDir "src/testmod/resources"
}
}
}
into my build.gradle, Forge doesn't scan the source set and therefore doesn't load my mod. How can I make Forge load my mod?
Note that my mod is in Forge 1.14.4, and this should apply to all versions with Forge Gradle 3.
Forge loads mod through the mods declared in the minecraft/run section. You will need to add the following lines to make Forge scan the testmod source set for mods.
minecraft {
// ...
runs {
client {
// ...
mods {
mainmodid {
source sourceSets.main
}
testmodid {
source sourceSets.testmod
}
}
}
server {
// ...
mods {
mainmodid {
source sourceSets.main
}
testmodid {
source sourceSets.testmod
}
}
}
}
}
Replace mainmodid and testmodid with your own modid's. The mainmodid should be included in MDK as examplemod.
Although this does make Forge load your classes, it won't be able to find your mods.toml properly though (depends on ForgeGradle version). If you run into loading errors saying test mod isn't found in mods.toml, add the following snippet too
processResources {
from(sourceSets.testmod.resources.srcDirs) {
include "META_INF/mods.toml"
}
}
Also simply adding the source set testmod won't add Forge and Minecraft as a dependency for it automatically. You will also to have add
configurations {
testmodCompile.extendsFrom(compile)
testmodCompileOnly.extendsFrom(compileOnly)
testmodRuntimeOnly.extendsFrom(runtimeOnly)
}
to make Gradle add Forge and Minecraft (the testmod in testmodCompile is the source set name, see Gradle documentation).

How to add a dependency in a multiplatform kotlin / native intellij project?

I have the following build.gradle configuration:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.41'
}
repositories {
mavenCentral()
}
kotlin {
linuxX64("linux") {
binaries {
executable {
entryPoint = 'sample.main'
runTask?.args('')
}
}
}
sourceSets {
linuxMain {
dependencies {
api("org.http4k:http4k-core:3.183.0")
}
}
linuxTest {
}
}
}
And the following source file src/linuxMain/kotlin/sample/SampleLinux.kt :
package sample
fun hello(): String = "Hello, Kotlin/Native!"
fun main() {
println(hello())
}
How to add a external library in order to be able to use autocomplete in imports for the library org.http4k:http4k-core:3.183.0?
As you can see, I tried to add the line api("org.http4k:http4k-core:3.183.0") in linuxMain dependencies, but although intellij show the library in External Libraries section, I cannot work with the packages neither classes of http4k in SampleLinux.kt file: any org.http4k..... import attempt is not recognized and generates compilation error.
After a quick look, I am almost sure that http4k is JVM-only library, at least for now. According to this issue, they are still waiting for Native to grow. If you are interested, it would be nice if one can ask the library maintainers again. As far as K/N has grown a lot by the last year, maybe they change their mind.

Can Gradle produce multiple Kotlin Native binaries (for one OS)?

Can I convince Gradle to produce multiple binaries? I have several Kotlin packages with files that have a proper "fun main(...)" but the default IntelliJ build.gradle file only allows me to specifiy one "compilations.main.entryPoint".
I could put the main functions into Kotlin classes or objects if that would help.
Changing the entryPoint argument to an array did not work :)
If it's not currently possible, is it a general limitation of Gradle or only of the "kotlin-multiplatform" plugin?
plugins {
id 'kotlin-multiplatform' version '1.3.11'
}
repositories {
mavenCentral()
}
kotlin {
targets {
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
// For Linux, preset should be changed to e.g. presets.linuxX64
// For MacOS, preset should be changed to e.g. presets.macosX64
fromPreset(presets.mingwX64, 'mingw')
configure([mingw]) {
// Comment to generate Kotlin/Native library (KLIB) instead of executable file:
compilations.main.outputKinds('executable')
// Change to specify fully qualified name of your application's entry point:
compilations.main.entryPoint = 'hello.main'
}
}
sourceSets {
// Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
// in gradle.properties file and re-import your project in IDE.
mingwMain {
}
mingwTest {
}
}
}
task runProgram {
def buildType = 'debug' // 'release' - Change to 'debug' to run application with debug symbols.
dependsOn "link${buildType.capitalize()}ExecutableMingw"
doLast {
def programFile = kotlin.targets.mingw.compilations.main.getBinary('EXECUTABLE', buildType)
exec {
executable programFile
args ''
}
}
}
In https://github.com/JetBrains/kotlin-native/issues/2505 I've just got the answer that this will be possible with Kotlin Native 1.3.20!

How to include headers for C compilation in Gradle?

I am a newbie to Gradle. I am trying to compile a set of source files which contain headers which are distributed across the project directory. My source directory structure does not comply with the Gradle convention. How do I add the header locations needed for compilation in my build.gradle? Attached here is my build.gradle file.
// build.gradle
apply plugin: 'c'
model {
components {
my_project (NativeExecutableSpec){
sources {
c {
source {
srcDir "my_proj_src/a/a1.1"
include "**/*.c"
}
exportedHeaders {
srcDir "my_proj_src/a/a1.1", "fsw/b/b1.2"
}
}
}
}
}
}
This does not work. And additionally, is there a possibility to do partial linking using Gradle?
EDIT: Additionally, I would like to also know how to make Gradle search recursively for headers within the source hierarchy.
exportedHeaders` are for exporting headers from the component itself, not for adding headers. So this would not work.
You would need to create a library and add it as the api linkage so that those headers will be added to headers your component is compiled against:
model {
repositories {
libs(PrebuiltLibraries) {
ffmpegHeaders {
headers.srcDirs "$ffmpegDir/include"
}
}
}
components {
libUsingHeaders(NativeLibrarySpec) {
sources {
c {
lib library: 'ffmpegHeaders', linkage: 'api'
}
}
}
}
}

Resources