c-binding demo

This commit is contained in:
Christoph J. Scherr 2023-09-12 18:21:59 +02:00
parent 790fbbac4a
commit 27afb6247b
8 changed files with 102 additions and 233 deletions

25
Cargo.lock generated
View File

@ -5,6 +5,31 @@ version = 3
[[package]] [[package]]
name = "c-bindings" name = "c-bindings"
version = "0.1.0" version = "0.1.0"
dependencies = [
"cc",
"cty",
]
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]] [[package]]
name = "rs-basic" name = "rs-basic"

View File

@ -2,7 +2,13 @@
name = "c-bindings" name = "c-bindings"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
links = "test" # this is the important part! cargo will search for some library called `test` (i.e. libtest.a)
build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
cty = "0.2.2"
[build-dependencies]
cc = "1.0.83"

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.9)
project(test VERSION 1.0.1 DESCRIPTION "test lib") project(test VERSION 1.0.1 DESCRIPTION "test lib")
include(GNUInstallDirs) include(GNUInstallDirs)
add_library(test SHARED test.c) add_library(test STATIC test.c)
set_target_properties(test PROPERTIES set_target_properties(test PROPERTIES
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
SOVERSION 1 SOVERSION 1

View File

@ -1,230 +0,0 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.22
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Disable VCS-based implicit rules.
% : %,v
# Disable VCS-based implicit rules.
% : RCS/%
# Disable VCS-based implicit rules.
% : RCS/%,v
# Disable VCS-based implicit rules.
% : SCCS/s.%
# Disable VCS-based implicit rules.
% : s.%
.SUFFIXES: .hpux_make_needs_suffix_list
# Command-line flag to silence nested $(MAKE).
$(VERBOSE)MAKESILENT = -s
#Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
# Set environment variables for the build.
# The shell in which to execute make rules.
SHELL = /bin/sh
# The CMake executable.
CMAKE_COMMAND = /usr/bin/cmake
# The command to remove a file.
RM = /usr/bin/cmake -E rm -f
# Escaping for special characters.
EQUALS = =
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /home/cscherr/Documents/code/rust/rs-basic/members/c-bindings/lib
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /home/cscherr/Documents/code/rust/rs-basic/members/c-bindings/lib
#=============================================================================
# Targets provided globally by CMake.
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
/usr/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# Special rule for the target list_install_components
list_install_components:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\""
.PHONY : list_install_components
# Special rule for the target list_install_components
list_install_components/fast: list_install_components
.PHONY : list_install_components/fast
# Special rule for the target install
install: preinstall
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
/usr/bin/cmake -P cmake_install.cmake
.PHONY : install
# Special rule for the target install
install/fast: preinstall/fast
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
/usr/bin/cmake -P cmake_install.cmake
.PHONY : install/fast
# Special rule for the target install/local
install/local: preinstall
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
/usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
.PHONY : install/local
# Special rule for the target install/local
install/local/fast: preinstall/fast
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
/usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
.PHONY : install/local/fast
# Special rule for the target install/strip
install/strip: preinstall
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
/usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
.PHONY : install/strip
# Special rule for the target install/strip
install/strip/fast: preinstall/fast
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
/usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
.PHONY : install/strip/fast
# The main all target
all: cmake_check_build_system
$(CMAKE_COMMAND) -E cmake_progress_start /home/cscherr/Documents/code/rust/rs-basic/members/c-bindings/lib/CMakeFiles /home/cscherr/Documents/code/rust/rs-basic/members/c-bindings/lib//CMakeFiles/progress.marks
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 all
$(CMAKE_COMMAND) -E cmake_progress_start /home/cscherr/Documents/code/rust/rs-basic/members/c-bindings/lib/CMakeFiles 0
.PHONY : all
# The main clean target
clean:
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall/fast
# clear depends
depend:
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend
#=============================================================================
# Target rules for targets named test
# Build rule for target.
test: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 test
.PHONY : test
# fast build rule for target.
test/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles/test.dir/build.make CMakeFiles/test.dir/build
.PHONY : test/fast
test.o: test.c.o
.PHONY : test.o
# target to build an object file
test.c.o:
$(MAKE) $(MAKESILENT) -f CMakeFiles/test.dir/build.make CMakeFiles/test.dir/test.c.o
.PHONY : test.c.o
test.i: test.c.i
.PHONY : test.i
# target to preprocess a source file
test.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles/test.dir/build.make CMakeFiles/test.dir/test.c.i
.PHONY : test.c.i
test.s: test.c.s
.PHONY : test.s
# target to generate assembly for a file
test.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/test.dir/build.make CMakeFiles/test.dir/test.c.s
.PHONY : test.c.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@echo "... all (the default if no target is provided)"
@echo "... clean"
@echo "... depend"
@echo "... edit_cache"
@echo "... install"
@echo "... install/local"
@echo "... install/strip"
@echo "... list_install_components"
@echo "... rebuild_cache"
@echo "... test"
@echo "... test.o"
@echo "... test.i"
@echo "... test.s"
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system

View File

@ -1,5 +1,17 @@
#include "test.h" #include "test.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ret19() { int ret19() {
return 19; return 19;
} }
char* structInfo(MyStruct *st) {
char* buf = malloc(1024*sizeof(char));
/* snprintf(buf, sizeof(buf), "foo: %d\n", st->foo); */
/* snprintf(buf, sizeof(buf), "bar: %c", st->bar); */
sprintf(buf,"Infos about the struct:\nfoo:\t%d\nbar:\t%c\n\ngreetings from C", st->foo, st->bar);
return buf;
}

View File

@ -1 +1,6 @@
int ret19(); int ret19();
typedef struct MyStruct {
int foo;
char bar;
} MyStruct;
char* stuctInfo(MyStruct st);

View File

@ -0,0 +1,12 @@
///
/// we could simply do this to compile the test lib to a static lib,
/// but that solution might not scale as good as calling a professional
/// build system
#[allow(unused)]
fn main() {
// Tell Cargo that if the given file changes, to rerun this build script.
println!("cargo:rerun-if-changed=src/hello.c");
cc::Build::new()
.file("lib/test.c")
.compile("test");
}

View File

@ -1,3 +1,42 @@
fn main() { use cty;
println!("Hello, world!");
// we first need to declare bindings for our C code.
// This can be automated too, but I did it myself here.
#[derive(Debug)]
#[repr(C)]
pub struct MyStruct {
pub foo: cty::c_int,
pub bar: cty::c_char,
}
// The functions too of course
extern "C" {
pub fn ret19() -> isize;
pub fn structInfo(st: &MyStruct) -> *const cty::c_char;
}
///////////////////////////////////////////////////////////////////////
fn main() {
// calling random C functions is generally treated as unsafe.
// Best practice is programming wrapper functions that give it
// confirmed safe inputs
let qux = unsafe { ret19() };
println!("`ret19()` returned {qux}");
let st = MyStruct {
foo: 17, bar: 0x41
};
// converting a c "string" to a real rust String is
// a bit complicated
let info: &str = unsafe {
// convert the returned value to a rust internal CStr.
std::ffi::CStr::from_ptr(
// the function returns a pointer to a array of chars on the heap
structInfo(&st)
)
// now to a string slice (result)
.to_str()
.unwrap()
};
println!("{info}");
} }