commit d1a0fdc9dd14ae43e4d9256486f0e9a0425c9264 Author: sys64738 Date: Thu Jan 9 23:57:00 2025 +0100 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc0fd91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.zig-cache/ +build/ +cmake-build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2da98fb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,65 @@ +set(PROJECT "rpsdk-mix-c-rs-zig") + +option(PICO_NO_FLASH "Disable writing the compiled program to flash, and only load it to RAM. Useful for testing, but not much else (ON by default)." OFF) +option(PICO_COPY_TO_RAM "Run all code in RAM, while the program is also stored on flash. On bootup, everything will be copied to RAM (OFF by default)." OFF) + +cmake_minimum_required(VERSION 3.11) + +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pico_sdk_import.cmake) + +project(${PROJECT}) + +pico_sdk_init() + +add_executable(${PROJECT}) + +pico_enable_stdio_uart(${PROJECT} 0) +pico_enable_stdio_usb(${PROJECT} 1) + +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/clib.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c +) + +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/inc/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/ +) + +target_link_libraries(${PROJECT} + pico_stdlib pico_unique_id hardware_pio hardware_dma hardware_uart + hardware_pwm cmsis_core pico_multicore + pico_stdlib +) + +pico_add_extra_outputs(${PROJECT}) + +### hacks start here + +set(RUST_TARGET thumbv6m-none-eabi) +set(RUST_CPU cortex-m0plus) +list(APPEND BINDGEN_CFLAGS "--use-core" "--rust-edition=2021") +list(APPEND RUST_CFLAGS "--edition=2021") +include(cmake/rust-obj.cmake) +add_rust_object(${PROJECT} + # rust top level module + ${CMAKE_CURRENT_SOURCE_DIR}/src/rustcode.rs + # optional: path to header file to use for c2rs bindgen stuff (currently limited to one, sorry) + ${CMAKE_CURRENT_SOURCE_DIR}/inc/clib.h +) + +set(ZIG_TARGET thumb-freestanding-gnueabi) +set(ZIG_CPU cortex_m0plus) +list(APPEND ZIG_CFLAGS "-ffunction-sections" "-fdata-sections") +include(cmake/zig-obj.cmake) +add_zig_object(${PROJECT} + # zig top level module + ${CMAKE_CURRENT_SOURCE_DIR}/src/zigcode.zig +) + +### hacks end here + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type -Werror=aggressive-loop-optimizations") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--cref") + diff --git a/README.md b/README.md new file mode 100644 index 0000000..814869c --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# rpsdk-mix-c-rs-zig + +Mix C, Rust and Zig in a Raspberry Pico SDK (CMake) project. + +Yeah. + +## Usage + +Don't. + +Other than that, + +### Add Rust code to your project + +```cmake +set(RUST_TARGET thumbv6m-none-eabi) +set(RUST_CPU cortex-m0plus) +list(APPEND BINDGEN_CFLAGS "--use-core" "--rust-edition=2021") +list(APPEND RUST_CFLAGS "--edition=2021") +include(cmake/rust-obj.cmake) +add_rust_object(${PROJECT} + # rust top level module + ${CMAKE_CURRENT_SOURCE_DIR}/src/rustcode.rs + # optional: path to header file to use for c2rs bindgen stuff (currently limited to one, sorry) + ${CMAKE_CURRENT_SOURCE_DIR}/inc/clib.h +) +``` + +Bindgen and CBindgen are used automatically to generate C headers from the Rust +code, and vice versa. + +### Add Zig code to your project + +```cmake +set(ZIG_TARGET thumb-freestanding-gnueabi) +set(ZIG_CPU cortex_m0plus) +list(APPEND ZIG_CFLAGS "-ffunction-sections" "-fdata-sections") +include(cmake/zig-obj.cmake) +add_zig_object(${PROJECT} + # zig top level module + ${CMAKE_CURRENT_SOURCE_DIR}/src/zigcode.zig +) +``` + +## Known issues and limitations + +* Sometimes, compilation fails with `rustc` failing to copy an object file to + the destination path. Reason for this is unknown, just retrying the build + seems to work. +* References between Zig and Rust code aren't really possible automatically + (can't generate Rust via Bindgen from Zig-generated headers, or include + Bindgen-generated C headers in Zig code). This is because Zig can't generate + these headers without also compiling the code. +* Only one C header can be Bindgen'ed per Rust "root" module/crate (one crate + gets compiled into an object file and included in the main link). + +## License + +``` +be gay, do crimes, death to america +``` diff --git a/cmake/pico_sdk_import.cmake b/cmake/pico_sdk_import.cmake new file mode 100644 index 0000000..28efe9e --- /dev/null +++ b/cmake/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/cmake/rust-obj.cmake b/cmake/rust-obj.cmake new file mode 100644 index 0000000..de158e1 --- /dev/null +++ b/cmake/rust-obj.cmake @@ -0,0 +1,148 @@ + +# ============================================================================= +# first, get some info about the compiler (and bindgen) + +# get rustc binary +if(NOT DEFINED RUSTC_BIN OR "${RUSTC_BIN}" STREQUAL "") + find_program(RUSTC_BIN rustc REQUIRED) +endif() +set(RUSTC_BIN ${RUSTC_BIN} CACHE FILEPATH "rustc command") +message(NOTICE "-- RUSTC_BIN is ${RUSTC_BIN}") + +# get cbindgen binary +if(NOT DEFINED CBINDGEN_BIN OR "${CBINDGEN_BIN}" STREQUAL "") + find_program(CBINDGEN_BIN cbindgen REQUIRED) +endif() +set(CBINDGEN_BIN ${CBINDGEN_BIN} CACHE FILEPATH "cbindgen command") +message(NOTICE "-- CBINDGEN_BIN is ${CBINDGEN_BIN}") + +# get bindgen binary +if(NOT DEFINED BINDGEN_BIN OR "${BINDGEN_BIN}" STREQUAL "") + find_program(BINDGEN_BIN bindgen REQUIRED) +endif() +set(BINDGEN_BIN ${BINDGEN_BIN} CACHE FILEPATH "bindgen command") +message(NOTICE "-- BINDGEN_BIN is ${BINDGEN_BIN}") + +# ============================================================================= +# set compile options + +if(DEFINED RUST_TARGET) + list(APPEND RUST_CFLAGS "--target=${RUST_TARGET}") +endif() +if(DEFINED RUST_CPU) + list(APPEND RUST_CFLAGS "-C" "target-cpu=${RUST_CPU}") +endif() +if(CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND RUST_CFLAGS "-C" "opt-level=0" "-g") +elseif(CMAKE_BUILD_TYPE STREQUAL Release) + list(APPEND RUST_CFLAGS "-C" "opt-level=3") +elseif(CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo) + list(APPEND RUST_CFLAGS "-C" "opt-level=2" "-g") +elseif(CMAKE_BUILD_TYPE STREQUAL MinSizeRel) + list(APPEND RUST_CFLAGS "-C" "opt-level=z") +endif() + +list(APPEND BINDGEN_CFLAGS "--explicit-padding" "--formatter=none") + +list(APPEND CBINDGEN_CFLAGS "--cpp-compat" "-l" "C") +if(CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND CBINDGEN_CFLAGS "--profile" "Debug") +elseif(CMAKE_BUILD_TYPE STREQUAL Release OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo OR CMAKE_BUILD_TYPE STREQUAL MinSizeRel) + list(APPEND CBINDGEN_CFLAGS "--profile" "Release") +endif() + +# ============================================================================= +# now define the macro we'll be using + +function(add_rust_object TOP_PROJECT RUST_TOP_SOURCE_FILE) + if(ARGC GREATER 3) + set(TARGET_NAME ${ARGV3}) + else() + cmake_path(GET RUST_TOP_SOURCE_FILE STEM TARGET_NAME) + endif() + + # directory the header file will be in + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/) + + ### C-to-rust bindings (generate .rs from C headers, bindgen) + set(HEADER_PATH "") + set(MAIN_RUST_DEP "") + + if(ARGC GREATER 2) + set(HEADER_PATH ${ARGV2}) + cmake_path(GET HEADER_PATH STEM HEADER_NAME) + + if(NOT(HEADER_PATH STREQUAL "")) + # generate rs from c headers + if("--use-core" IN_LIST BINDGEN_CFLAGS) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs + COMMAND ${BINDGEN_BIN} ${BINDGEN_CFLAGS} --depfile ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.d -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.tmp ${HEADER_PATH} + COMMAND python -c "import sys;f=open\(sys.argv[1]+'.tmp','r'\).read\(\);open\(sys.argv[1],'w'\).write\('#![no_std]'+chr(10)+f\)" ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.tmp + DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.d + MAIN_DEPENDENCY ${RUST_TOP_SOURCE_FILE} + VERBATIM + ) + else() + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs + COMMAND ${BINDGEN_BIN} ${BINDGEN_CFLAGS} --depfile ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.d -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs ${HEADER_PATH} + DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.d + MAIN_DEPENDENCY ${RUST_TOP_SOURCE_FILE} + VERBATIM + ) + endif() + add_custom_target(c2rs_${TARGET_NAME}_src_tgt DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs) + + # build crate from it so we can actually import it + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib + COMMAND ${RUSTC_BIN} ${RUST_CFLAGS} --crate-type=rlib --emit=link,dep-info=${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib.d -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs + DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib.d + MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs + VERBATIM + ) + add_custom_target(c2rs_${TARGET_NAME}_tgt DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib) + add_library(c2rs_${TARGET_NAME} OBJECT $) + list(APPEND RUST_CFLAGS "--extern" "${HEADER_NAME}_bindgen=${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib") + + set(MAIN_RUST_DEP "c2rs_${TARGET_NAME}_tgt") + endif() + endif() + + ### rust-to-C bindings (generate C headers from .rs, cbindgen) + # cbindgen call + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h + COMMAND ${CBINDGEN_BIN} ${CBINDGEN_CFLAGS} -d --depfile ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h.d -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h ${RUST_TOP_SOURCE_FILE} + DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h.d + MAIN_DEPENDENCY ${RUST_TOP_SOURCE_FILE} + VERBATIM + ) + # make a virtual library for the header files + add_library(rs2c_${TARGET_NAME} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h) + + # add dependency to ensure build order and stuff + target_link_libraries(${TOP_PROJECT} rs2c_${TARGET_NAME}) + # and add include directories + target_include_directories(${TOP_PROJECT} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/) + + ### rust compilation + # the compile command + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj + COMMAND ${RUSTC_BIN} ${RUST_CFLAGS} --emit=obj,dep-info=${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj.d --crate-type=staticlib -o ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj ${RUST_TOP_SOURCE_FILE} + DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj.d + # ensures the bindgen happens before the rustc compilation + #DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${TARGET_NAME}_bindgen.rlib + DEPENDS ${MAIN_RUST_DEP} + MAIN_DEPENDENCY ${RUST_TOP_SOURCE_FILE} + VERBATIM + ) + # add a virtual library that can be reused and referred to (not possible with a bare add_custom_command()) + add_library(${TARGET_NAME} OBJECT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj) + # add generated object file from the library to the code to compile + target_sources(${TOP_PROJECT} PUBLIC $) +endfunction() + diff --git a/cmake/zig-obj.cmake b/cmake/zig-obj.cmake new file mode 100644 index 0000000..b0c22b1 --- /dev/null +++ b/cmake/zig-obj.cmake @@ -0,0 +1,79 @@ + +# ============================================================================= +# first, get some info about the compiler + +# get zig binary +if(NOT DEFINED ZIG_BIN OR ZIG_BIN STREQUAL "") + find_program(ZIG_BIN zig REQUIRED) +endif() +set(ZIG_BIN ${ZIG_BIN} CACHE FILEPATH "Zig command") +message(NOTICE "-- ZIG_BIN is ${ZIG_BIN}") + +# get zig libdir (for zig.h) +execute_process( + COMMAND ${ZIG_BIN} env + OUTPUT_VARIABLE ZIG_LIBDIR +) +string(JSON ZIG_LIBDIR GET ${ZIG_LIBDIR} lib_dir) +set(ZIG_LIBDIR ${ZIG_LIBDIR} CACHE PATH "from `zig env`") + +# ============================================================================= +# set compile options + +if(DEFINED ZIG_TARGET) + list(APPEND ZIG_CFLAGS "-target" "${ZIG_TARGET}") +endif() +if(DEFINED ZIG_CPU) + list(APPEND ZIG_CFLAGS "-mcpu=${ZIG_CPU}") +endif() + +if(NOT DEFINED ZIG_OPTLEVEL) + if(CMAKE_BUILD_TYPE STREQUAL Release) + set(ZIG_OPTLEVEL ReleaseFast) + elseif(CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo) + set(ZIG_OPTLEVEL ReleaseSafe) + elseif(CMAKE_BUILD_TYPE STREQUAL MinSizeRel) + set(ZIG_OPTLEVEL ReleaseSmall) + else() + # also the Debug case + set(ZIG_OPTLEVEL ${CMAKE_BUILD_TYPE}) + endif() +endif() +list(APPEND ZIG_CFLAGS "-O" "${ZIG_OPTLEVEL}") + +# ============================================================================= +# now define the macro we'll be using + +function(add_zig_object TOP_PROJECT ZIG_TOP_SOURCE_FILE) + if(ARGC GREATER 2) + set(TARGET_NAME ${ARGV2}) + else() + cmake_path(GET ZIG_TOP_SOURCE_FILE STEM TARGET_NAME) + endif() + + # directory the header file will be in + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated/zig_${TARGET_NAME}/) + # the compile command + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj + # TODO: when is this generator expression evaluated??? (matters for direct rust<->zig references) + COMMAND ${ZIG_BIN} build-obj ${ZIG_CFLAGS} -femit-bin=${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj -femit-h=${CMAKE_CURRENT_BINARY_DIR}/generated/zig_${TARGET_NAME}/${TARGET_NAME}.h "$,PREPEND,-I>" ${ZIG_TOP_SOURCE_FILE} + # ${TARGET_NAME}.obj.o is a bug in zig currently + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj.o ${CMAKE_CURRENT_BINARY_DIR}/generated/zig_${TARGET_NAME}/${TARGET_NAME}.h + MAIN_DEPENDENCY ${ZIG_TOP_SOURCE_FILE} + DEPENDS ${ZIG_TOP_SOURCE_FILE} + #DEPFILE ${TARGET_NAME}.obj.d # no way to generate it :/ + VERBATIM + COMMAND_EXPAND_LISTS + ) + # add a virtual library that can be reused and referred to (not possible with a bare add_custom_command()) + add_library(${TARGET_NAME} OBJECT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj) + + # add generated object file from the library to the code to compile + target_sources(${TOP_PROJECT} PUBLIC $) + # add directory of zig.h and the generated header to the top project's stuff + target_include_directories(${TOP_PROJECT} PRIVATE ${ZIG_LIBDIR} ${CMAKE_CURRENT_BINARY_DIR}/generated/zig_${TARGET_NAME}/) + # work around https://github.com/ziglang/zig/issues/16730 + target_compile_definitions(${TOP_PROJECT} PUBLIC ZIG_TARGET_MAX_INT_ALIGNMENT="\(sizeof\(intmax_t\)\)") +endfunction() + diff --git a/inc/clib.h b/inc/clib.h new file mode 100644 index 0000000..f5fc434 --- /dev/null +++ b/inc/clib.h @@ -0,0 +1,8 @@ + +#ifndef CLIB_H_ +#define CLIB_H_ + +void print_my_int(int x); + +#endif + diff --git a/src/clib.c b/src/clib.c new file mode 100644 index 0000000..5791c2f --- /dev/null +++ b/src/clib.c @@ -0,0 +1,9 @@ + +#include + +#include "clib.h" + +void print_my_int(int x) { + iprintf("the int is: %d\n", x); +} + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f0859e3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,30 @@ + +#include +#include +#include + +#include +#include +#include + + +// same filenames as .rs/.zig files, but with a .h extension of course +#include "rustcode.h" +#include "zigcode.h" + +int main() { + busy_wait_ms(16); + + stdio_init_all(); + + iprintf("[main] hello world from C!\n"); + + iprintf("[main] call into rust: "); + add_and_print(413, 612); + + iprintf("[main] call into zig: "); + mul_and_print(20, 25); + + return 0; +} + diff --git a/src/rustcode.rs b/src/rustcode.rs new file mode 100644 index 0000000..9c58aeb --- /dev/null +++ b/src/rustcode.rs @@ -0,0 +1,21 @@ +#![no_std] + +use clib_bindgen; // c header file basename + _bindgen suffix + +#[no_mangle] +pub unsafe extern "C" fn add_and_print(a: i32, b: i32) { + clib_bindgen::print_my_int(a + b); +} + +extern "C" { + fn non_existent_fn(); +} + +#[panic_handler] +pub fn panic(_info: &core::panic::PanicInfo) -> ! { + unsafe { + non_existent_fn(); // makes sure to cause linker errors if the rust code would ever + // actually use this function + } + loop {} +} diff --git a/src/zigcode.zig b/src/zigcode.zig new file mode 100644 index 0000000..a388dfe --- /dev/null +++ b/src/zigcode.zig @@ -0,0 +1,8 @@ + +const c = @cImport({ + @cInclude("clib.h"); +}); + +export fn mul_and_print(a: i32, b: i32) void { + c.print_my_int(a * b); +}