Link separate library files per architecture for imported library

Our project links to a number of pre-compiled 3rd party libraries, for which we set up “find modules” using find_library(), find_package_handle_standard_args(), and add_library(... IMPORTED).

On macOS some of these libraries come as separate .a files per architecture (x86_64/arm64). How can we link the right library for the architecture when doing a multi-arch build (CMAKE_OSX_ARCHITECTURES = "x86_64;arm64")?

In Xcode we link the libraries using -l<libname> and using $(CURRENT_ARCH) as part of the library search path, which works because Xcode builds and links each architecture separately and then uses lipo to merge it, but CMake seems to build and link both architectures in one go using multiple -arch options (at least when using the Ninja and Clang).

Is there any CMake option we could use for this, or is there maybe a linker option that would allow us to specify per-architecture linker search paths?

Two things come to mind:

  • use lipo to fuse the libraries together; or
  • does -Xarch_x86_64;x86.lib;-Xarch_arm64;arm64.lib work at all (by manually crafting the command line; CMake won’t know how to generate these flags).

If the latter does work, I suppose that it’d be possible to enhance IMPORTED target properties to have this, but it doesn’t sound trivial.

What about creating two imported library targets, one per arch that has the correct binary as its IMPORTED_LOCATION and sets its OSX_ARCHITRCTURES to only its arch? And then the find module would publicly expose an interface library that links to both of those.

Thanks for the suggestions! I ended up using lipo to create a multi-arch library from the existing ones via a custom target like this, which works great:

foreach(arch ${CMAKE_OSX_ARCHITECTURES})
	find_library(${LIBRARY_NAME}_LIBRARY_${arch}
		NAMES ${LIBRARY_NAME}
		PATHS ${LIBRARY_DIR}/${arch}
	)
	list(APPEND arch_libraries "${${LIBRARY_NAME}_LIBRARY_${arch}}")
endforeach()

set(${LIBRARY_NAME}_LIBRARY "${CMAKE_BINARY_DIR}/libs-gen/${LIBRARY_NAME}-multiarch.a")

if(NOT TARGET ${LIBRARY_NAME}_MULTIARCH)
	add_custom_target(${LIBRARY_NAME}_MULTIARCH
		DEPENDS ${arch_libraries}
		BYPRODUCTS ${${LIBRARY_NAME}_LIBRARY}
		COMMAND lipo ${arch_libraries} -create -output "${${LIBRARY_NAME}_LIBRARY}"
	)
endif()

add_library(${LIBRARY_NAME} UNKNOWN IMPORTED)
add_dependencies(${LIBRARY_NAME} ${LIBRARY_NAME}_MULTIARCH)
set_target_properties(${LIBRARY_NAME} PROPERTIES IMPORTED_LOCATION "${${LIBRARY_NAME}_LIBRARY}")

@benthevining thank you also for your suggestion. I didn’t try it because I had already gotten the above solution working before I saw yours.

1 Like

Does that work? I’m not familiar with that being a thing that works at least… Then again, not something I’ve dealt with much either.

I’m not entirely sure. But that’s the only way I could come up with to possibly express this in cmake today…

AFAIK, CMake is lacking genexes to do any -Xarch_ wrapping of arguments to handle this.

1 Like