Calling C from Rust with external headers in C file - c

I am trying to call C code from Rust, I succeeded! The only issue I get is when I try to take other libraries with me... the documentation says that you should include all headers in the one file you try to load in Rust, so I do this in the .c file...
#include <stdio.h>
#include <stdbool.h>
#include "dependencies/glfw-3.3.4/include/GLFW/glfw3.h"
int someFunc() {
int i = glfwInit();
glfwTerminate();
return i;
}
But when I run the Rust program, it says glfwInit and glfwTerminate are unresolved symbols... If I do this instead:
...
#include <GLFW/glfw3.h>
...
I get the error in my Rust program that there is no such directory, though the library was linked correctly using CMake... I read something about a cmake crate, so I am guessing it has something to do with that, but at the moment I am completely clueless. I must say that I am new to both Rust and C/C++ (student in Computer Science...) :slight_smile:
Note that when I remove any glfw related stuff, everything works correctly!
I am thinking if I can somehow invoke CMake from the build.rs file, that I can link everything in the C project correctly, right? I just do not understand very much CMake since I have been doing everything with an IDE (CLion by jet brains) and very limited commands in CMakeLists.txt.
This is how the project is organised...
project structure:
// generated with cargo new rust_project ...
.../rust_project/
c_project/ // This was generated with the IDE for C (CLion)...
dependecies/
glfw-3.3.4/
include/
...
CMakeLists.txt
MyCFile.c
...
src
main.rs
build.rs
cargo.toml
...
CMakeLists.txt:
cmake_minimum_required(VERSION 3.17)
project(c_project)
set(CMAKE_C_STANDARD 11)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dependencies/glfw-3.3.4)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/dependencies/glfw-3.3.4/include)
##some more libraries... not currently included in MyCFile.c !##
add_library(c_project MyCFile.c)
target_link_libraries(c_project PRIVATE <other_libraries> glfw ${GLFW_LIBRARIES} <other_libraries>)
MyCFile.c:
#include <GLFW/glfw3.h>
int someFunc() {
int i = glfwInit();
glfwTerminate();
return i;
}
main.rs:
extern "C" {
fn someFunc() -> i32;
}
fn main() {
unsafe {
println!("{}", someFunc());
}
}
build.rs
extern crate cc;
fn main() {
cc::Build::new()
.file("c_project/MyCFile.c")
.compile("library");
/*
* I am guessing here goes something along the lines:
* "CMakeLists.txt".execute (as pseudo code...)
*/
}
cargo.toml:
[package]
name = "rust_project"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[build-dependencies]
cc = "1.0.70"
the error message upon "cargo check":
error: failed to run custom build command for `version_zero v0.1.0 (D:\...\rust_project)`
Caused by:
process didn't exit successfully: `D:\...\rust_project\target\debug\build\rust_project-bf398c13c8af8b0c\build-script-build` (exit code: 1)
--- stdout
TARGET = Some("x86_64-pc-windows-msvc")
OPT_LEVEL = Some("0")
HOST = Some("x86_64-pc-windows-msvc")
CC_x86_64-pc-windows-msvc = None
CC_x86_64_pc_windows_msvc = None
HOST_CC = None
CC = None
CFLAGS_x86_64-pc-windows-msvc = None
CFLAGS_x86_64_pc_windows_msvc = None
HOST_CFLAGS = None
CFLAGS = None
CRATE_CC_NO_DEFAULTS = None
CARGO_CFG_TARGET_FEATURE = Some("fxsr,sse,sse2")
DEBUG = Some("true")
running: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-Z7" "-Brepro" "-W4" "-FoD:\\...\\rust_project\\target\\debug\\build\\version_zero-54603a96dee57aac\\out\\c_project/MyCFile.o" "-c" "c_project/MyCFile.c"
MyCFile.c
c_project/MyCFile.c(5): fatal error C1083: Cannot open include file: 'GLFW/glfw3.h': No such file or directory
exit code: 2
--- stderr
error occurred: Command "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-Z7" "-Brepro" "-W4" "-FoD:\\...\\rust_project\\target\\debug\\build\\version_zero-54603a96dee57aac\\out\\c_project/MyCFile.o" "-c" "c_project/MyCFile.c" with args "cl.exe" did not execute successfully (status code exit code: 2).
I used: https://crates.io/crates/cc, https://liufuyang.github.io/2020/02/02/call-c-in-rust.html, https://docs.rust-embedded.org/book/interoperability/c-with-rust.html
CMake was installed through: https://cmake.org/download/.
Downloaded the option: "Windows x64 Installer: Installer tool has changed. Uninstall CMake 3.4 or lower first! cmake-3.21.2-windows-x86_64.msi"
Honoustly don't know how you would do it with the zip files...
tried to uninstall the current CMake, and downloaded version 3.4 (and installed it).
Also changed the build.rs file to:
use cmake;
fn main() {
let dst = cmake::build("c_project");
println!("cargo:rustc-link-search=native={}", dst.display());
println!("cargo:rustc-link-lib=static=MyCFile");
}
cargo check compiles just fine, cargo run, will produce an error:
<lots of file paths...>
= note: LINK : fatal error LNK1181: cannot open input file 'MyCFile.lib'
Note that I do not get any indications anymore of cmake not being installed...

Cannot open include file: 'GLFW/glfw3.h': No such file or directory - the error states that the c compiler cannot find the header file glfw3.h from your build point. The cc crate provides the .include for it's builder. Find out where you have the glfw3.h file and pass its path into the include builder method:
fn main() {
cc::Build::new()
.file("c_project/MyCFile.c")
.include("path/to/glfw3.h")
.compile("library");
/*
* I am guessing here goes something along the lines:
* "CMakeLists.txt".execute (as pseudo code...)
*/
}

Related

Rust bindgen cannot find platform specific library?

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.

Why does use libc stop cargo from linking my program correctly?

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

Yocto / Poky: install and use shared library .so on separate layers

I'm trying to learn how to build custom linux images with Yocto and I'm struggling to create an image with both a shared library and a program that uses it.
I started by following this tutorial and everything went ok. Then I tried to separate the program from the library in two different layers, without success.
I started with the library code:
greetings.c
#include <stdio.h>
#include <string.h>
#include "greetings.h"
void get_greeting(char * buffer) {
if(buffer == NULL) {
return;
}
char greeting[] = "Hello world from the greetings lib\n";
strcpy(buffer, greeting);
return;
}
greetings.h
void get_greeting(char * buffer);
Makefile.am
AUTOMAKE_OPTIONS = foreign
lib_LTLIBRARIES = libgreetings.la
libgreetings_la_SOURCES = greetings.c
include_HEADERS = greetings.h
libgreetings_la_CFLAGS = -Wall -Werror -fPIC
libgreetings_la_LDFLAGS = -shared
ACLOCAL_AMFLAGS = -I m4
configure.ac
AC_INIT([Greetings lib], 1.0)
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_CONFIG_MACRO_DIR([m4])
LT_INIT()
AC_CONFIG_FILES(Makefile)
AC_OUTPUT
I added this code to a git repository and created a "meta-greetings" layer with the layer.conf and recipe files:
layer.conf
# We have a conf and classes directory, add to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have recipes-* directories, add to BBFILES
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-greetings"
BBFILE_PATTERN_meta-greetings = "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-greetings = "6"
LAYERDEPENDS_meta-greetings = "core"
LAYERSERIES_COMPAT_meta-greetings = "thud"
IMAGE_INSTALL_append = " greetings"
recipes-greetings/greetings/greetings_0.1.bb
SUMMARY = "bitbake-layers recipe"
DESCRIPTION = "Simple helloworld lib"
DEPENDS = ""
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=96af5705d6f64a88e035781ef00e98a8"
KBRANCH = "master"
SRCREV = "1a908a8f8616af704ce71d693e88c6d4498f24c4"
SRC_URI = "git://bitbucket.org/Grifo/greetings_lib.git;branch=${KBRANCH};protocol=ssh"
S = "${WORKDIR}/git"
inherit autotools
So far so good, I added this layer to my bblayers file and proceeded to compile the final image. I run it in qemu and even got to see the files in /usr/lib:
However, there's no "libgreetings.so". I don't know if that may be the cause of the problem (still to explain) but the previously mentioned tutorial got similar results so I proceeded.
After that I did the program:
helloworld.c
#include <stdio.h>
#include "greetings.h"
int main(void) {
char greeting[40];
get_greeting(greeting);
printf("Hello world!\n");
printf("%s", greeting);
return 0;
}
Makefile.am
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = hello_world
hello_world_SOURCES = helloworld.c
hello_world_LDADD = $(libdir)/libgreetings.so
ACLOCAL_AMFLAGS = -I m4
configure.ac
AC_INIT([Hello world], 1.0)
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_FILES(Makefile)
AC_OUTPUT
Added this code to git and created a "meta-helloworld" layer with the files:
layer.conf
# We have a conf and classes directory, add to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have recipes-* directories, add to BBFILES
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-helloworld"
BBFILE_PATTERN_meta-helloworld = "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-helloworld = "7"
LAYERDEPENDS_meta-helloworld = "core meta-greetings"
LAYERSERIES_COMPAT_meta-helloworld = "thud"
IMAGE_INSTALL_append = " helloworld"
recipes-helloworld/helloworld/helloworld_0.1.bb
SUMMARY = "bitbake-layers helloworld"
DESCRIPTION = "Simple helloworld program"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=96af5705d6f64a88e035781ef00e98a8"
KBRANCH = "master"
SRCREV = "6a29425473286028e85e74003f2f57ecaf766354"
SRC_URI = "git://bitbucket.org/Grifo/hello_world.git;branch=${KBRANCH};protocol=ssh"
DEPENDS = "greetings"
S = "${WORKDIR}/git"
inherit autotools
After all this I bitbaked the final image again but got the following error:
(...)
Sstate summary: Wanted 7 Found 0 Missed 7 Current 737 (0% match, 99% complete)
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
ERROR: helloworld-0.1-r0 do_compile: oe_runmake failed
ERROR: helloworld-0.1-r0 do_compile: Function failed: do_compile (log file is located at /var/tmp/workspaces/grifo/poky/build/tmp/work/armv5e-poky-linux-gnueabi/helloworld/0.1-r0/temp/log.do_compile.12040)
ERROR: Logfile of failure stored in: /var/tmp/workspaces/grifo/poky/build/tmp/work/armv5e-poky-linux-gnueabi/helloworld/0.1-r0/temp/log.do_compile.12040
Log data follows:
| DEBUG: SITE files ['endian-little', 'bit-32', 'arm-common', 'arm-32', 'common-linux', 'common-glibc', 'arm-linux', 'arm-linux-gnueabi', 'common']
| DEBUG: Executing shell function do_compile
| NOTE: make -j 8
| make: *** No rule to make target `/usr/lib/libgreetings.so', needed by `hello_world'. Stop.
| ERROR: oe_runmake failed
| WARNING: /var/tmp/workspaces/grifo/poky/build/tmp/work/armv5e-poky-linux-gnueabi/helloworld/0.1-r0/temp/run.do_compile.12040:1 exit 1 from 'exit 1'
| ERROR: Function failed: do_compile (log file is located at /var/tmp/workspaces/grifo/poky/build/tmp/work/armv5e-poky-linux-gnueabi/helloworld/0.1-r0/temp/log.do_compile.12040)
ERROR: Task (/var/tmp/workspaces/grifo/poky/meta-helloworld/recipes-helloworld/helloworld/helloworld_0.1.bb:do_compile) failed with exit code '1'
NOTE: Tasks Summary: Attempted 1966 tasks of which 1965 didn't need to be rerun and 1 failed.
Summary: 1 task failed:
/var/tmp/workspaces/grifo/poky/meta-helloworld/recipes-helloworld/helloworld/helloworld_0.1.bb:do_compile
Summary: There was 1 WARNING message shown.
Summary: There were 2 ERROR messages shown, returning a non-zero exit code.
I am sorry for the really long question but I felt like I need to give all the details since I don't know if the problem comes from my recipe or my autotools files.
Before I built the recipes and compile it using yocto, I first compiled and run it in my host computer using the shell and everything run fine. I compiled and make install the greetings library (/usr/local/lib) and after that compiled the helloworld program which run without any problem.
I know that I could probably do this easily all within the same layer, however I'm trying to do it in separate layers to simulate different projects. Another requirement of mine is to use autotools instead of cmake.
Thank you in advance,
Grifo
EDIT:
I got it to work! Thank you Alexander Kanavin for pointing me in the right direction. I just had to change hello_world_LDADD = $(libdir)/libgreetings.so to hello_world_LDADD = -lgreetings in my helloworld's Makefile.am.
libgreetings.so is a file needed only for development and so it does not get installed to the image (unless you also install libgreetings-dev package - that's where it went).
During cross-compile, you typically specify libraries to link with like this:
-lgreetings
So change hello_world_LDADD = $(libdir)/libgreetings.so to hello_world_LDADD = -lgreetings.
I would start with that. Typically you shouldn't hardcode them like that in the makefile, but rather 'discover' and check the library in configure.ac (e.g. using pkg-config, assuming your library installs the corresponding .pc file), and set the appropriate compiler and linker flags:
PKG_CHECK_MODULES(GREETINGS, [greetings])
Then, in Makefile.am:
hello_world_LDADD = $(GREETINGS_LIBS)

Why does cmake give undefined references for netcdf with cygwin on windows?

I am trying to set up some model code on multiple operating systems using clion 2016.3-1 and its bundled cmake 3.6.2.
On Windows I am using the cygwin environment but I ran into a linking issue I can not resolve. Here is a minimum example of my code:
#include <stdlib.h>
#include <stdio.h>
#include <netcdf.h>
#define ERRCODE 2
#define ERR(e) {printf("Error: %s\n", nc_strerror(e)); exit(ERRCODE);}
int main() {
int ncid, varid, dimid, retval;
size_t nlat;
char filepath[] = "minimumExample.nc";
char dimname[64];
double *latitudes;
// open nc file
if ((retval = nc_open(filepath, NC_NOWRITE, &ncid))) ERR(retval);
// find variable IDs
if ((retval = nc_inq_varid(ncid, "latitude", &varid))) ERR(retval);
// find dimension bounds
if ((retval = nc_inq_vardimid(ncid, varid, &dimid))) ERR(retval);
if ((retval = nc_inq_dim(ncid, dimid, dimname, &nlat))) ERR(retval);
// allocate data array
latitudes = malloc(sizeof(double) * nlat);
// get data
if ((retval = nc_get_var_double(ncid, varid, &latitudes[0]))) ERR(retval);
// close nc file
if ((retval = nc_close(ncid))) ERR(retval);
// free data array
free(latitudes);
return(0);
}
Here is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.3)
project(test_examples)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lnetcdf")
include_directories("/usr/include")
link_directories("/usr/lib")
set(SOURCE_FILES main.c)
add_executable(test_examples ${SOURCE_FILES})
However I get a lot of error messages similar to:
undefined reference to `nc_open'
The binaries and include files are all present in the cygwin environment at the respective folders.
What am I missing?
The errors are saying that linker could not find code for that functions: in other words the library is not reachable.
You should use the CMake target_link_libraries function to link netcdf lib.
remove
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lnetcdf")
and add
target_link_libraries (test_examples netcdf)

Unzip one file with libzip

I need to create an application to extract one file from zip archive, after which I want to compile it for Android.
I'm using Ubuntu, with libzip-0.10.1 pre-installed.
I created C project in Eclipse, added include path and found simple script for extracting file. Unfortunately I cannot get the following to build and I could use some advice.
// zip.c file
#include <stdio.h>
#include <stdlib.h>
#include <zip.h>
int main(int argc, char **argv) {
struct zip *zip_file;
struct zip_file *file_in_zip;
int err;
int files_total;
int file_number;
int r;
char buffer[10000];
if (argc < 3) {
fprintf(stderr,"usage: %s <zipfile> <fileindex>\n",argv[0]);
return -1;
};
zip_file = zip_open(argv[1], 0, &err);
if (!zip_file) {
fprintf(stderr,"Error: can't open file %s\n",argv[1]);
return -1;
};
file_number = atoi(argv[2]);
files_total = zip_get_num_files(zip_file);
if (file_number > files_total) {
printf("Error: we have only %d files in ZIP\n",files_total);
return -1;
};
file_in_zip = zip_fopen_index(zip_file, file_number, 0);
if (file_in_zip) {
while ( (r = zip_fread(file_in_zip, buffer, sizeof(buffer))) > 0) {
printf("%s",buffer);
};
zip_fclose(file_in_zip);
} else {
fprintf(stderr,"Error: can't open file %d in zip\n",file_number);
};
zip_close(zip_file);
return 0;
};
Also I added few .h files to include directory in my project and few .c files to directory with zip.c file. After that all dependences was good, but I have an error:
‘struct zip’ has no member named ‘default_password’ in file zip_fopen_index.c
The file zip_fopen_index.c is:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "zipint.h"
ZIP_EXTERN struct zip_file *
zip_fopen_index(struct zip *za, zip_uint64_t fileno, int flags)
{
return zip_fopen_index_encrypted(za, fileno, flags, za->default_password); // error here
}
First of all allow me some comments:
Your program is not compiled and linked by Eclipse.
Compiling is done by the compiler (gcc using option -c):
make all
Building file: ../zip.c
Invoking: GCC C Compiler
gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"zip.d" -MT"zip.d" -o "zip.o" "../zip.c"
Finished building: ../zip.c
Linking is done by the linker (via the compiler using option -o):
Invoking: GCC C Linker
gcc -o "unzipper" ./zip.o
./main.o: In function `zip':
/home/alk/workspace/unzipper/Debug/../zip.c:20: undefined reference to `zip_open'
/home/alk/workspace/unzipper/Debug/../zip.c:27: undefined reference to `zip_get_num_files'
/home/alk/workspace/unzipper/Debug/../zip.c:33: undefined reference to `zip_fopen_index'
/home/alk/workspace/unzipper/Debug/../zip.c:35: undefined reference to `zip_fread'
/home/alk/workspace/unzipper/Debug/../zip.c:38: undefined reference to `zip_fclose'
/home/alk/workspace/unzipper/Debug/../zip.c:43: undefined reference to `zip_close'
collect2: ld returned 1 exit status
Eclipse provides a framework helping you in managing all sources and their references as also spawing compiler and linker tasks and setting their options.
When the linker told you there where undefined references to the zip_*function during the build of your program, the cause for this was, you were missing to tell the linker (via the compiler, via Eclipse) where those zip_* functions could be found.
Those zip_* functions are located in a library, namely libzip.
So what you as the programmer need to tell the linker (via the compiler, via Eclipse) is to link those functions against what the compiler compiled from your sources.
As the result the linker is able to create a runnable program from your compiled sources together with all libraries needed. Certain libraries are know to Eclipse (and therfore to the linker) by default, for example the one containing the C standard functions, namely libc.
To get things going:
1 Remove the source files you pulled from the libzip librarie's sources from your project. Those sources had been compiled into the library libzip, which you will use in your project.
2 Tell the linker (via Eclipse) to use libzip for your project.
Do so by following the steps below:
open the project's properties
click 'C/C++ General'
click 'Path and Symbols', on the left select the 'Libraries' tab, there click 'Add' and enter zip
finally click 'OK'
3 Then try to build your program:
Building target: unzipper
Invoking: GCC C Linker
gcc -o "unzipper" ./zip.o -lzip
Finished building target: unzipper
(Please note additional option -lzip!)
If the developement version of 'libzip' had been installed properly before, you should be fine.
PS: unzipper was the name I used for the Eclispe project to produce the examples.
PSS: I used Eclipse Juno SR1

Resources