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.f90c
#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_cstarlark
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=allPASS: c_add_doubles(2.0, 3.0) = 5.02.B. C Calls Fortran
interop/
├── BUILD.bazel
├── c_calls_fortran.c
└── fortran_math.f90fortran
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_modc
#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=allPASS: fortran_square(5.0) = 25.02.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.f90fortran
! 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_modc
// 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=allTest 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.02.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.cc
#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=allOK
3.00 0.33 0.67
6.00 2.00 0.50
10.00 3.67 -0.50