Compiling ardupilot to allow linking as an imported static library

Hi
I’ve spent a bit too much time trying to solve this issue so any help is appreciated.

Goal:
I have a standalone project where I wish to include the AR_AttitudeControl library.

Attempt:
Use CMakeLists
Compile ardupilot Rover using the sitl board and with the -fPIC flag.
Set macros needed to include AR_AttitudeControl.h
Add ardupilot as a static library and wrap malloc
Link ardupilot to the target

Problem:
The compiled target contains and undefined symbol “_ZN11RC_Channels8var_infoE”.
This should be defined in “libRover_libs.a”, but it is not.

The symbol is defined in ardupilots generated executable “ardurover”.
The symbol is also defined in “ardupilot/build/sitl/Rover/RC_Channel.cpp.0.o”.

I also tried linking to the object containing the definition, which resulted in a new missing definition and so forth until the linker complained about multiple definitions. Forgoing the library and just linking to object files yielded similar results.

Questions:
I assume the underlying issue is that I’m compiling ardupilot incorrectly. I’m also under the impression that this should be quite simple, but am somewhat illiterate when it comes to waf.

How should I go about compiling ardupilot so that the generated library contains all the symbols?
Or do I have the wrong approach altogether?

Hello,

Ryan here from the Ardupilot Dev Team. I asked about a similar thing in the dev call this week, but for AC_Fence.
It is similar to this previous thread:

Unfortunately, the way that ArduPilot is designed is not suitable or intended to be linked to from an external system. Ardupilot’s libraries have a public header, but are not made to be called from another program outside of ArduPilot. Letting external libraries call AP library functions would be a bad idea because ArduPilot does not currently care about ABI in its classes. If it had an ABI stability guarantee for all the libraries, it would make changing things extremely slow and difficult because tons of classes expose a lot as public that should be private if it were a maintainable API.

Here’s the static libraries that Ardilot generates when you compile:

$ find build -name *.a
build/sitl/lib/libArduPlane_libs.a
build/sitl/lib/libap.a
build/sitl/lib/gtest/libgtest.a

I didn’t see any shared libraries show up.

I tried re-writing the entire build system in CMake (skip waf entirely) to generate targets for each library, starting with AP_HAL and AP_Common and working my way up through the dependency tree until AC_Fence. This effort proved extremely difficult for a number of reasons:

  • There are circular dependencies in the libraries
  • GCS_Mavlink is used everywhere for logging, but that has many dependencies so you need to build up many unrelated libraries such as the EKF even if you just want to build AC_Fence
  • ArduPilot doesn’t use CMake from it’s mavlink fork and hasn’t maintained the CMakeLists. Upstream fixes to CMake build of mavlink and ability to link to a mavlink target are not possible, and the way that mavgen.py works within waf cannot easily be used in CMake without modifications. There’s also a different call in wscript to _build_dynamic_sources that generates the v1.0 all.xml into the v2.0 directory. I never figured out how these two methods relate.
  • ArduPilot has many custom definitions that must be added that do not have defaults in the code. Examples include CONFIG_HAL_BOARD, CONFIG_HAL_BOARD_SUBTYPE
  • You can disable loggign with HAL_LOGGING_ENABLED, and then must enable heap with ENABLE_HEAP. The list goes on.
  • You need to define built in macros such as __AP_LINE__ being equal to __LINE__ for some reason
  • The ardupilot devs are unwilling to make modifications to the source code to make it usable in outside systems because of the risk of breaking it, the code churn, and other reasons.
  • No one else on the dev team seems invested in figuring out how to do this
  • CMake was already decided to not be supported, and effort was put in for cmake version 2 but it was decided not to be used instead of waf because all the variable usage. In CMake 3, variables have largely gone away, so that problem is largely mitigated, however it would be a huge effort for the dev team if they wanted to maintain two parallel build tools

That said, I went down this path for a good reason. If you are interested to keep trying, I invite you to continue on the path I went down on my branch here:

If you do like this approach, and can figure out how to generate the mavlink headers, please share. My current approach was to run waf first to generate the headers, and then have CMake just re-use the same build tree and not have to generate the mavlink headers. There’s better ways to do it, and the dev team is open to cherry-picking upstream mavlink’s fixes to CMake.