Precompile library, .a with header, included with the source? Embedded target.

OK, I wanted to get some feedback on something.

I am using CMake to build for embedded targets, and ran into something that was surprisingly challenging to do.
It is not uncommon to be given pre-compiled libraries as .a files, along with an interface in .h files.

The first results that turn up on google, I just could NOT get them to work.

For example, lets say if I have a “libvendor”, distributed as a libvendor_arm.a and vendor.h.

Like this, as it’s own CMakeLists.txt files in libs/libvendor/,
with add_subdirectory( libs/libvendor ) added to my base cmake script.

add_library(vendor STATIC IMPORTED)
set_target_properties(cbt PROPERTIES
        IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libvendor_arm.a
        INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}
)

Using that, and linking vendor to my main exe target, include paths do not seem to get added, and builds fail with:
cannot find -lvendor: No such file or directory

Eventually, I settled on this:

add_library( vendor INTERFACE )
target_sources( vendor INTERFACE vendor.h )
target_include_directories( vendor INTERFACE
        ${CMAKE_CURRENT_SOURCE_DIR} )

add_library(vendor_arm STATIC IMPORTED)
set_target_properties(vendor_arm
        PROPERTIES IMPORTED_LOCATION  ${CMAKE_CURRENT_SOURCE_DIR}/libvendor_arm.a )
target_link_libraries(vendor INTERFACE vendor_arm )

Have I … overlooked something? Is this idiomatically correct, such that I can then do:
target_link_libraries( app.elf PUBLIC vendor ), and get the header interface AND linking to the .a ?

IMPORTED libraries are scoped. By making it in a subdirectory, it is not “visible” to the parent directory. You can either:

  • use a FindVendor.cmake file and find_package(Vendor) and do this logic (my recommendation);
  • use add_library(vendor STATIC IMPORTED GLOBAL) to make the target globally visible.
1 Like

Oh. Ok. Would I use find_package even if it is something inside the repo, and not a package installed somewhere else in the system, or provided by the OS?

I am trying some versions of this, but just cannot get it to work …

# Try to find the library
find_path(VENDOR_INCLUDE_DIR NAMES vendor.h PATHS ${PROJECT_SOURCE_DIR}/libs/libvendor )
find_library(VENDOR_LIBRARY NAMES libvendor_arm.a PATHS ${PROJECT_SOURCE_DIR}/libs/libvendor )

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(VENDOR DEFAULT_MSG VENDOR_LIBRARY VENDOR_INCLUDE_DIR)

if(VENDOR_FOUND)
    set(VENDOR_LIBRARIES ${VENDOR_LIBRARY})
    set(VENDOR_INCLUDE_DIRS ${VENDOR_INCLUDE_DIR})
endif()

However, it just refuses to pick up anything for VENDOR_INCLUDE_DIR, even when using an absolute path to search …

Oh, if you vendor it, just make it GLOBAL (there’s nothing to find_* as you “know” the paths). It’s basically “your” target anyways at that point.

This contains a typo. You’ve used a different target when you define the library (vendor) and when you set properties (cbt). Does your real project have that typo too?

whoops it does
in my script, it’s all libcbt, but I was anonymizing that for the post, changing everything back to “vendor” this and that.

BUT I think that typo is not too relevant - the issue there will be that the library is not exported to the global scope, as Ben informed me of.