I want to use C code which depends on babeltrace2 in Rust.
For now I have in my main.rs, where prepare_graph is a custom C function:
#[link(name = "bt_util", kind = "static")]
extern "C" {
fn prepare_graph(
ctf_directory: *const c_char,
plugins_directory: *const c_char
) -> c_int;
}
with libbabeltrace/bt_util.c:
#include <babeltrace2/babeltrace.h>
#include "bt_util.h"
int prepare_graph(const char *ctf_directory, const char *plugins_directory) {
bt_graph *graph = bt_graph_create(0);
int error_code = 0;
bt_plugin_find_all_from_dir_status ret;
const bt_plugin_set *plugins;
...
}
it is built with the following build.rs:
extern crate cmake;
use cmake::Config;
fn main(){
let babeltrace_path = Config::new("libbabeltrace").build();
println!("cargo:rustc-link-search=native={}", babeltrace_path.display());
println!("cargo:rustc-link-lib=static=bt_util");
}
and the libbabeltrace/CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
project(LibBabeltrace C)
set(CMAKE_C_STANDARD 17)
include(ExternalProject)
include(FetchContent)
set(EXTERNAL_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/external)
include_directories(${EXTERNAL_INSTALL_LOCATION}/include)
link_directories(${EXTERNAL_INSTALL_LOCATION}/lib)
ExternalProject_Add(babeltrace2
GIT_REPOSITORY https://github.com/efficios/babeltrace.git
GIT_TAG v2.0.4
BUILD_IN_SOURCE 0
WORKING_DIR ${CMAKE_CURRENT_SOURCE_DIR}/babeltrace2
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/babeltrace2
CONFIGURE_COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/babeltrace2/bootstrap && BABELTRACE_DEBUG_MODE=1 BABELTRACE_DEV_MODE=1 BABELTRACE_MINIMAL_LOG_LEVEL=TRACE BABELTRACE_PLUGINS_DIR=${CMAKE_CURRENT_SOURCE_DIR}/babeltrace2/plugins ./configure --disable-man-pages --disable-glibtest --disable-doxygen-doc --disable-doxygen-html --prefix "${EXTERNAL_INSTALL_LOCATION}"
BUILD_COMMAND ${MAKE_EXE}
BUILD_ALWAYS FALSE
)
add_custom_target(build_external
DEPENDS babeltrace2
)
add_library(bt_util STATIC src/bt_util.c)
install(TARGETS bt_util DESTINATION .)
As can be seen the libbabeltrace/bt_util.c depends on an external library (babeltrace2).
This leads to a compilation error within the build.rs which is due to not being able to link the external library.
Compiling ctf_reader v0.1.0 (/home/tk/Documents/bitaggregat/minimal-stide-examples/Rust)
error: linking with `cc` failed: exit status: 1
|
...
= note: /usr/bin/ld: .../target/debug/build/ctf_reader-13cbb68ba72803ed/out/libbt_util.a(bt_util.c.o): in function `my_consumer_func':
.../libbabeltrace/src/bt_util.c:18: undefined reference to `bt_message_get_type'
/usr/bin/ld: .../libbabeltrace/src/bt_util.c:24: undefined reference to `bt_message_event_borrow_event_const'
/usr/bin/ld: .../libbabeltrace/src/bt_util.c:25: undefined reference to `bt_event_borrow_class_const'
/usr/bin/ld: .../libbabeltrace/src/bt_util.c:29: undefined reference to `bt_event_borrow_payload_field_const'
...
error: could not compile `ctf_reader` due to previous error
Couldn't find resources on including C with dependencies.
I know there is also a babeltrace2-sys crate. But I couldn't get this to work with system call traces.
Any help appreciated.
Related
I'm able to get Zig to create a C library but when I attempt to use said library from a C program, it fails to find the definition of the included function.
My library definition:
const std = #import("std");
export fn removeAll(name: [*]const u8, len: u32) u32 {
const n: []const u8 = name[0..len];
std.fs.cwd().deleteTree(n) catch |err| {
return 1;
};
return 0;
}
test "basic remove functionality" {
}
build.zig
const Builder = #import("std").build.Builder;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const lib = b.addStaticLibrary("removeall", "src/main.zig");
lib.setBuildMode(mode);
switch (mode) {
.Debug, .ReleaseSafe => lib.bundle_compiler_rt = true,
.ReleaseFast, .ReleaseSmall => lib.disable_stack_probing = true,
}
lib.force_pic = true;
lib.setOutputDir("build");
lib.install();
var main_tests = b.addTest("src/main.zig");
main_tests.setBuildMode(mode);
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
}
zig build creates the build directory with the libremoveall.a static library.
My C program:
#include <stdio.h>
int removeAll(char *, int);
int main(int argc, char **argv)
{
removeAll("/tmp/mytest/abc", 15);
return 0;
}
When I attempt to include it in my C program, it get the following error:
gcc -o main build/libremoveall.a main.c
/usr/bin/ld: /tmp/cckS27fw.o: in function 'main':
main.c:(.text+0x20): undefined reference to 'removeAll'
Any ideas on what I'm doing wrong?
Thanks
EDIT
Thanks Paul R and stark, flipping the order worked. Can you help me understand why the order matter?
The linker searches a library for unresolved references encountered so far in the process.
If you put your program after the library, there are no unresolved references when the linker reads the library. And then when it reads your program, there is no library to resolve the reference.
Swap your program and the library, and you are all set.
I am trying to port my simple application from C to Rust. It was running only on my Mac, with a library on Mac only. Here is a simplified version of the failed part in C code
// myLog.h
#include <os/log.h> // macOS header
void debug(const char *str);
//************************************
// myLog.c
#include "myLog.h"
void debug(const char* str) {
// call the macOS log function
os_log_debug(OS_LOG_DEFAULT, "%{public}s", str);
}
This code can be compiled simply calling gcc debug.c, and it works fine.
Then I added the .h and .c to my rust project with bindgen specified like below
fn main() {
println!("cargo:rerun-if-changed=myLog.h");
let bindings = bindgen::Builder::default()
.header("myLog.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to build bindgen");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("mylog_bindings.rs"))
.expect("Couldn't write bindings!");
}
And the main function has no other functions, but testing the log for now:
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::ffi::CString;
include!(concat!(env!("OUT_DIR"), "/mylog_bindings.rs"));
fn main() {
let log_infomation = CString::new("Log from Rust").expect("Failed to create c string");
let c_pointer = log_infomation.as_ptr();
unsafe {
debug(c_pointer);
}
}
The program failed with following error:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-m64" "-arch" "x86_64" "-L" ......
= note: Undefined symbols for architecture x86_64:
"_debug", referenced from:
bindgen_test::main::hc0e5702b90adf92c in bindgen_test.3ccmhz8adio5obzw.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: aborting due to previous error; 2 warnings emitted
error: could not compile `bindgen_test`.
I am not sure why this failed, but I found if I remove the whole unsafe block (without calling the function), the compilation will work. But can someone explain to me what I did wrong? Is there something I need to add to make it compile?
Thank you very much!
The problem is that you are not including the myLog.c file anywhere, only the myLog.h header. This is what bindgen does: it converts a C header file into Rust code, but it does not compile the C code itself.
For that you need the cc crate. You have to use both cc and bindgen together in your build.rs file:
use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=myLog.h");
println!("cargo:rerun-if-changed=myLog.c"); // new line here!!
let bindings = bindgen::Builder::default()
.header("myLog.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to build bindgen");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("mylog_bindings.rs"))
.expect("Couldn't write bindings!");
//Compile and link a static library named `myLog`:
cc::Build::new()
.file("myLog.c")
.compile("myLog");
}
And do not forget to add the cc crate to your build-dependencies.
I have some C code which I compiled to a .so file which i want to be called from a Rust program.
// hello.c
void greet() {
printf("Hello, world");
}
so I compiled it to a shared object file and added it to my build.rs and it worked fine
// main.rs
#[link(name = "hello")]
extern "C" {
fn greet();
}
fn main() {
unsafe {
greet();
}
}
The problem is the I have a second function in my C code which accepts a char* as a parameter so I tried to use libc::c_char to communicate between C and Rust but whenever my program doesn't compile when I import libc.
// main.rs
#[link(name = "hello")]
use libc::c_char;
extern "C" {
greet();
}
And I already tried to compile just with import libc (because I thought that might have been the problem) but it works perfectly so it seems like the program only doesn't compile when I am using my C shared object and importing the libc crate.
This is the error message
error: linking with `cc` failed: exit code: 1
= note: "cc"
= note: Undefined symbols for architecture x86_64:
"_greet", referenced from:
project::main::h501a37fa09c5db9f in project.2q2eogqn7p5k3u7s.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The #[link] attribute must be just before the extern block. By inserting a use between the #[link] attribute and the extern block, the #[link] attribute becomes attached to the use and has no effect. (There really should be a warning for this...)
Works just fine for me, are you sure you compiled a static library that Rust linker can use regardless of what else gets linked into the final executable?
I can only guess that this is whats wrong as you haven't provided how exactly you setup your project, and I'd recommend letting cc crate handle it for you, and if you really need something it doesn't have, contribute to it, instead of manually compiling C code and trying to link it in.
Example
build.rs
fn main() {
cc::Build::new()
.file("src/hello.c")
.compile("hello");
}
src/hello.c
#include <stdio.h>
void greet() {
printf("Hello, world\n");
}
src/main.rs
use libc::c_char;
#[link(name = "hello")]
extern "C" {
fn greet();
}
fn main() {
unsafe {
greet();
}
}
cli
$ cargo run
Compiling link v0.1.0 (~/Desktop/link)
warning: unused import: `libc::c_char`
--> src/main.rs:4:5
|
4 | use libc::c_char;
| ^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: 1 warning emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
Running `~/.cargo/target/debug/link`
Hello, world
I tried to write a simple D Program and use it to access a simple C library but there is unknown error.
My c Code, Box.c
#include "Box.h"
int SayHello( int _int)
{
_int ++;
return _int;
}
My c header file, Box.h
#ifndef BOX_H_INCLUDED
#define BOX_H_INCLUDED
/* export interfaces */
#ifdef __cplusplus
extern "C" {
#endif
int SayHello( int _int);
#ifdef __cplusplus
}
#endif
#endif // BOX_H_INCLUDED
I compile it
gcc -c Box.c Box.h
resulting files
Box.o
Box.h.gch
I place them to my D Program's project directory
My D Code
module main;
import std.stdio;
import std.conv;
import std.c.stdio;
import clib;
int main(string[] args)
{
// test external c library
auto s = to!string( SayHello(3) ) ;
writefln( "my int is "~ s );
readln();
return 0;
}
My D interface file ( clib ), trying to link to my C library
module clib;
import std.c.stdio;
extern (C) int SayHello( int _int);
The error I get when I compile it using codeblocks
Compiling: hello.d
Linking console executable: bin/Debug/tutorial03-access-c-library4
obj/Debug/hello.o: In function `_Dmain':
/home/hamilton/Tutorial/tutorial03-access-c-library4/hello.d:11: **undefined reference to `SayHello'**
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings
Error is "undefined reference to `SayHello'"
There is no error I get when I compile it using command in console
$ dmd Box.o hello.d clib.di
it will be very painful if I cannot use codeblocks as I need the debugging functionality.
Thanks
Update:
Compiler setting in codeblocks as followed
Linker for dynamic libs: gcc -m32 -lrt
Linker for static libs: ar
Debugger: gdb
You can change the build options in CodeBlocks from Project -> Build Options, Compiler settings -> Other options. The simplest thing to do would be to just add Box.o to Other options.
I'm trying to build a basic FANN (Fast Artificial Neural Network) project on Windows with MinGW. However, whenever I try to link the executable, I run into a bunch of undefined reference to errors. Interestingly, if I don't link the library at all, I get more errors, implying that at least some of the library is working. The code for the file I'm trying to compile and link is:
#include "doublefann.h"
int main() {
const unsigned int num_input_neurons = 9;
const unsigned int num_output_neurons = 1;
const unsigned int num_layers = 3;
const unsigned int num_hidden_neurons = 9;
const float desired_error = (const float) 0;
const unsigned int max_epochs = 500000;
const unsigned int epochs_between_reports = 1000;
struct fann *ann = fann_create_standard(num_layers,
num_input_neurons,
num_hidden_neurons,
num_output_neurons);
fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC);
fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC);
fann_train_on_file(ann,
"titanic-training.data",
max_epochs,
epochs_between_reports,
desired_error);
fann_save(ann, "titanic.net");
fann_destroy(ann);
return 0;
}
and the command I'm using to compile and link is:
gcc -Wall -Ifann\src\include titanic-train.c -Lfann\bin -lfanndouble -o titanic-train.exe
The errors I'm getting back are:
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0x7f): undefined reference to `fann_set_activation_function_hidden'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0x93): undefined reference to `fann_set_activation_function_output'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0xbf): undefined reference to `fann_train_on_file'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0xd3): undefined reference to `fann_save'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0xdf): undefined reference to `fann_destroy'
c:/fragileprograms/mingw-native/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o: bad reloc address 0x64 in section `.rdata'
c:/fragileprograms/mingw-native/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: final link failed: Invalid operation
collect2.exe: error: ld returned 1 exit status
If I don't link the library at all, I instead get:
C:\Users\kunkelwe\AppData\Local\Temp\ccyOO3jL.o:titanic-train.c:(.text+0x67): undefined reference to `fann_create_standard'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0x7f): undefined reference to `fann_set_activation_function_hidden'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0x93): undefined reference to `fann_set_activation_function_output'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0xbf): undefined reference to `fann_train_on_file'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0xd3): undefined reference to `fann_save'
C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o:titanic-train.c:(.text+0xdf): undefined reference to `fann_destroy'
c:/fragileprograms/mingw-native/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: C:\Users\kunkelwe\AppData\Local\Temp\ccsWQg66.o: bad reloc address 0x64 in section `.rdata'
c:/fragileprograms/mingw-native/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: final link failed: Invalid operation
collect2.exe: error: ld returned 1 exit status
Edit:
As per Haroogan's request, I ran nm fanndouble.lib. The output is rather extensive, so rather than paste it all here, I've made it available via pastebin here: http://pastebin.com/raw.php?i=vybFhEcX
I'm not familiar with nm, but it appears that the missing symbols do exist in the file.
Edit #2:
The contents of doublefann.h are: http://pastebin.com/mrHKJi8C
and the contents of fann.h, which it includes are: http://pastebin.com/gTrHCYAg
Could the problem just be solved by recompiling the library with MinGW?
Edit #3:
Making the changes that Haroogan suggested worked! In addition to those changes, I had to modify the CMakeLists.txt file for FANN by adding:
if (WIN32)
ADD_DEFINITIONS(-DFANN_DLL_EXPORTS)
endif (WIN32)
Then, running cmake -G "MinGW Makefiles" and then mingw32-make in the root of the project produced a file, libdoublefann.dll, that when linked against and included in the directory of the .exe, allowed me, finally, to run my program.
In doublefann.h on the line #116:
#if (_MSC_VER > 1300)
change to:
#if (_MSC_VER > 1300) || defined(__MINGW32__) || defined(__MINGW64__)
Furthermore, on the line #121:
#if defined(_MSC_VER) && (defined(FANN_USE_DLL) || defined(FANN_DLL_EXPORTS))
change to:
#if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)) && \
(defined(FANN_USE_DLL) || defined(FANN_DLL_EXPORTS))