Custom FindXXX module not working in CMake? - c

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})

Related

Makefile handling auto-generated source and Makefile

I have a problem with a Makefile that's using an auto-generated Makefile to compile .o files from auto-generated .c and .s (assembler) source files.
The context is embedded programming for STM32 microcontrollers, where a Makefile and hardware initialization source code is generated from a hardware configuration file (.ioc file).
Suppose, we have the following directory structure:
.
├── archive
│   ├── a
│   │   └── a.c
│   ├── b
│   │   └── b.s
│   └── Makefile
├── Makefile
├── source
│   └── generate.sh
The files archive/a/a.c and archive/b/b.s may be empty here. My main ./Makefile looks like this:
.PHONY: default all clean generate objects
BUILD_DIR := build
SOURCE_DIR := source
OBJECTS := build/source/a/a.o build/source/b/b.o
default: all
all: objects
objects: $(OBJECTS)
$(BUILD_DIR)/%.o: %.c $(SOURCE_DIR)/Makefile
mkdir -p $(shell dirname $#)
$(MAKE) -C $(SOURCE_DIR) BUILD_DIR=../$(shell dirname $#) VPATH=../$(shell dirname $<) ../$#
$(BUILD_DIR)/%.o: %.s $(SOURCE_DIR)/Makefile
mkdir -p $(shell dirname $#)
$(MAKE) -C $(SOURCE_DIR) BUILD_DIR=../$(shell dirname $#) VPATH=../$(shell dirname $<) ../$#
$(SOURCE_DIR)/%.c :: generate
$(SOURCE_DIR)/%.s :: generate
$(SOURCE_DIR)/Makefile :: generate
generate: $(SOURCE_DIR)/generate.sh
cd $(SOURCE_DIR) ; bash generate.sh
clean:
$(RM) -r build $(SOURCE_DIR)/Makefile $(SOURCE_DIR)/a $(SOURCE_DIR)/b
The source/generate.sh looks like this:
cp -r ../archive/. ./
In reality, this process much more complicated and takes a few minutes.
Finally, the archive/Makefile looks like this:
BUILD_DIR := build
$(BUILD_DIR)/%.o: %.c
cp $^ $#
$(BUILD_DIR)/%.o: %.s
cp $^ $#
In reality, we use gcc instead of a simple cp to compile the C and ASM source files.
The auto-generated Makefile uses VPATH to find the required input file and is actually meant to compile everything in a flat build folder (which I don't want).
I have no control over the contents of the auto-generated Makefile! The source/generate.sh should be treated as a black-box that I have no influence on.
Therefore, the superordinate Makefile has to slightly abuse the auto-generated Makefile by overwriting the BUILD_DIR and VPATH accordingly.
Now, if I first run make generate and then, e.g. make build/source/a/a.o, everything works as planned:
First, the source directory is populated with the auto-generated stuff, and then build/source/a/a.o is "compiled" from source/a/a.c.
However, if I don't run make generate first, I get:
$ make build/source/a/a.o
make: *** No rule to make target 'build/source/a/a.o'. Stop.
My assumption was that the recipes would have been resolved in the following manner:
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c $(SOURCE_DIR)/Makefile
-> $(SOURCE_DIR)/%.c: generate
-> generate: $(SOURCE_DIR)/generate.sh
-> $(SOURCE_DIR)/Makefile: generate
-> generate: $(SOURCE_DIR)/generate.sh
After a bit of trial and error, I figured that I could somewhat fix this behaviour by defining the recipes as follows:
$(SOURCE_DIR)/%.c: generate
true
$(SOURCE_DIR)/%.s: generate
true
$(SOURCE_DIR)/Makefile: generate
true
This yields:
$ make build/source/a/a.o
cd source ; bash generate.sh
true
true
mkdir -p build/source/a
make -C source BUILD_DIR=../build/source/a VPATH=../source/a ../build/source/a/a.o
make[1]: Entering directory '/[...]/source'
cp ../source/a/a.c ../build/source/a/a.o
make[1]: Leaving directory '/[...]/source'
rm source/a/a.c
However, this leads to the generate step being run every time I run make build/source/a/a.o. Since the generate step is expensive in real life, this is not an option.
Also, source/a/a.c is treated as a temporary file that is meant to be deleted which I don't want.
How can I design my superordinate ./Makefile so that the generate step is being run automatically, but only if necessary?
I also need to be able to build non-auto-generated source files from the root directory.
A few more strange make behaviours:
Simply running make (i.e. make objects) with this Makefile yields
cd source ; bash generate.sh
true
true
mkdir -p build/source/a
make -C source BUILD_DIR=../build/source/a VPATH=../source/a ../build/source/a/a.o
make[1]: Entering directory '/[...]/source'
cp ../source/a/a.c ../build/source/a/a.o
make[1]: Leaving directory '/[...]/source'
true
mkdir -p build/source/b
make -C source BUILD_DIR=../build/source/b VPATH=../source/b ../build/source/b/b.o
make[1]: Entering directory '/[...]/source'
cp ../source/b/b.s ../build/source/b/b.o
make[1]: Leaving directory '/[...]/source'
rm source/a/a.c
Why does make remove source/a/a.c, but not source/b/b.c? For the record: I want neither of these files to be auto-removed.
An issue that I am sadly unable to reproduce with my example is that make basically outputs the following line at the start (adpated to the example):
make: Circular source/generate.sh.c <- generate dependency dropped.
Changing the recipe to the following
$(SOURCE_DIR)/%.c: generate
true
$(SOURCE_DIR)/%.s: generate
true
$(SOURCE_DIR)/Makefile: generate
yields:
$ make build/source/a/a.o
cd source ; bash generate.sh
true
cc -c -o source/Makefile.o source/Makefile.c
cc1: fatal error: source/Makefile.c: No such file or directory
compilation terminated.
make: *** [<builtin>: source/Makefile.o] Error 1
Why does make think that it needs to build a Makefile.o here?

No rule to make target in GCC Makefile

This is the following Makefile at issue:
#
# Compiler flags
#
CC = gcc
CFLAGS = -Wall -Werror -Wextra
#
# Project files
# example -
# SRCS = hash_table.c linked_list.c utils.c common.c business_logic.c user_interface.c
# SRCS = test/gc_test.c src/gc.c
SRCS := $(shell find . -name "*")
OBJS = $(SRCS:.c=.o)
EXE = output.out
FILENAMES := $(shell find . -type f -name "*.c" -printf "%f\n")
FILENAMES_OUT = $(FILENAMES:.c=.o)
#
# Default build settings
#
BUILDDIR = build
BUILDCFLAGS = -g -O0
BUILDEXE = $(BUILDDIR)/$(EXE)
BUILDOBJS = $(addprefix $(BUILDDIR)/, $(FILENAMES_OUT))
# Rules for default build
all: clean build
build: $(BUILDEXE)
$(BUILDEXE): $(BUILDOBJS)
$(CC) $(CFLAGS) $(BUILDCFLAGS) -o $(BUILDEXE) $^ -lcunit
$(BUILDDIR)/%.o: %.c
$(CC) $(CFLAGS) $(BUILDCFLAGS) -c $< -o $#
memtest: $(BUILDEXE)
valgrind --leak-check=full ./$<
#
# Other rules
#
clean:
mkdir -p $(BUILDDIR)
PROBLEM -> make: *** No rule to make target `build/gc_test.o', needed by `build/output.out'. Stop.
PROJECT TREE
.
├── build
├── doc
│   └── design.md
├── Makefile
├── proj
│   ├── code_quality_report.md
│   ├── deviations.md
│   ├── individual_reflection.md
│   ├── team_reflection.md
│   └── test_report.md
├── README.md
├── src
│   ├── gc.c
│   └── headers
│   └── gc.h
└── tests
└── gc_test.c
The issue itself happens in $(BUILDEXE): $(BUILDOBJS) where the dependencies are gc_test.c gc.c. Those dependencies SHOULD get caught in the function below it, because it's input is all the .c files in the build directory. Those files SHOULD get properly matched and then compiled to .o files which then should climb up the tree and produce an executable. I'm confused because $(BUILDOBJS) should be the same as $(BUILDDIR)/%.o.
I'm new to making Makefiles, but I want to get better at it. Please point out better naming conventions or terminology that could have been used better for this post. Thanks!
The problem is here:
FILENAMES := $(shell find . -type f -name "*.c" -printf "%f\n")
This is wrong because -printf "%f\n" prints only the filenames, without any path. You're losing all information about the path where files are found, so how can make find them?
You should change this to simply -print then it will work.

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.

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);
}

Linking to a local C library for Rust with FFI

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.

Resources