Scons: run a make command as a dependency for a target - makefile

I have a library that needs to by built as a dependency for my target. The library is distributed with a Makefile and there's nothing special needed to build it other than to run:
make my_target
How would I run this command as part of my SConstruct file if my file looks something like:
env = Environment()
flags = env.ParseFlags( CCFLAGS + LDFLAGS )
env.MergeFlags( flags )
env.Program( target = 'my_prog', source = SRC )

Create a Command builder with the name of the library as the target:
env.Command("other/lib/libother.a", "", "cd other && make my_target")
Be sure to add this library to your Program line:
env.Program(target="my_prog", source=SRC, LIBS=["other/lib/libother.a"])

Related

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

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?

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)

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.

Link static library against another static library

I am trying to link a static library [1] into another static library [2] with scons.
Unfortunately the emitted call to "ar" never contains any path to library [1].
According to this post How to merge two "ar" static libraries into one it should be possible to merge to archives into one.
Is the usual call to CheckLibWithHeader not sufficient here?
Best regards
Have you tried specifying the complete path to library [1] when referring to it with the SCons ar command?
Brady
Adding more info to my original answer:
Since you havent posted your SCons scripts, I'll assume its something like the one I present below:
Normally, the LIBPATH construction variable is used to specify paths to libraries, but that appears to only work with the Program() builder and is not used with the ar command. What needs to be done then is to specify the complete path for the library in question. Assuming I have the following directory structure:
# tree .
.
|-- SConstruct
|-- fileA.cc
|-- fileA.o
|-- libB
| `-- libmoduleB.a
|-- libmoduleA.a
`-- libmoduleC.a
Here is the SConscript that shows how to do so:
env = Environment()
env.Library(target = 'moduleA', source = 'fileA.cc')
env.Library(target = 'moduleC', source = ['libmoduleA.a', '#libB/libmoduleB.a'])
Or Instead of the relative dir '#libB', you could specify an absolute path. (the '#' in a path means its relative to the SConscript)
And, to make it portable, you should specify the moduleB library (and moduleA) like this:
libBname = "%smoduleB%s" % (env['LIBPREFIX'], env['LIBSUFFIX'])
libB = os.path.join(pathToLibB, libBname)
Here is the result:
# scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o fileA.o -c fileA.cc
ar rc libmoduleA.a fileA.o
ranlib libmoduleA.a
ar rc libmoduleC.a libmoduleA.a libB/libmoduleB.a
ranlib libmoduleC.a
scons: done building targets.
You'd need to create a builder which runs the commands in the other SO question you've linked to.
ar -x libabc.a
ar -x libxyz.a
ar -c libaz.a *.o
Though you might need a scanner to find the files contained in each static library (ar t libabc.a) and then use the output from that as the input to a normal static library builder.
ofiles = env.unArchive('libabc.a')
ofiles.extend(env.unArchive('libxyz.a'))
env.StaticLibrary('az',ofiles)
Something like the above should work.

automake for creating a lib and program using it

I'm trying to develop a program that uses another internal library done in the same project.
I want to link both. The lib is stored and succesfully compiled under ./lib/mylib and a mylib.a is created. The issue is that I need to include ./lib/mylib directory in the INCLUDE search and also link the program against the library.
Are there any automatically defined variables or do I have to do it by my own like in the Makefile.am below?
SUBDIRS = lib .
# set the include path found by configure
INCLUDES = $(all_includes) -Ilib/mylib
bin_PROGRAMS = myprogram
myprogram_SOURCES = main.c
myprogram_CPPFLAGS = $(libmylib_CFLAGS) $(AM_CFLAGS) $(CFLAGS)
nfc_network_config_LDADD =$(LIB_MYLIB)
Your Makefile could look something like this.
SUBDIRS = lib .
bin_PROGRAMS = myprogram
myprogram_SOURCES = main.c
myprogram_CPPFLAGS = -Ilib/mylib $(AM_CPPFLAGS)
myprogram_LDADD = lib/mylib/mylib.a
Note that *_CPPFLAGS should usually not be mixed with *_CFLAGS, and that the $(CFLAGS) and $(CPPFLAGS) variables are always used (they are user variables) so you should not have to mention them. Also INCLUDES is an obsolete variable (you should use *_CPPFLAGS instead), and automake will warn about it if you run it with the -Wall option.

Resources