Getting started

The primary goal of this backend is to cover native platforms which are not supported by GraalVM Native Image or similar tools. C compiler exists everywhere, and TeaVM's C backend tries to emit standard C only, without any hacks and compiler-specific extensions.

Unfortunately, TeaVM relies on handwritten runtime, which should be maintained for all possible platforms, which are not available to the maintainers. If you want to use TeaVM with a platform which it does not support, feel free to open a PR. Usually, it's not a rocket science, and you don't need to learn all internal compiler infrastructure to make the runtime compatible with your platform. The runtime code can be found at core/src/main/resources/org/teavm/backend/c.

Step 1. Generate C code with Gradle

Here's a minimal build.gradle:

plugins {
    id "java"
    id "war"
    id "org.teavm" version "0.13.0"
}
repositories {
    mavenCentral()
}

// This is optional, but for real-world applications you need to interact with native libs.
// This dependency provides useful wrappers.
dependencies {
    implementation teavm.libs.interop
}

teavm {
    all {
        mainClass = "example.MainClass"
    }
    c {
        minHeapSize = 2   // optional
        maxHeapSize = 128 // optional
    }
}

where MainClass could do something simple like writing "Hello, world" string in the console, for example:

package example;

public class MainClass {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

Now run

./gradlew generateC

and you can find generated C code in build/generated/teavm/c.

Step 2. Generate native code

Just for educational/evaluation purpose you can start with compilation of all.c, though it's not recommended for daily use. For example, if you are using gcc, you can go to build/generated/teavm/c and run:

gcc all.c

and a.out appears in the same dir.

Then, you can improve your development experience by using particular C build tool, like CMake. You need to parse all.txt, which contains path to a source file per line. For example, in CMake:

cmake_minimum_required(VERSION 3.31)
project(teavm_example C)

set(CMAKE_C_STANDARD 11)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
    ***/build/dist/
)

if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()

set(TEAVM_C_DIR ***/build/generated/teavm/c)

file(STRINGS
    ***/all.txt
    TEAVM_C_SOURCES_REL
)

set(TEAVM_C_SOURCES "")
foreach(src ***)
    list(APPEND TEAVM_C_SOURCES ***/***)
endforeach()

set_property(DIRECTORY APPEND PROPERTY
    CMAKE_CONFIGURE_DEPENDS
    ***/all.txt
)

set_source_files_properties(
    ***
    PROPERTIES
        GENERATED TRUE
)

add_executable(teavm_example ***)
target_compile_definitions(teavm_example PRIVATE _XOPEN_SOURCE=700)
target_compile_definitions(teavm_example PRIVATE __USE_XOPEN)
target_compile_definitions(teavm_example PRIVATE _GNU_SOURCE)


target_compile_features(teavm_example PRIVATE c_std_11)

target_compile_options(teavm_example PRIVATE
    $<$<CONFIG:Debug>:-g>
    $<$<CONFIG:Release>:-O3>
)

set_target_properties(teavm_example PROPERTIES
    INTERPROCEDURAL_OPTIMIZATION_RELEASE ON
)

Then run

cmake .
make

and you'll get executable in build/dist

You can also configure Gradle to build executable. Just add

def cmakeTask = tasks.register("cmake", Exec) {
    dependsOn tasks.generateC
    inputs.files tasks.generateC.outputDir.map { new File(it, "all.txt") }
    inputs.files layout.projectDirectory.file("CMakeLists.txt")
    outputs.file layout.projectDirectory.file("Makefile")
    workingDir = layout.projectDirectory
    commandLine = [ "cmake", "." ]
}

def buildExecutableTask = tasks.register("buildExecutable", Exec) {
    group "build"
    dependsOn cmakeTask
    dependsOn tasks.generateC
    inputs.files fileTree(tasks.generateC.outputDir)
    outputs.file layout.buildDirectory.dir("dist/teavm_example")
    commandLine = [ "make" ]
}

tasks.build {
    dependsOn buildExecutableTask
}

Now you can run

./gradlew buildExecutable

or even

./gradlew build

to get executable.

You can take a look at example that shows how to write a GTK+ application with Java and TeaVM.