Linking to a local C library for Rust with FFI - c

I am trying to wrap a quite large C library in Rust. I used bindgen to generate the bindings. Rust seems content with those. However, despite my different attempts and my many reads of the build-script docs, I keep on getting linking issues.
The library I'm using has a set of csh script which end up building two .a files: cspice.a and csupport.a. They are both stored in the cspice_linux_gcc_64bit/lib of my project (cf. the tree at bottom).
I currently test the proper binding by calling constants and functions from my tests, cf. lib.rs (on my Github).
I highly suspect that something is wrong with my build.rs or, less likely, my Cargo.toml.
Linking error
Running `rustc --crate-name spice src/lib.rs --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=94c06ffe4bfd7d5f -C extra-filename=-94c06ffe4bfd7d5f --out-dir /home/chris/Workspace/rust/SPICE/target/debug/deps -L dependency=/home/chris/Workspace/rust/SPICE/target/debug/deps -L native=cspice_linux_gcc_64bit/lib -l static=cspice`
error: could not find native static library `cspice`, perhaps an -L flag is missing?
error: Could not compile `spice`.
Caused by:
process didn't exit successfully: `rustc --crate-name spice src/lib.rs --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=94c06ffe4bfd7d5f -C extra-filename=-94c06ffe4bfd7d5f --out-dir /home/chris/Workspace/rust/SPICE/target/debug/deps -L dependency=/home/chris/Workspace/rust/SPICE/target/debug/deps -L native=cspice_linux_gcc_64bit/lib -l static=cspice` (exit code: 101)
Build failed, waiting for other jobs to finish...
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/home/chris/Workspace/rust/SPICE/target/debug/deps/spice-e22b64d176595306.0.o" "-o" "/home/chris/Workspace/rust/SPICE/target/debug/deps/spice-e22b64d176595306" "-Wl,--gc-sections" "-pie" "-nodefaultlibs" "-L" "/home/chris/Workspace/rust/SPICE/target/debug/deps" "-L" "cspice_linux_gcc_64bit/lib" "-L" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "-Wl,--whole-archive" "-l" "cspice" "-Wl,--no-whole-archive" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libtest-163c20e386ec0612.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgetopts-bd312ea730ea22d0.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libterm-4959b4e709084e0a.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-35ad9950c7e5074b.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-fb44afc024bbc636.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-14b8f3202acdad6a.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librand-20a50a22d4c2b1e9.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcollections-b479831207997444.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-10b591f1a68dd370.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc_jemalloc-28913dc5a1e63cd7.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-6ecacccb5bdc4911.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_unicode-f4f0ae88f5ad8ad4.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-bfaa82017ca17cb2.rlib" "/home/chris/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-863b57a66ba6c3e1.rlib" "-Wl,-Bdynamic" "-l" "dl" "-l" "rt" "-l" "pthread" "-l" "gcc_s" "-l" "pthread" "-l" "c" "-l" "m" "-l" "rt" "-l" "pthread" "-l" "util"
= note: /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: cannot find -lcspice
collect2: error: ld returned 1 exit status
error: aborting due to previous error(s)
error: Could not compile `spice`.
Caused by:
process didn't exit successfully: `rustc --crate-name spice src/lib.rs --emit=dep-info,link -C debuginfo=2 --test -C metadata=e22b64d176595306 -C extra-filename=-e22b64d176595306 --out-dir /home/chris/Workspace/rust/SPICE/target/debug/deps -L dependency=/home/chris/Workspace/rust/SPICE/target/debug/deps -L native=cspice_linux_gcc_64bit/lib -l static=cspice` (exit code: 101)
build.rs
extern crate bindgen;
use std::env;
use std::path::PathBuf;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
// Tell cargo to tell rustc to link the system bzip2
// shared library.
println!("cargo:rustc-link-search=native=cspice_linux_gcc_64bit/lib");
println!("cargo:rustc-link-lib=static=cspice");
//println!("cargo:rustc-flags=-L cspice_linux_gcc_64bit/lib");
// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("spice_wrapper.h")
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
let out_path = PathBuf::from(out_dir.clone());
// Write the bindings to the $OUT_DIR/bindings.rs file.
bindings
.write_to_file(out_path.join("spice_bindings.rs"))
.expect("Couldn't write bindings!");
}
Cargo.toml
[package]
(...)
links = "cspice"
build = "build.rs"
[build-dependencies]
bindgen = "0.26.3"
[dependencies]
Tree
.
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── cspice_linux_gcc_64bit
│   ├── data
│   ├── doc
│   ├── etc
│   ├── exe
│   ├── include
│   ├── lib
│   ├── makeall.csh
│   └── src
├── spice_wrapper.h
├── src
│   └── lib.rs
└── target
└── debug

building two .a files: cspice.a and csupport.a
On Linux, static libraries are generally called libfoo.a, but your library doesn't have the lib prefix.
Renaming the file to libcspice.a should allow it to compile. I'm not sure if you'd want to modify your build script or the libraries build script to perform the renaming. It's even possible that the library itself has a compilation switch to use a platform-standard naming scheme.

Related

Intermediate .obj files not getting generated when compiling a static C library in CMake

The intermediate .obj files aren't being created when building a static C library using CMake and the ARM GNU Toolchain.
System Information
System: Linux Debian 10 4.19.0-4-amd64
CMake: 3.13.4
ARM GNU Toolchain: 8-2018-q4-major
Files
Library Directory Structure
bsp
└── SDK_2.5.0_FRDM-KV11Z
├── CMSIS
│   ├── Driver
│   │   ├── DriverTemplates
│   │   └── Include
│   └── Include
├── components
│   ├── fxos8700cq
│   ├── lists
│   ├── serial_manager
│   └── uart
└── devices
└── MKV11Z7
├── arm
├── cmsis_drivers
├── drivers
├── mcuxpresso
├── project_template
├── template
└── utilities
├── debug_console
└── str
Top Level CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(rtos-project-structure C)
ENABLE_LANGUAGE(ASM)
SET(CMAKE_STATIC_LIBRARY_PREFIX)
SET(CMAKE_STATIC_LIBRARY_SUFFIX)
SET(CMAKE_EXECUTABLE_LIBRARY_PREFIX)
SET(CMAKE_EXECUTABLE_LIBRARY_SUFFIX)
add_subdirectory(bsp)
bsp/CMakeLists.txt
add_subdirectory(SDK_2.5.0_FRDM-KV11Z)
bsp/SDK_2.5.0_FRDM-KV11Z/CMakeLists.txt
cmake_policy(SET CMP0076 NEW)
add_library(kv11 STATIC)
set_target_properties(kv11 PROPERTIES LINKER_LANGUAGE C)
add_subdirectory(CMSIS)
add_subdirectory(components)
add_subdirectory(devices)
From here, there are just single line CMakeLists.txt in the directories with child directories, like in bsp/CMakeLists.txt, unless they have sources in them.
bsp/SDK_2.5.0_FRDM-KV11Z/CMSIS/Drivers/DriverTemplates/CMakeLists.txt
This is how the CMakeLists.txt file in directories with source files look.
set(SOURCE_FILES
Driver_CAN.c;
Driver_ETH_MAC.c;
Driver_ETH_PHY.c;
Driver_Flash.c;
Driver_I2C.c;
Driver_MCI.c;
Driver_SAI.c;
Driver_SPI.c;
Driver_USART.c;
Driver_USBD.c;
Driver_USBH.c;
)
target_sources(kv11 PUBLIC ${SOURCE_FILES})
target_include_directories(kv11 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Running CMake
The build is invoked using the following commands:
rm -rf build
mkdir -p build
cd build
cmake -D CMAKE_BUILD_TYPE=Debug \
-D CMAKE_TOOLCHAIN_FILE="arm-gcc-toolchain.cmake" \
-D CMAKE_C_FLAGS="-fdiagnostics-color=always" \
--verbose \
..
make
which nets the following output:
-- The C compiler identification is GNU 8.2.1
-- Check for working C compiler: /home/vagrant/toolchain/bin/arm-none-eabi-gcc
-- Check for working C compiler: /home/vagrant/toolchain/bin/arm-none-eabi-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- The ASM compiler identification is GNU
-- Found assembler: /home/vagrant/toolchain/bin/arm-none-eabi-gcc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/vagrant/nxp/build
make[1]: Entering directory '/home/vagrant/nxp/build'
make[2]: Entering directory '/home/vagrant/nxp/build'
make[3]: Entering directory '/home/vagrant/nxp/build'
Scanning dependencies of target kv11
make[3]: Leaving directory '/home/vagrant/nxp/build'
make[3]: Entering directory '/home/vagrant/nxp/build'
[ 1%] Building C object bsp/SDK_2.5.0_FRDM-KV11Z/CMakeFiles/kv11.dir/CMSIS/Driver/DriverTemplates/Driver_CAN.c.obj
...
[100%] Linking C static library kv11
/home/vagrant/toolchain/bin/arm-none-eabi-ar: CMakeFiles/kv11.dir/CMSIS/Driver/DriverTemplates/Driver_CAN.c.obj: No such file or directory
make[3]: *** [bsp/SDK_2.5.0_FRDM-KV11Z/CMakeFiles/kv11.dir/build.make:895: bsp/SDK_2.5.0_FRDM-KV11Z/kv11] Error 1
make[3]: Leaving directory '/home/vagrant/nxp/build'
make[2]: *** [CMakeFiles/Makefile2:1013: bsp/SDK_2.5.0_FRDM-KV11Z/CMakeFiles/kv11.dir/all] Error 2
make[2]: Leaving directory '/home/vagrant/nxp/build'
make[1]: *** [Makefile:95: all] Error 2
make[1]: Leaving directory '/home/vagrant/nxp/build'
make: *** [Makefile:20: build2] Error 2
Taking a closer look at the compile commands
GCC call created by CMake for one of the objects: (formated)
cd /home/vagrant/nxp/build/bsp/SDK_2.5.0_FRDM-KV11Z &&
/home/vagrant/toolchain/bin/arm-none-eabi-gcc
--sysroot=/home/vagrant/toolchain/bin/../arm-none-eabi
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/CMSIS/Driver/DriverTemplates
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/CMSIS/Driver/Include
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/CMSIS/Include
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/components/lists
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/components/serial_manager
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/components/uart
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/arm
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/cmsis_drivers
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/drivers
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/template
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/utilities
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/utilities/debug_console
-I/home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/devices/MKV11Z7/utilities/str
-fdiagnostics-color=always
-fsyntax-only
-fno-common
-ffunction-sections
-fdata-sections
-ffreestanding
-fno-builtin
-mthumb
-mapcs
-mcpu=cortex-m0plus
-mfloat-abi=soft
-std=gnu99
-DCPU_MKV11Z128VLH7
-DFRDM_KV11Z
-DFREEDOM
-MMD
-MP
-DDEBUG
-g
-O0
-o CMakeFiles/kv11.dir/CMSIS/Driver/DriverTemplates/Driver_CAN.c.obj
-c /home/vagrant/nxp/bsp/SDK_2.5.0_FRDM-KV11Z/CMSIS/Driver/DriverTemplates/Driver_CAN.c
After this is run, there is no Driver_CAN.c.obj file in the CMakeFiles/kv11.dir/CMSIS/Driver/DriverTemplates directory.
What is wrong with the CMake Configuration that is causing the intermediate .obj files to not be created, which causes the library to not link properly.
I figured it out.
I turned on -fsyntax-only in the toolchain when debugging some initial files and forgot to turn it off.

Adding own library to QT creator C project

How can I use my own C library in QT creator C project? After googling I met two ways to do that:
Creating a completely new library and copy/paste your own library codes into the new library in QT creator.
The second one is done by "Add Existing Files" where you need to just adjust path and libs.
But both of them give in my case "undefined reference to ...".
I think and believe that it is because of linking problem. What is the solution for that? Any suggestions appreciated.
I could solve the same problem in Geany by altering "Set Build Command" from "gcc -Wall -o "%e" "%f" to "gcc -Wall -o "%e" "%f" "myHeader.c". Here myHeader.h is my own library.
I am coding completely on C.
You do not have a library technically speaking, you only have a set of files, a simple solution is to create a .pri where those files are added and link the .pri to .pro:
├── your_project.pro
├── main.c
├── ...
├── ...
└── mylibrary
   ├── file1.h
   ├── file1.c
   ├── file2.h
   ├── file2.c
    └── mylibrary.pri
mylibrary.pri
INCLUDEPATH += $$PWD
SOURCES += \
$$PWD/file1.c \
$$PWD/file2.c
HEADERS += \
$$PWD/file1.h \
$$PWD/file2.h
your_project.pro
# ...
include(mylibrary/mylibrary.pri)

Using cmake to cross compile multiple libraries and apps

I'm having issues with cmake while trying to cross compile an existing, large codebase. Our current build system uses a proprietary IDE that is causing a large number of issues -- so we're trying to move away.
I've distilled this to the simplest example I can.
Note: This all works when I don't use the toolchain option with cmake. I can get this to compile and run if just using the native toolchain.
I don't currently suspect issues with the toolchain file for cmake. I have used it to get OpenCV to compile -- with some hacking OpenCV.
The error that I get when trying to cross compile is:
-- The C compiler identification is GNU 4.8.3
-- The CXX compiler identification is GNU 4.8.3
-- Check for working C compiler: /opt/qnx650/host/linux/x86/usr/bin/ntox86-gcc
-- Check for working C compiler: /opt/qnx650/host/linux/x86/usr/bin/ntox86-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /opt/qnx650/host/linux/x86/usr/bin/ntox86-g++
-- Check for working CXX compiler: /opt/qnx650/host/linux/x86/usr/bin/ntox86-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/MY_USER/Desktop/test_app/build-test_app
Scanning dependencies of target liba
[ 25%] Building C object libs/liba/CMakeFiles/liba.dir/liba.c.o
Linking C shared library libliba.so
[ 25%] Built target liba
Scanning dependencies of target libb
[ 50%] Building C object libs/libb/CMakeFiles/libb.dir/libb.c.o
make[2]: *** No rule to make target `libs/liba/liba.a', needed by `libs/libb/liblibb.so.1.0.0'. Stop.
make[1]: *** [libs/libb/CMakeFiles/libb.dir/all] Error 2
make: *** [all] Error 2
It's like it's looking for a static version of the library, but I've configured it to use shared objects when calling cmake:
cmake -DBUILD_SHARED_LIBS=1 ..
I've included all of the files in the test project below. This is the same issue I run into when trying to get the larger project to compile.
Project structure:
test_app
├── app
│   ├── appa
│   │   ├── appa.c
│   │   └── CMakeLists.txt
│   └── CMakeLists.txt
├── CMakeLists.txt
├── include
│   ├── liba
│   │   └── liba.h
│   ├── libb
│   │   └── libb.h
│   └── libc
│   └── libc.h
└── libs
├── CMakeLists.txt
├── liba
│   ├── CMakeLists.txt
│   └── liba.c
├── libb
│   ├── CMakeLists.txt
│   └── libb.c
└── libc
├── CMakeLists.txt
└── libc.c
Top level CMakeLists.txt
project(test)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_INSTALL_PREFIX "/opt/test")
set(INSTALL_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_EXECUTE)
set(TEST_VERSION 1.0.0)
set(TEST_MAJOR_VERSION 1)
set(TEST_GLOBAL_INCLUDES
$<BUILD_INTERFACE:${CROSS_TARGET}/usr/pkg/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
set(TEST_GLOBAL_LIB_PATHS
$<BUILD_INTERFACE:${CROSS_TARGET}/usr/pkg/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
add_subdirectory(libs)
add_subdirectory(app)
app/CMakeLists.txt
add_subdirectory(appa)
app/appa/CMakeLists.txt
project(appa)
add_executable(appa appa.c)
target_include_directories(appa PUBLIC ${TEST_GLOBAL_INCLUDES})
target_link_libraries(appa libc)
# Set version information
set_target_properties(appa PROPERTIES
VERSION ${TEST_VERSION}
)
# set install destination and permissions
install(
TARGETS appa
RUNTIME DESTINATION lib
PERMISSIONS ${INSTALL_PERMISSIONS}
)
app/appa/appa.c
#include <stdlib.h>
#include <stdio.h>
#include <libc/libc.h>
int main(int argc, char **argv)
{
printf("Hello world: %d\n", libc_function(2));
return EXIT_SUCCESS;
}
include/liba/liba.h
#pragma once
int liba_function(int a);
include/libb/libb.h
#pragma once
int libb_function(int a);
include/libc/libc.h
#pragma once
int libc_function(int a);
libs/CMakeLists.txt
add_subdirectory(liba)
add_subdirectory(libb)
add_subdirectory(libc)
libs/liba/CMakeLists.txt
project(libliba)
add_library(liba liba.c)
target_include_directories(liba PUBLIC ${TEST_GLOBAL_INCLUDES})
# Set version information
set_target_properties(liba PROPERTIES
VERSION ${TEST_VERSION}
SOVERSION ${TEST_MAJOR_VERSION}
)
# set install destination and permissions
install(
TARGETS liba
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION lib
PERMISSIONS ${INSTALL_PERMISSIONS}
)
libs/liba/liba.c
#include <liba/liba.h>
int liba_function(int a)
{
return a * a;
}
libs/libb/CMakeLists.txt
project(liblibb)
add_library(libb libb.c)
target_include_directories(liba PUBLIC ${TEST_GLOBAL_INCLUDES})
# Set version information
set_target_properties(libb PROPERTIES
VERSION ${TEST_VERSION}
SOVERSION ${TEST_MAJOR_VERSION}
)
target_link_libraries(libb liba)
# set install destination and permissions
install(
TARGETS libb
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION lib
PERMISSIONS ${INSTALL_PERMISSIONS}
)
libs/libb/libb.c
#include <liba/liba.h>
#include <libb/libb.h>
int libb_function(int a)
{
return liba_function(a) * liba_function(a);
}
libs/libc/CMakeLists.txt
project(liblibc)
add_library(libc libc.c)
target_include_directories(libc PUBLIC ${TEST_GLOBAL_INCLUDES})
# Set version information
set_target_properties(libc PROPERTIES
VERSION ${TEST_VERSION}
SOVERSION ${TEST_MAJOR_VERSION}
)
target_link_libraries(libc libb)
# set install destination and permissions
install(
TARGETS libc
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION lib
PERMISSIONS ${INSTALL_PERMISSIONS}
)
libs/libc/libc.c
#include <libb/libb.h>
#include <libc/libc.h>
int libc_function(int a)
{
return libb_function(a) * libb_function(a);
}

How can I use the check testing framework from Clion and Cmake?

I use the check testing framework to unit test my C code. The test when it's run looks like this.
$ checkmk tst_bquotes.check > bquotes-test.c
dac#dac-Latitude-E7450:~/kth/os/lab3/openshell$ gcc -Wall -o bquotes-test util.c errors.c bquotes-test.c -lcheck -lsubunit -lrt -pthread -lm
dac#dac-Latitude-E7450:~/kth/os/lab3/openshell$ ./bquotes-test
Running suite(s): Core
100%: Checks: 1, Failures: 0, Errors: 0
How can I add this test to my cmake build script?
cmake_minimum_required(VERSION 3.0)
project(shell.test)
if (EDITLINE_LIBRARIES AND EDITLINE_INCLUDE_DIRS)
set(Editline_FIND_QUIETLY TRUE)
endif (EDITLINE_LIBRARIES AND EDITLINE_INCLUDE_DIRS)
find_path(EDITLINE_INCLUDE_DIRS NAMES editline/readline.h)
find_library(EDITLINE_LIBRARIES NAMES edit)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Editline DEFAULT_MSG
EDITLINE_LIBRARIES
EDITLINE_INCLUDE_DIRS)
mark_as_advanced(EDITLINE_INCLUDE_DIRS EDITLINE_LIBRARIES)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L/usr/local/include/ -L/usr/include -std=gnu99 -pedantic-errors -O3 -g -Wall -pedantic -ledit -ltermcap")
include_directories(/usr/local/include/ /usr/include)
link_directories(/usr/lib)
link_directories(/usr/local/lib)
add_executable(shell main.c errors.c util.c shellparser.c)
target_link_libraries(shell edit readline)
add_custom_target(shellparser DEPENDS ${CMAKE_SOURCE_DIR}/shellparser.c)
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/shellparser.c COMMAND lemon -s ${CMAKE_SOURCE_DIR}/shellparser.y DEPENDS ${CMAKE_SOURCE_DIR}/shellparser.y)
add_dependencies(shell shellparser)
My project structure looks like:
$ tree
.
├── bquotes-test
├── bquotes-test.c
├── CMakeLists.txt
├── CommandEntry.h
├── do.h
├── errors.c
├── errors.h
├── integrationtest.sh
├── lemon.c
├── lempar.c
├── LICENSE
├── main.c
├── Makefile
├── openshell.h
├── README.md
├── shellparser.c
├── shellparser.h
├── shellparser.out
├── shellparser.y
├── stresstest.sh
├── tst_bquotes.check
├── tst_exp.sh
├── types.h
├── unittest.sh
└── util.c
0 directories, 25 files
My check test is
#include "openshell.h"
#include "errors.h"
#test myQtest
fail_unless(isBetweenQuotes(11, "abc'asdqerfdsdxcvc'xc") == 1, "isBetweenQuotes function borked");
fail_unless(isBetweenQuotes(5, "This has no quotes") == 0, "isBetweenQuotes2 function borked");
fail_unless(isBetweenQuotes(11, "This' is a sentence in 'quotes") == 1, "isBetweenQuotes3 function borked");
fail_unless(isBetweenQuotes(15, "\"This is a sentence in quotes\"") == 1, "isBetweenQuotes4 function borked");
/* gcc -Wall -o bquotes-test util.c errors.c bquotes-test.c -lcheck -lsubunit -lrt -pthread -lm */
/* checkmk tst_bquotes.check > bquotes-test.c */
I would try this. Create an external linux shell script, which has the line
checkmk tst_bquotes.check > bquotes-test.c
in it and call it something like prepare_test.sh. Then, I would add these lines to your CMakeLists.txt at the end:
execute_process(
COMMAND ./prepare_test.sh
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE prep_test_result)
add_executable(bquotes-test util.c errors.c bquotes-test.c )
target_link_libraries(bquotes-test check subunit rt pthread m)
add_test(start_test_bquotes bquotes-test)
include(CTest)
After you've called cmake, you can then start the tests with make test.

Custom FindXXX module not working in CMake?

I have written a basic FindUsb CMake module to find libusb, however it does not seem to be linked correctly when used in a library.
My project structure is as follows:
MyProject
├── MyLibrary
│   ├── CMakeLists.txt
│   ├── cmake
│   │   └── Modules
│   │   └── FindLibUsb.cmake
│   ├── include
│   │   └── MyLibrary
│   │   └── library.h
│   └── src
│   └── library.c
│
└── MyProject
├── CMakeLists.txt
└── src
└── project.c
Where MyProject depends on MyLibrary and MyLibrary depends on libusb.
Here is the contents of MyLibrary/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules")
set(MyLibrary_SOURCES
src/library.c
)
set(MyLibrary_INCLUDES
include
)
# Add source to library
add_library(MyLibrary STATIC ${MyLibrary_SOURCES} ${MyLibrary_INCLUDES})
# Find libusb
find_package(LibUsb REQUIRED)
# Add dependencies
target_link_libraries(MyLibrary ${LIBUSB_LIBRARY})
# Add include directories to library
target_include_directories(MyLibrary PUBLIC ${MyLibrary_INCLUDES} ${LIBUSB_INCLUDE_DIR})
And MyLibrary/cmake/Modules/FindLibUsb.cmake:
# FindLibUsb
# ----------
# Tries to find libusb
#
# Variables:
# LIBUSB_ROOT_DIR - Set this variable to the root installation of CMocka
#
# Read-Only variables:
# LIBUSB_FOUND - system has libusb
# LIBUSB_INCLUDE_DIRS - the libusb include directories
# LIBUSB_INCLUDE_DIR - for backwards compatiblity, the same as LIBUSB_INCLUDE_DIRS
# LIBUSB_LIBRARY - libusb library location
# LIBUSB_DEFINITIONS - compiler switches required for using libusb
#
include(FindPackageHandleStandardArgs)
set(LIBUSB_ROOT_DIR ${LIBUSB_ROOT_DIR} CACHE PATH "Root installation directory of libusb")
find_path(LIBUSB_INCLUDE_DIRS
NAMES libusb.h
PATHS ${LIBUSB_ROOT_DIR}/include
PATH_SUFFIXES libusb-1.0
)
find_library(LIBUSB_LIBRARY
NAMES usb
PATHS ${LIBUSB_ROOT_DIR}/lib
)
set(LIBUSB_INCLUDE_DIR ${LIBUSB_INCLUDE_DIRS})
find_package_handle_standard_args(LibUsb DEFAULT_MSG LIBUSB_LIBRARY LIBUSB_INCLUDE_DIR)
mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARY)
I include libusb.h inside MyLibrary/include/MyLibrary/library.h:
#include <libusb.h>
void do_something(libusb_context **context);
And use it inside MyLibrary/src/library.c:
#include "MyLibrary/library.h"
void do_something(libusb_context **context) {
libusb_init(context);
// Do something
libusb_exit(*context);
}
I then include MyLibrary in MyProject from MyProject/CMakeLists.txt like so:
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
project(MyProject)
add_subdirectory(../MyLibrary ${CMAKE_BINARY_DIR}/MyLibrary)
set(MyProject_SOURCES
src/project.c
)
set(MyProject_INCLUDES
include
)
# Set include directory
include_directories(${MyProject_INCLUDES})
# Add source to executable
add_executable(MyProject ${MyProject_SOURCES})
# Add dependencies
target_link_libraries(MyProject MyLibrary)
And here is the content of MyProject/src/project.c:
#include "MyLibrary/library.h"
int main(int argc, char *argv[]) {
libusb_context *context;
do_something(&context);
}
When compiling the project from MyLibrary/build using cmake .. and make, the project is compiled successfully, however when compiling from MyProject/build I get the following error:
[ 25%] Building C object MyLibrary/CMakeFiles/MyLibrary.dir/src/library.c.o
[ 50%] Linking C static library ../lib/libMyLibrary.a
[ 50%] Built target MyLibrary
[ 75%] Building C object CMakeFiles/MyProject.dir/src/project.c.o
[100%] Linking C executable bin/MyProject
Undefined symbols for architecture x86_64:
"_libusb_exit", referenced from:
_do_something in libMyLibrary.a(library.c.o)
"_libusb_init", referenced from:
_do_something in libMyLibrary.a(library.c.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/MyProject] Error 1
make[1]: *** [CMakeFiles/MyProject.dir/all] Error 2
make: *** [all] Error 2
It looks like the actual library binary for libusb isn't being linked, which is confirmed when I run make VERBOSE=1 (after a make clean) inside MyProject/build, which has the following output:
/usr/local/Cellar/cmake/3.3.2/bin/cmake -H/Users/jack/Documents/Development/Languages/C/issue/MyProject -B/Users/jack/Documents/Development/Languages/C/issue/MyProject/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/local/Cellar/cmake/3.3.2/bin/cmake -E cmake_progress_start /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/CMakeFiles /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/CMakeFiles/progress.marks
/Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/Makefile2 all
/Applications/Xcode.app/Contents/Developer/usr/bin/make -f MyLibrary/CMakeFiles/MyLibrary.dir/build.make MyLibrary/CMakeFiles/MyLibrary.dir/depend
cd /Users/jack/Documents/Development/Languages/C/issue/MyProject/build && /usr/local/Cellar/cmake/3.3.2/bin/cmake -E cmake_depends "Unix Makefiles" /Users/jack/Documents/Development/Languages/C/issue/MyProject /Users/jack/Documents/Development/Languages/C/issue/MyLibrary /Users/jack/Documents/Development/Languages/C/issue/MyProject/build /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/MyLibrary /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/MyLibrary/CMakeFiles/MyLibrary.dir/DependInfo.cmake --color=
/Applications/Xcode.app/Contents/Developer/usr/bin/make -f MyLibrary/CMakeFiles/MyLibrary.dir/build.make MyLibrary/CMakeFiles/MyLibrary.dir/build
[ 25%] Building C object MyLibrary/CMakeFiles/MyLibrary.dir/src/library.c.o
cd /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/MyLibrary && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -I/Users/jack/Documents/Development/Languages/C/issue/MyLibrary/include -I/usr/local/include/libusb-1.0 -o CMakeFiles/MyLibrary.dir/src/library.c.o -c /Users/jack/Documents/Development/Languages/C/issue/MyLibrary/src/library.c
[ 50%] Linking C static library ../lib/libMyLibrary.a
cd /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/MyLibrary && /usr/local/Cellar/cmake/3.3.2/bin/cmake -P CMakeFiles/MyLibrary.dir/cmake_clean_target.cmake
cd /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/MyLibrary && /usr/local/Cellar/cmake/3.3.2/bin/cmake -E cmake_link_script CMakeFiles/MyLibrary.dir/link.txt --verbose=1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar cq ../lib/libMyLibrary.a CMakeFiles/MyLibrary.dir/src/library.c.o
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib ../lib/libMyLibrary.a
[ 50%] Built target MyLibrary
/Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/MyProject.dir/build.make CMakeFiles/MyProject.dir/depend
cd /Users/jack/Documents/Development/Languages/C/issue/MyProject/build && /usr/local/Cellar/cmake/3.3.2/bin/cmake -E cmake_depends "Unix Makefiles" /Users/jack/Documents/Development/Languages/C/issue/MyProject /Users/jack/Documents/Development/Languages/C/issue/MyProject /Users/jack/Documents/Development/Languages/C/issue/MyProject/build /Users/jack/Documents/Development/Languages/C/issue/MyProject/build /Users/jack/Documents/Development/Languages/C/issue/MyProject/build/CMakeFiles/MyProject.dir/DependInfo.cmake --color=
/Applications/Xcode.app/Contents/Developer/usr/bin/make -f CMakeFiles/MyProject.dir/build.make CMakeFiles/MyProject.dir/build
[ 75%] Building C object CMakeFiles/MyProject.dir/src/project.c.o
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -I/Users/jack/Documents/Development/Languages/C/issue/MyProject/include -I/Users/jack/Documents/Development/Languages/C/issue/MyLibrary/include -I/usr/local/include/libusb-1.0 -o CMakeFiles/MyProject.dir/src/project.c.o -c /Users/jack/Documents/Development/Languages/C/issue/MyProject/src/project.c
[100%] Linking C executable bin/MyProject
/usr/local/Cellar/cmake/3.3.2/bin/cmake -E cmake_link_script CMakeFiles/MyProject.dir/link.txt --verbose=1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/MyProject.dir/src/project.c.o -o bin/MyProject lib/libMyLibrary.a
Undefined symbols for architecture x86_64:
"_libusb_exit", referenced from:
_do_something in libMyLibrary.a(library.c.o)
"_libusb_init", referenced from:
_do_something in libMyLibrary.a(library.c.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/MyProject] Error 1
make[1]: *** [CMakeFiles/MyProject.dir/all] Error 2
make: *** [all] Error 2
Which lacks any references to the actual libusb library.
Printing LIBUSB_LIBRARY gives the path /usr/local/lib/libusb.dylib which is valid and exists.
Compiling MyLibrary dynamically does not help either. It looks like MyLibrary just isn't being linked to libusb at all? There's no references to it in the built libMyLibrary.a or libMyLibrary.dylib.
I guess the following line
# Add dependencies
message(MyLibrary ${LIBUSB_LIBRARY})
should be replaced with
# Add dependencies
target_link_libraries(MyLibrary ${LIBUSB_LIBRARY})

Resources