.. | ||
src | ||
build.gradle | ||
gradle.properties | ||
LICENSE.txt | ||
NOTICE.txt | ||
README.md |
C - Gradle plugin
An experimental plugin for customized C development with Gradle.
It showcases how to use tasks that are provided by Gradle core for C development,
such as org.gradle.language.c.tasks.CCompile
, directly as part of a custom build system.
This can make sense in a highly customized Gradle build system where specific (and multiple) C compilation/assembling/linking
steps are part of a larger build pipeline with other custom Gradle tasks.
The plugin provides an extension called c
to:
- Set the
targetPlatform
property of a task to a generic platform viatargetPlatform.set(c.platform())
- Set the
toolChain
property of a task to a locally installed tool viatoolChain.set(c.localTool("version", "/path/to/tool", ".o"))
- Set the
toolChain
property of a task to a tool located in a ZIP file in a Maven repository viatoolChain.set(c.repositoryTool("group", "name", "version", "path/in/zip", ".o"))
Example
plugins {
id 'org.xbib.gradle.plugin.c"
}
tasks.register<CCompile>("compileC") {
toolChain.set(c.localTool("14.0.0", "/usr/bin/clang", ".o"))
targetPlatform.set(c.platform())
source(layout.projectDirectory.files("src/c").asFileTree)
includes(layout.projectDirectory.files("src/headers"))
compilerArgs.add("-S")
objectFileDir.set(layout.buildDirectory.dir("out/o"))
}
val compileC2 = tasks.register<CCompile>("compileC2") {
toolChain.set(c.repositoryTool("com.example", "vendor-x", "1.2", "bin/xcc", ".src"))
targetPlatform.set(c.platform())
source(layout.projectDirectory.files("src/c-special").asFileTree)
includes(layout.projectDirectory.files("src/headers"))
objectFileDir.set(layout.buildDirectory.dir("out/src"))
}
tasks.register<Assemble>("assembleC2") {
toolChain.set(c.repositoryTool("com.example", "vendor-x", "1.2", "bin/xas", ".o"))
targetPlatform.set(c.platform())
source(compileC2.map { it.objectFileDir.asFileTree })
includes(layout.projectDirectory.files("src/headers"))
assemblerArgs = listOf("-D")
objectFileDir = layout.buildDirectory.dir("out/o2").get().asFile
}
How does this work?
In order to fully configure one of the native tasks of Gradle (such as org.gradle.language.c.tasks.CCompile
),
you require a NativeToolchain (instance of org.gradle.nativeplatform.toolchain.NativeToolChain
)
and a TargetPlatform (instance of org.gradle.nativeplatform.platform.NativePlatform
).
Gradle's own native languages plugins
pre-configure these task properties through the so-called native toolchain registry.
This allows, for example, to compile the same code for multiple target platforms with different tools in the same build.
This is great, if this is what you need. However, in a scenario where we do not care about this feature and would rather register
tasks directly, we miss flexibility as the native toolchain registry in Gradle is not very flexible right now and makes it difficult or impossible to register
your own tools (and it has not been improved for several years).
The CExtension.java class now allows you to
set the targetPlatform
and toolChain
properties of any task without the native toolchain registry.
It does so by utilising internal Gradle APIs to:
- Construct a generic
NativePlatform
object. This is the same everywhere and will be ignored during tool selection. In our scenario, we do not care about different platforms. - Construct a
NativeToolChain
instance based on a concrete executable. Either by providing a concrete path to an executable (localTool(...)
) or coordinates pointing at a ZIP file in a Maven repository (repositoryTool(...)
). In the latter case, Gradle's dependency management is used to find, download, extract and cache the tool.
Behind the custom NativeToolChain
implementation is a custom PlatformToolProvider
implementation and extensions of
Gradle's NativeCompiler
implementations. This allows for more customization and extension of Gradle's functionality
in this area. This uses a lot of APIs from internal
packages. Which, however, have not been touched for years.
This showcase could help to determine which APIs in this area should probably be public.