Skip to content

Interop

Source: examples/interop/

fortran_library provides CcInfo, enabling bidirectional linking between Fortran and C/C++ targets.

2.A. Fortran Calls C

interop/
├── BUILD.bazel
├── c_math.c
├── c_math.h
└── fortran_calls_c.f90
c
#ifndef C_MATH_H
#define C_MATH_H

/**
 * Simple C math function for testing C/Fortran interop.
 * Adds two double precision numbers.
 *
 * @param a First number
 * @param b Second number
 * @return Sum of a and b
 */
double c_add_doubles(double a, double b);

#endif /* C_MATH_H */
c
#include "c_math.h"

double c_add_doubles(double a, double b) {
    return a + b;
}
fortran
program fortran_calls_c
    use iso_c_binding, only: c_double
    implicit none

    ! Interface to C function
    interface
        function c_add_doubles(a, b) bind(c, name="c_add_doubles")
            import :: c_double
            real(c_double), value :: a, b
            real(c_double) :: c_add_doubles
        end function c_add_doubles
    end interface

    ! Test variables
    real(c_double) :: result

    ! simple addition
    result = c_add_doubles(2.0d0, 3.0d0)
    if (abs(result - 5.0d0) > 1.0d-10) then
        print *, "FAIL: c_add_doubles(2.0, 3.0) = ", result, " expected 5.0"
    else
        print *, "PASS: c_add_doubles(2.0, 3.0) = 5.0"
    end if

end program fortran_calls_c
starlark
cc_library(
    name = "c_math",
    srcs = ["c_math.c"],
    hdrs = ["c_math.h"],
)

fortran_test(
    name = "fortran_calls_c",
    srcs = ["fortran_calls_c.f90"],
    deps = [":c_math"],
)
bash
bazel test //interop:fortran_calls_c --test_output=all
PASS: c_add_doubles(2.0, 3.0) = 5.0

2.B. C Calls Fortran

interop/
├── BUILD.bazel
├── c_calls_fortran.c
└── fortran_math.f90
fortran
module fortran_math_mod
    use iso_c_binding, only: c_double
    implicit none

contains

    ! Function callable from C
    ! The bind(c, name="...") attribute ensures C-compatible name mangling
    function fortran_square(x) bind(c, name="fortran_square")
        real(c_double), value :: x  ! value attribute for pass-by-value (C convention)
        real(c_double) :: fortran_square

        fortran_square = x * x
    end function fortran_square

end module fortran_math_mod
c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// use bind(c) to ensure C-compatible name mangling.
extern double fortran_square(double x);

int main(void) {
    int errors = 0;

    double result1 = fortran_square(5.0);
    if (fabs(result1 - 25.0) > 1e-10) {
        printf("FAIL: fortran_square(5.0) = %f, expected 25.0\n", result1);
        errors++;
    } else {
        printf("PASS: fortran_square(5.0) = 25.0\n");
    }
}
starlark
fortran_library(
    name = "fortran_math",
    srcs = ["fortran_math.f90"],
)

cc_test(
    name = "c_calls_fortran",
    srcs = ["c_calls_fortran.c"],
    deps = [":fortran_math"],
)
bash
bazel test //interop:c_calls_fortran --test_output=all
PASS: fortran_square(5.0) = 25.0

2.C. Fortran Runtime Features

C calling Fortran subroutines that use runtime features -- I/O, intrinsics, dynamic arrays, formatted output, strings.

interop/
├── BUILD.bazel
├── c_calls_fortran_runtime.c
└── fortran_runtime_features.f90
fortran
! routines that (probably) depend on flang_rt
module fortran_runtime_features_mod
    use iso_c_binding, only: c_double, c_int
    implicit none

contains

    ! uses Fortran I/O
    function test_io(x) bind(c, name="test_io")
        real(c_double), value :: x
        real(c_double) :: test_io
        
        print *, "Fortran I/O: input value =", x
        
        test_io = x * 2.0
    end function test_io

    ! uses intrinsic math functions
    function test_intrinsics(x) bind(c, name="test_intrinsics")
        real(c_double), value :: x
        real(c_double) :: test_intrinsics
        real(c_double) :: temp
        
        temp = sqrt(x)
        temp = exp(temp)
        temp = log(temp)
        temp = sin(temp)
        temp = cos(temp)
        
        print *, "Fortran intrinsics: sqrt->exp->log->sin->cos of", x, "=", temp
        
        test_intrinsics = temp
    end function test_intrinsics

    ! uses array operations
    function test_array_ops(n) bind(c, name="test_array_ops")
        integer(c_int), value :: n
        real(c_double) :: test_array_ops
        real(c_double), allocatable :: arr(:)
        integer :: i
        
        ! malloc - requires runtime support
        allocate(arr(n))
        
        ! array init
        do i = 1, n
            arr(i) = real(i, c_double) ** 2
        end do
        
        ! array ops
        test_array_ops = sum(arr) / real(n, c_double)
        
        print *, "Fortran array ops: average of squares from 1 to", n, "=", test_array_ops
        
        ! duh
        deallocate(arr)
    end function test_array_ops

    ! uses formatted I/O
    function test_formatted_io(x, y) bind(c, name="test_formatted_io")
        real(c_double), value :: x, y
        real(c_double) :: test_formatted_io
        character(len=100) :: buffer
        
        write(buffer, '(A,F10.3,A,F10.3)') "Values: x=", x, " y=", y
        print *, trim(buffer)
        
        test_formatted_io = x + y
    end function test_formatted_io

    ! uses character operations
    function test_string_ops() bind(c, name="test_string_ops")
        integer(c_int) :: test_string_ops
        character(len=50) :: str1, str2, str3
        
        str1 = "Hello"
        str2 = "World"
        str3 = trim(str1) // " " // trim(str2)
        
        print *, "Fortran string ops: ", trim(str3)
        
        test_string_ops = len_trim(str3)
    end function test_string_ops

    ! uses error handling
    function test_runtime_checks(x) bind(c, name="test_runtime_checks")
        real(c_double), value :: x
        real(c_double) :: test_runtime_checks
        real(c_double) :: result
        
        if (x > 0.0) then
            result = sqrt(x)
            print *, "Fortran runtime checks: sqrt of positive", x, "=", result
        else
            result = 0.0
            print *, "Fortran runtime checks: skipped sqrt of non-positive", x
        end if
        
        test_runtime_checks = result
    end function test_runtime_checks

end module fortran_runtime_features_mod
c
// tests that flang_rt and clang_rt libraries are properly linked

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// Fortran function declarations
extern double test_io(double x);
extern double test_intrinsics(double x);
extern double test_array_ops(int n);
extern double test_formatted_io(double x, double y);
extern int test_string_ops(void);
extern double test_runtime_checks(double x);

int main(void) {
    int errors = 0;
    double result;
    int int_result;

    printf("=== Testing Fortran Runtime Library Dependencies ===\n\n");

    // Test 1: Fortran I/O
    printf("Test 1: Fortran I/O\n");
    result = test_io(42.0);
    if (fabs(result - 84.0) > 1e-10) {
        printf("FAIL: test_io(42.0) = %f, expected 84.0\n", result);
        errors++;
    } else {
        printf("PASS: test_io(42.0) = 84.0\n");
    }
    printf("\n");

    // Test 2: Fortran intrinsic math functions
    printf("Test 2: Fortran intrinsic functions (sqrt, exp, log, sin, cos)\n");
    result = test_intrinsics(4.0);
    // Just check that we got a finite number (the actual value doesn't matter much)
    if (isfinite(result)) {
        printf("PASS: test_intrinsics(4.0) returned finite value %f\n", result);
    } else {
        printf("FAIL: test_intrinsics(4.0) returned non-finite value\n");
        errors++;
    }
    printf("\n");

    // Test 3: Array operations with dynamic allocation
    printf("Test 3: Dynamic array allocation and operations\n");
    result = test_array_ops(10);
    // Average of squares from 1 to 10: (1+4+9+16+25+36+49+64+81+100)/10 = 38.5
    double expected_avg = 38.5;
    if (fabs(result - expected_avg) > 1e-10) {
        printf("FAIL: test_array_ops(10) = %f, expected %f\n", result, expected_avg);
        errors++;
    } else {
        printf("PASS: test_array_ops(10) = %f\n", result);
    }
    printf("\n");

    // Test 4: Formatted I/O
    printf("Test 4: Formatted I/O\n");
    result = test_formatted_io(3.14159, 2.71828);
    double expected_sum = 3.14159 + 2.71828;
    if (fabs(result - expected_sum) > 1e-5) {
        printf("FAIL: test_formatted_io = %f, expected %f\n", result, expected_sum);
        errors++;
    } else {
        printf("PASS: test_formatted_io = %f\n", result);
    }
    printf("\n");

    // Test 5: String operations
    printf("Test 5: String operations\n");
    int_result = test_string_ops();
    // "Hello World" has 11 characters
    if (int_result != 11) {
        printf("FAIL: test_string_ops = %d, expected 11\n", int_result);
        errors++;
    } else {
        printf("PASS: test_string_ops = %d\n", int_result);
    }
    printf("\n");

    // Test 6: Runtime checks
    printf("Test 6: Runtime checks (positive value)\n");
    result = test_runtime_checks(16.0);
    if (fabs(result - 4.0) > 1e-10) {
        printf("FAIL: test_runtime_checks(16.0) = %f, expected 4.0\n", result);
        errors++;
    } else {
        printf("PASS: test_runtime_checks(16.0) = 4.0\n");
    }
    printf("\n");

    printf("Test 7: Runtime checks (negative value)\n");
    result = test_runtime_checks(-5.0);
    if (fabs(result - 0.0) > 1e-10) {
        printf("FAIL: test_runtime_checks(-5.0) = %f, expected 0.0\n", result);
        errors++;
    } else {
        printf("PASS: test_runtime_checks(-5.0) = 0.0\n");
    }
    printf("\n");

    // Summary
    printf("=== Test Summary ===\n");
    if (errors == 0) {
        printf("All tests passed! Runtime libraries properly linked.\n");
        return 0;
    } else {
        printf("%d test(s) failed.\n", errors);
        return 1;
    }
}
starlark
fortran_library(
    name = "fortran_runtime_features",
    srcs = ["fortran_runtime_features.f90"],
)

cc_test(
    name = "c_calls_fortran_runtime",
    srcs = ["c_calls_fortran_runtime.c"],
    deps = [":fortran_runtime_features"],
)
bash
bazel test //interop:c_calls_fortran_runtime --test_output=all
Test 1: Fortran I/O
PASS: test_io(42.0) = 84.0

Test 2: Fortran intrinsic functions (sqrt, exp, log, sin, cos)
PASS: test_intrinsics(4.0) returned finite value 0.614300

Test 3: Dynamic array allocation and operations
PASS: test_array_ops(10) = 38.500000

Test 4: Formatted I/O
PASS: test_formatted_io = 5.859870

Test 5: String operations
PASS: test_string_ops = 11

Test 6: Runtime checks (positive value)
PASS: test_runtime_checks(16.0) = 4.0

Test 7: Runtime checks (negative value)
PASS: test_runtime_checks(-5.0) = 0.0

2.D. LAPACKE from C

Pure C client using LAPACKE to call LAPACK routines (LU factorization). Requires @blas, @lapack, and @lapacke repos -- see examples/MODULE.bazel.

interop/
├── BUILD.bazel
└── full.c
c
#include <lapacke.h>
#include <stdio.h>

int main() {
    float A[9] = {1, 2, 3, 4, 5, 6, 7, 8, 10};
    int ipiv[3];
    int info = LAPACKE_sgetrf(LAPACK_COL_MAJOR, 3, 3, A, 3, ipiv);

    printf(info ? "FAIL\n" : "OK\n");
    for (int i = 0; i < 9; i++)
        printf("%8.2f%c", A[i], (i+1)%3 ? ' ' : '\n');
    return info;
}
starlark
cc_test(
    name = "full",
    srcs = ["full.c"],
    deps = [
        "@blas//:single",
        "@lapack//:single",
        "@lapacke//:single",
    ],
)
bash
bazel test //interop:full --test_output=all
OK
    3.00     0.33     0.67
    6.00     2.00     0.50
   10.00     3.67    -0.50