What's the best way to add thirdparty lib with existing makefile?

Hi there,

I’m new to CMake, and I’m currently trying to add a third-party library into my CMake project. My problem is that this third-party library only builds on Linux and comes with its own makefile. I figure this is probably a common situation and many have run into, and wonder if there is a idiomatic way to do this?

What I’m currently doing is use add_custom_target where I call the make command to generate the lib.a file. However, my understanding is that add_custom_target only generate utility target which can’t be linked to. So I have my main target link to the lib.a file explicitly, like this: target_link_libraries(MY_TARGET PRIVATE lib.a).

Is this the right way to do it?

Hi.

Regarding the “link to the target” bit: you can create an IMPORTED target for that 3rd-party library. I would personally do that in your situation. This will allow you to add usage requirements (e.g. defines, include directories) to the target and have them propagate automatically via linking, and generally allow you to treat the library as a target in your CMake code. It could look like this:

add_custom_target(BuildTheLib ALL
  COMMAND make ...
  # (as you have now)
)

add_library(TheLib STATIC IMPORTED)
set_target_properties(TheLib
  IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/path/to/lib.a # Make sure to use absolute path here
  INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/lib_include
  INTERFACE_COMPILE_DEFINITIONS "USING_THELIB;THELIB_STATIC"
)
add_dependencies(TheLib BuildTheLib) # So that anyone linking against TheLib causes BuildTheLib to build first

target_link_libraries(MY_TARGET PRIVATE TheLib)

(The values of the properties written above are just examples, naturally, and should be modified or removed based on your actual situation).

Feel free to look into the documentation of imported targets and IMPORTED_* properties to learn more about what you can do with them.

You should have a look at the ExternalProject module.

Hi.

I was recently presented with the same exact problem, where Oracle “odpi” library I required in a CMake-based project is supplied with a Makefile.win32 for Windows compilations and CMakeLists.txt for the rest of the world. Having read the advice you provided I would like to clarify a few points for myself and, hopefully, for the others facing the same problem.

  • Makefile script supplied with a third-party library de-facto defines the build process for the third-party library. And CMake is one level above in logical abstractions since it can produce various build-scripts, including Makefiles, but also Ninja-scripts, vcxproj projects and so on as user demands. As such, the third party actions should be handled separately. ExternalProject or FetchContent are designed for that and are the appropriate way to go.
  • Makefile can be produced by CMake but CMake does not parse existing Makefiles (nor vcxproj, nor Ninja build-scripts) and so it cannot use or invoke targets mentioned in these files. I have to manually specify that I want to “make -f MyMakefile.win32” for the building purposes “make -f MyMakefile.win32 --install” for the installation purposes and so on. I am still uncertain on the “clean” step, but I suppose I have to call “make -f MyMakefile.win32 --clean” manually as well.
  • If I am to link against a library built with MyMakefile.win32, I have to read through the makefile myself, find out what the library name is, in which sub-directory it will be placed and use that in a call to the target_link_libraries in my main project.
  • To get a Makefile-based third-party library into my project that is distributed as source code I cannot rely on find_package, find_module or anything similar to find things like build targets for me. They are for the CMake-based projects only.

I will be grateful if you confirm or correct my understanding.

Both FetchContent and ExternalProject have trouble with stuff you want to build and link when you build your project. The problem is none of the things you need to find in the library will exist at configure time since you’ve not built the library yet.

I think the “cleanest” way may be to rewrite the build system of the project in cmake, and use that to build it.

You can use find_package with third party projects that don’t use cmake, someone just has to supply the module find_package calls (either Find.cmake or Config.cmake (with a few acceptable variations). The Find version is for when the “find module” is written, maintained, and provided by someone other than the upstream maintainers of the project, the config version is for when the maintainers of the upstream project provide the cmake configuration.

There are a few projects that don’t use cmake but provide cmake config files, Qt comes to mind (well, they use cmake to build as of Qt6, but they started providing config files in Qt5).

Another approach is to use pkg-config, cmake has a built in module called FindPkgConfig.cmake (you can call it with find_package(PkgConfig) and it will run pkg-config with the right flags, parse the output, and create a target for you.

Neither of these approaches help if the library (and the pkg-config or cmake configuration files) don’t actually exist when you are configuring, as is the case when you’d like to build the library as part of your project’s

As a workaround you can do things like spawning a new cmake process as a custom command or with execute_process (this is, in fact, how FetchContent works internally) but all such methods are pretty messy.

Also: most makefile based build systems don’t support all the stuff cmake does, and projects that ship Makefile.win32 files in many cases don’t actually do much CI on windows, and their windows makefiles are very frequently just broken.

tl;dr it’s probably easier to just rewrite the makefile in cmake (stuff like file(GLOB) that’s usually not recommended can be quite handy here). As a bonus it’ll build like 10x faster since nmake on windows can’t do multiple rules at once.