0

I want to collect object files (.o) in a particular directory with CMake

I have tried the below snippet from How to collect object files (.o) in a particular directory with CMake?

add_custom_command(TARGET ${LIBNAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${OBJ_DIR} COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_OBJECTS:${LIBNAME_OBJ}> ${OBJ_DIR}) 

The command copy_if_different works well when the library contains less number of sources and fails when it's more. How to collect objects of a project which contains huge number of sources ?

1
  • 1
    You need to be specific about this threshold. What does "huge" mean? How many files? If it is too big maybe try doing that with an external script (e.g., in bash) and call that from a CMake custom command. Commented Mar 31, 2021 at 13:35

1 Answer 1

0

For add_custom_command to execute its COMMAND arguments, something has to create a process environment for the command to execute in. Whether that's done by a command shell or directly using exec(), the size limit for that environment is defined by the limits.h macro ARG_MAX. On POSIX systems, getconf ARG_MAX will show the compile-time value of that configuration. On my Fedora 35 system, the value is 2097152 which seems almost impossible to exceed, but maybe you're dealing with smaller limits.

(Also, the ARG_MAX limit represents the entire environment, not just command arguments. So if there are a lot of environment variables defined, that could eat into your max length.)

If you truly are exceeding that, you have two choices:

Option 1: Install the objects in the install stage, as intended

If I'm understanding the linked q&a correctly, the only reason you need the $<TARGET_OBJECTS> paths is so that you can copy them somewhere else in the build tree, in order to then install them from there?

If you just want to install them, a simpler solution is to write custom install code, e.g.

# necessary because config variables aren't present # in the install context. install(CODE "set(OBJECT_DEST \"${CMAKE_INSTALL_LIBDIR}\")" install(CODE [[ file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/${OBJECT_DEST}" FILES $<TARGET_OBJECTS:_actual_object_name_> )]]) 

Messing around with keeping the object name in a variable is a mistake when using custom install code, IMHO, as the evaluation contexts get too confusing. Instead of ${LIBNAME_OBJ}, just use the actual target name in the generator expression.1

Since the generator-expression expansion is handled by CMake when generating the cmake_install.cmake script, there shouldn't be any limit on the length it can have. (Not beyond whatever limits it has internally, if any.)

Option 2: Break up your targets

If you really want to use add_custom_command with the objects, you'll need smaller targets. Why do all of your sources have to be defined on a single object library? Just break them up:

set(SOURCES1 a.c b.c c.c d.c) set(SOURCES2 w.c x.c y.c z.c) add_library(s1_obj OBJECT ${SOURCES1}) add_library(s2_obj OBJECT ${SOURCES2}) add_executable(foo $<TARGET_OBJECTS:s1_obj> $<TARGET_OBJECTS:s2_obj> ) add_custom_target(copy_all_objs COMMAND ${CMAKE_COMMAND} -E make_directory ${OBJ_ROOT_DIR}/myobjects/ COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_OBJECTS:s1_obj> ${OBJ_ROOT_DIR}/myobjects/ COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_OBJECTS:s2_obj> ${OBJ_ROOT_DIR}/myobjects/ COMMAND_EXPAND_LISTS) 
Notes
  1. Actually, I typically recommend using real target names whenever possible in generator expressions.

    With some targets — at least SHARED, STATIC, MODULE and ARCHIVE libraries, plus executables — the target name defines the default output file name. So, I get using a variable for the name. (Though, I did say default — it can be overridden with the OUTPUT_NAME target property, and often that's a better way to make filenames configurable. And it lets you keep static target names.)

    But for targets like INTERFACE, OBJECT, and ALIAS libraries, the target name is meaningless — it's only used internally in the CMake code. There's no reason not to just hardcode it.

    If you're doing this in a macro/function and the target name has to be in a variable, your best bet is probably to write the install() code as a quoted argument rather than a bracket argument, so that variables get evaluated immediately. Which means lots of fun escaping, for the things you don't want evaluated immediately. (Though you can lose the OBJECT_DEST variable, at least.):

    install(CODE " file(INSTALL DESTINATION \"\$\{CMAKE_INSTALL_PREFIX\}/${CMAKE_INSTALL_LIBDIR}\" FILES $<TARGET_OBJECTS:${LIBNAME_OBJ}> )") 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.