Buildroot build error: `ERROR: Dependency "nlohmann_json" not found, tried pkgconfig and cmake` - embedded-linux

I'm trying to add the header-only library https://github.com/nlohmann/json "JSON for Modern C++" to my Buildroot package for use both on the target embedded Linux board, as well as on my host build machine in unit tests.
However, I keep getting a variety of errors no matter what I do, including ERROR: Dependency "nlohmann_json" not found, tried pkgconfig and cmake.
What's the proper way to add this library package to be used by my package?
Details about my setup:
That JSON library is known as json-for-modern-cpp in the Buildroot source code, and is located here: https://github.com/buildroot/buildroot/tree/master/package/json-for-modern-cpp
My buildroot/package/json-for-modern-cpp/json-for-modern-cpp.mk file straight from Buildroot looks like this:
################################################################################
#
# json-for-modern-cpp
#
################################################################################
JSON_FOR_MODERN_CPP_VERSION = 3.10.5
JSON_FOR_MODERN_CPP_SOURCE = json-$(JSON_FOR_MODERN_CPP_VERSION).tar.gz
JSON_FOR_MODERN_CPP_SITE = $(call github,nlohmann,json,v$(JSON_FOR_MODERN_CPP_VERSION))
JSON_FOR_MODERN_CPP_LICENSE = MIT
JSON_FOR_MODERN_CPP_LICENSE_FILES = LICENSE.MIT
JSON_FOR_MODERN_CPP_CPE_ID_VENDOR = json-for-modern-cpp_project
JSON_FOR_MODERN_CPP_INSTALL_STAGING = YES
# header only library
JSON_FOR_MODERN_CPP_INSTALL_TARGET = NO
JSON_FOR_MODERN_CPP_CONF_OPTS = -DJSON_BuildTests=OFF -DJSON_MultipleHeaders=ON
$(eval $(cmake-package))
My package's br2-external/package/my-module-name/my-module-name.mk makefile has these dependencies in it (notice I added json-for-modern-cpp to both the MY_MODULE_NAME_DEPENDENCIES target board dependencies, as well as to the HOST_MY_MODULE_NAME_DEPENDENCIES host build system dependencies:
MY_MODULE_NAME_DEPENDENCIES += \
some-other-lib1 \
some-other-lib2 \
json-for-modern-cpp
HOST_MY_MODULE_NAME_DEPENDENCIES += \
host-some-other-lib1 \
host-some-other-lib2 \
json-for-modern-cpp

How to add a Buildroot package to your package, and to your meson.build file
I figured it out. I've documented what I learned in my eRCaGuy_dotfiles/git & Linux cmds, help, tips & tricks - Gabriel.txt notes file. Search that document for "Adding dependencies from other meson.build files" and for "nlohmann" for details and notes.
Here's the solution:
If you need to add a dependency on a 3rd-party library or something, such as JSON for Modern C++ (https://github.com/nlohmann/json), simply do this:
Find that project's meson.build file. Ex: https://github.com/nlohmann/json/blob/develop/meson.build
Notice the 'nlohmann_json' project name at the top. Use that in your module's meson.build file:
all_deps = [
# NB: this `nlohmann_json` meson.build name can be identified from its public repo's
# meson.build file here: https://github.com/nlohmann/json/blob/develop/meson.build#L1
dependency('nlohmann_json')
dependency('some_other_lib2'),
dependency('some_other_lib3'),
dependency('some_other_lib4'),
]
all_deps will then be used in your meson.build file to build your executable and specify your library dependencies--something like this, for example:
my_lib = static_library(
'my_module_name',
include_directories : [include_dirs],
sources: [library_sources],
dependencies: all_deps,
cpp_args: compiler_options,
install: true,
)
Add $(eval $(host-cmake-package)) to the bottom of the json-for-modern-cpp.mk file in Buildroot here: https://github.com/buildroot/buildroot/blob/master/package/json-for-modern-cpp/json-for-modern-cpp.mk. This requires adding your own commit to your own custom fork of that repo.
That changes that file from this:
################################################################################
#
# json-for-modern-cpp
#
################################################################################
JSON_FOR_MODERN_CPP_VERSION = 3.10.5
JSON_FOR_MODERN_CPP_SOURCE = json-$(JSON_FOR_MODERN_CPP_VERSION).tar.gz
JSON_FOR_MODERN_CPP_SITE = $(call github,nlohmann,json,v$(JSON_FOR_MODERN_CPP_VERSION))
JSON_FOR_MODERN_CPP_LICENSE = MIT
JSON_FOR_MODERN_CPP_LICENSE_FILES = LICENSE.MIT
JSON_FOR_MODERN_CPP_CPE_ID_VENDOR = json-for-modern-cpp_project
JSON_FOR_MODERN_CPP_INSTALL_STAGING = YES
# header only library
JSON_FOR_MODERN_CPP_INSTALL_TARGET = NO
JSON_FOR_MODERN_CPP_CONF_OPTS = -DJSON_BuildTests=OFF -DJSON_MultipleHeaders=ON
$(eval $(cmake-package))
to this:
################################################################################
#
# json-for-modern-cpp
#
################################################################################
JSON_FOR_MODERN_CPP_VERSION = 3.10.5
JSON_FOR_MODERN_CPP_SOURCE = json-$(JSON_FOR_MODERN_CPP_VERSION).tar.gz
JSON_FOR_MODERN_CPP_SITE = $(call github,nlohmann,json,v$(JSON_FOR_MODERN_CPP_VERSION))
JSON_FOR_MODERN_CPP_LICENSE = MIT
JSON_FOR_MODERN_CPP_LICENSE_FILES = LICENSE.MIT
JSON_FOR_MODERN_CPP_CPE_ID_VENDOR = json-for-modern-cpp_project
JSON_FOR_MODERN_CPP_INSTALL_STAGING = YES
# header only library
JSON_FOR_MODERN_CPP_INSTALL_TARGET = NO
JSON_FOR_MODERN_CPP_CONF_OPTS = -DJSON_BuildTests=OFF -DJSON_MultipleHeaders=ON
$(eval $(cmake-package))
$(eval $(host-cmake-package))
Again, the only line added was the last one above. That causes cmake to bring in the necessary dependencies for the host build computer too, for your unit tests, and not just for the target embedded Linux board, which is what the $(eval $(cmake-package)) line does.
Use the proper dependencies in your module's Buildroot br2-external/package/my-module-name/my-module-name.mk makefile. For the host it should be host-json-for-modern-cpp, not json-for-modern-cpp, since the latter is for the target board only. Note that the json-for-modern-cpp name seems to be defined in the config file here, by the way: https://github.com/buildroot/buildroot/blob/master/package/json-for-modern-cpp/Config.in#L2.
MY_MODULE_NAME_DEPENDENCIES += \
some-other-lib1 \
some-other-lib2 \
json-for-modern-cpp
HOST_MY_MODULE_NAME_DEPENDENCIES += \
host-some-other-lib1 \
host-some-other-lib2 \
host-json-for-modern-cpp
# This last line just above is now **fixed**!
That's it! It works now.
I'm not sure why I had to add $(eval $(host-cmake-package)) to the buildroot file, as it seems like that should be something in the json-for-modern-cpp.mk file in the Buildroot repo by default, no?

Related

How to issue a new compile command in Makefile.am?

I am building a library (using Autotools) that looks like the following. The building of the library works fine when I add a *.cpp file to libmytest_la_SOURCES.
lib_LTLIBRARIES = libmytest.la
libmytest_la_SOURCES = test.capnp.c++
libmytest_la_CXXFLAGS = -I/usr/include -I$(top_srcdir)/src/includes
libmytest_la_LDFLAGS = -version-info 0:0:0 -L/usr/lib64
libmytest_la_LIBADD = -lcapnp
The problem is that I need to call a third-party compiler to generate code before doing the normal compile process. The following capnp tool will generate a c++ output file named test.capnp.c++.
capnp compile -oc++ test.capnp
And if I plug the output of that (test.capnp.c++) into the makefile above, my library is built. What I don't get is how to invoke that command into the Makefile.am to generate the needed source file and plug it into the libmytest_la_SOURCES variable.
Any thoughts?
Automake does not have direct support for capnp, and adding support for a new language or tool would involve hacking the program. But you can provide ordinary make rules in your Makefile.am file, and these will be carried through to the final generated Makefile. This is Automake's primary extension point.
Thus, you might add this to your Makefile:
test.capnp.c++ : test.capnp
capnp compile -oc++ $<
# or
# $(CAPNP) compile -oc++ $<
# where $(CAPNP) is the capnp binary as discovered by configure
You would want to also designate test.capnp as an additional file to distribute:
EXTRA_DIST = test.capnp
You should also consider whether you want the .c++ file to be included in distribution packages, to relieve the build-time dependency on capnp. If not, then instead of listing it in libmytest_la_SOURCES you should list it in nodist_libmytest_la_SOURCES, plus also in CLEANFILES:
#
# test.capnp.c++ is a built file that we choose not to distribute
#
nodist_libmytest_la_SOURCES = test.capnp.c++
CLEANFILES = test.capnp.c++
# or: CLEANFILES = $(nodist_libmytest_la_SOURCES)

Unable to add a simple kernel-module to Yocto image

Goal
I want to add a touchscreen driver available in the linux kernel source tree to my Yocto image (The link takes you to goodix.c). I basically need to add it as a kernel module.
Solution
I follow the Incorporating Out-of-Tree Modules section of the Yocto Mega Manual. I base mine off their example kernel-module recipe, called hello-mod.
In recipe goodix-9271_0.1.bb: RPROVIDES_${PN} = "kernel-module-goodix"
In layer.conf: MACHINE_EXTRA_RDEPENDS += "kernel-module-goodix"
Problem
My build simply consistently fails in do_rootfs with:
Error:
Problem: package packagegroup-base-1.0-r83.imx6ul_var_dart requires packagegroup-machine-base, but none of the providers can be installed
- package packagegroup-base-extended-1.0-r83.imx6ul_var_dart requires packagegroup-base, but none of the providers can be installed
- package packagegroup-machine-base-1.0-r83.imx6ul_var_dart requires kernel-module-goodix, but none of the providers can be installed
- conflicting requests
- nothing provides kernel-module-goodix-5.4.3-imx6ul+gb40ccfdb73ea needed by goodix-9271-0.1-r0.imx6ul_var_dart
(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)
What I cannot understand after reading the documentation on this section is why this error is happening. I tried adjusting the recipe names (removing the kernel-module prefix, etc) but nothing seems to work. What is going wrong?
Source
inherit module logging
# Driver for Goodix touchscreens
SUMMARY = "Generic driver for Goodix touchscreens"
DESCRIPTION = "Support for Goodix 1151, 5663, 5688, 917S, 9286, 911, 9271, 9110, 927, 928, 912, 9147, and 967 touchscreens"
# License
LICENSE = "GPL-2.0"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
# Compatibility
COMPATIBLE_MACHINE = "(imx)"
# Package name
RPROVIDES_${PN} = "kernel-module-goodix"
# Source
S = "${WORKDIR}"
SRC_URI = "file://Makefile \
file://goodix.c"
# Functions
do_install() {
bbwarn "Installing Goodix kernel module ..."
bbwarn "KERNEL_SRC = ${KERNEL_SRC}"
bbwarn "KERNEL_VERSION = ${KERNEL_VERSION}"
bbwarn "WORKDIR = ${WORKDIR}"
cd ${S}
xz goodix.ko
install --verbose -d ${D}/lib/modules/${KERNEL_VERSION}/kernel/drivers/input/touchscreen
install --verbose -m 0644 goodix.ko.xz ${D}/lib/modules/${KERNEL_VERSION}/kernel/drivers/input/touchscreen
}
# Reference included files
FILES_${PN} = "/lib/modules/${KERNEL_VERSION}/kernel/drivers/input/touchscreen/*"
Edits
The error basically says that kernel-module-goodix-5.3.4-imx6ul+gb40ccfdb73ea isn't provided. I didn't name my package that way though. So why is it looking for something with the suffix 5.3.4-imx6ul+gb40ccfdb73ea there?
Edit (Solution)
For anyone reading this who isn't satisfied by the accepted answer. Just know that what was wrong with my original recipe was that I wasn't naming my actual recipe "kernel-module-<name>.bb". That was actually what was required.
I have created a recipe for an UART Bluetooth driver before and it works fine for me, here is the recipe:
#
# FNLINK BLUETOOTH 8822 KERNEL DRIVER
#
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""
SRC_URI = "file://uart_bt.zip"
S = "${WORKDIR}/bluetooth_uart_driver"
inherit module
EXTRA_OEMAKE_append_task-install = " -C ${STAGING_KERNEL_DIR} M=${S}"
EXTRA_OEMAKE += "KDIR=${STAGING_KERNEL_DIR}"
Change S to "bluetooth_uart_driver" because the zip file contains that directory with the content:
ifneq ($(KERNELRELEASE),)
obj-m := hci_uart.o
hci_uart-y := hci_ldisc.o hci_h4.o hci_rtk_h5.o rtk_coex.o
#EXTRA_CFLAGS += -DDEBUG
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order *.a
endif
This works well for me, and the .ko file is generated and shipped into /lib/modules/${KVER}/extra , so you can override the do_install function and install it where you want.
Simple Test:
I downloaded the goodix.c driver and created a custom recipe for it with this Makefile (I modified my old BT Makefile):
ifneq ($(KERNELRELEASE),)
obj-m := goodix.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order *.a
endif
My recipe:
|meta-test/
|--> recipes-driver/
|--> files/
|--> goodix.c
|--> Makefile
|--> goodix-driver_0.1.bb
goodix-driver_0.1.bb:
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""
SRC_URI = "file://goodix.c file://Makefile"
S = "${WORKDIR}"
inherit module
EXTRA_OEMAKE_append_task-install = " -C ${STAGING_KERNEL_DIR} M=${S}"
EXTRA_OEMAKE += "KDIR=${STAGING_KERNEL_DIR}"
With this in a simple poky build I was able to generate the .ko file.
Note:
If goodix.c is present in your upstream Linux kernel, which means you can find it in:
tmp/work/.../linux-../../git/drivers/input/touchscreen/goodix.c
which means you can just patch it without creating a whole recipe for it, you just edit it directly and then go back to git folder and :
git add drivers/input/touchscreen/goodix.c
git commit -m "My-updates"
git format-patch -1 -o /path/to/meta-custom/recipes-kernel/linux/files
Now, in /path/to/meta-custom/recipes-kernel/linux/linux-xx_%.bbappend add:
SRC_URI_append = " file://My-updates.patch"
Now, do not forget to activate it via menuconfig and add its flag to the kernel defconfig file so it gets compiled and shipped within the rootfs.
I am updating my question with an answer which also describes how to create a simple kernel module bitbake file.
File name and placement
Name of my kernel module: foo
Name of my bitbake file: kernel-module-foo (kernel-module- prefix required)
Topology (see below):
├── conf
│ └── layer.conf
├── COPYING.MIT
├── README
├── recipes-kernel
│ └── linux
│ ├── kernel-module-foo.bb
Bitbake file
# Bitbake class(es)
inherit module
# Dependencies
DEPENDS = "virtual/kernel"
# Metadata
SUMMARY = "Sample kernel module"
# Licensing
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5= 0835ade698e0bcf8506ecda2f7b4f302"
# Source
GIT_BRANCH = "my-branch"
SRC_URI = "git://<path-to-git-repo>;branch=${GIT_BRANCH}"
SRCREV = "<my-git-branch-src-revision>"
# OE build directives
EXTRA_OEMAKE_append_task-install = "-C ${STAGING_KERNEL_DIR} M=${S}"
EXTRA_OEMAKE += "KDIR=${STAGING_KERNEL_DIR}"
# Autoinstall (optionally disable)
KERNEL_MODULE_AUTOLOAD += "pwr_ctl_onoff"

How do I write the makefile for a kernel module in OpenWrt to load the module automatically

I'm writing a simple kernel module for Openwrt. I have working code that loads and does what it needs to do. What I am missing is how to get the code into the build process of Openwrt. I have a Makefile as below:
# Copyright (C) 2006-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
# name
PKG_NAME:=HelloWorld
# version of what we are downloading
PKG_VERSION:=1.0
# version of this makefile
PKG_RELEASE:=0
PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME)
PKG_CHECK_FORMAT_SECURITY:=0
include $(INCLUDE_DIR)/package.mk
define KernelPackage/$(PKG_NAME)
SUBMENU:=Other modules
TITLE:=helloworld lkm
FILES:= $(PKG_BUILD_DIR)/hello.ko
endef
define KernelPackage/$(PKG_NAME)/description
A sample kernel module.
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
SUBDIRS="$(PKG_BUILD_DIR)"
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,$(PKG_NAME)))
I'm trying to follow the instructions here: https://wiki.openwrt.org/doc/devel/packages#creating_packages_for_kernel_modules
Right now, I can see the module in the make menuconfig and select it. However when I run the build in QEMU I don't see the module. I can actually copy the *.ko module over and load it and that works. I just want the module to load automatically. How can I do that?
In Build/Compile, you need a line to include your module.
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
CONFIG_<your mod>=m \ # THIS LINE IS MISSING
modules
endef
This are some good examples in the tree. Check out exfat-nofuse.

How to statically build kernel module with buildroot?

Is there an example package somewhere on how you might go about statically compiling in a device driver?
I know that obj-y is used for static compilation vs obj-m. I have a dynamically loadable module being built in my buildroot package right now. That dynamic module works exactly as I would expect. I even figured out that I could change the module makefile to use obj-y, and add a buildroot option where, if I clicked it it would append a line in the drivers/Makefile. The output appeared to show that my module got built. But it didn't at all seem to me that my driver's init function was being executed at startup, because I don't see my device file in /dev.
Supposing you have a driver in driver.c, and a buildroot package called STATICDRVR, you can use the following Config.in and STATICDRVR.mk files to add a static module to be built when the kernel is built:
Config.in
config BR2_PACKAGE_STATICDRVR
bool "Build & link static driver?"
help
This is a driver that blah blah greatness whatever
STATICDRVR.mk
STATICDRVR_VERSION = master
STATICDRVR_SITE = /location/to/STATICDRVR_containing_src
STATICDRVR_SITE_METHOD = local
STATICDRVR_MODULE_SUBDIRS = src
STATICDRVR_INSTALL_TARGET = YES
STATICDRVR_LICENSE = GPLv2
STATICDRVR_LICENSE_FILES = COPYING
STATICDRVR_NAME = STATICDRVR
STATICDRVR_DEPENDENCIES = linux
define STATICDRVR_BUILD_CMDS
#make sure that obj-y += STATICDRVR/ is only in the build makefile once
sed -i '/obj-y += STATICDRVR/d' $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/Makefile
echo "obj-y += STATICDRVR/" >> $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/Makefile
rm -rf $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/STATICDRVR
cp -r $(#D)/src $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/STATICDRVR
echo "obj-y += driver.o" > $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/STATICDRVR/Makefile
endef
define STATICDRVR_INSTALL_STAGING_CMDS
endef
define STATICDRVR_INSTALL_TARGET_CMDS
endef
endif
define STATICDRVR_DEVICES
endef
define STATICDRVR_PERMISSIONS
endef
define STATICDRVR_USERS
endef
$(eval $(kernel-module))
$(eval $(generic-package))
It is not possible to statically link an external module with the kernel. To do that, you have to patch the kernel itself and add your module there.

Yocto recipe : how to install in specific folder

I have created a Yocto recipe for my program.
What are the default folders that are building image from recipe ?
At the time of building image, I want to move my files to another folder like "/opt/xyz".
Should I simply do "mv" or is there any other options?
I guess you want to copy your compiled program to a folder such as ${bindir}:
Quote from Yocto ref-manual 1.1:
When specifying paths as part of the CONFFILES variable, it is good practice to use appropriate path variables. For example, ${sysconfdir} rather than /etc or ${bindir} rather than /usr/bin. You can find a list of these variables at the top of the meta/conf/bitbake.conf file in the Source Directory.
You can copy files from your working directory to any directory in the target filesystem. See the hello-world example for instance (note that the example is taken from the 1.1 reference manual, but I haven't found it yet in the newer version):
DESCRIPTION = "Simple helloworld application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
PR = "r0"
SRC_URI = "file://helloworld.c"
S = "${WORKDIR}"
do_compile() {
${CC} helloworld.c -o helloworld
}
do_install() {
install -d ${D}${bindir}
install -m 0755 helloworld ${D}${bindir}
}
FILES_${PN} = "${bindir}"
In this example, the helloworld binary would be copied to /usr/bin on your image (could be /opt too, see Source Directory for the variable definition).
Adjust FILES_${PN} var for ${sysconfdir} ${bindir} ${datadir} ${libdir} directories.
do_install(){
install -d ${D}{base_prefix}/opt/xyz/
install -m ${WORKDIR}/yourbinary ${D}${base_prefix}/opt/xyz/
}
FILES_${PN} = "${base_prefix}/opt/*"
above
1st line creates the dir in imagedir in that opt/xvz/
2nd line copy your binary to opt/xyz/dir
3rd line use to copy of your opt/xyz/binary to yocto rootfs.

Resources