Compile and add object file from binary with CMake

2019-02-01 22:07发布

I am writing an Excel file builder in C++.

I have everything I need working, but I still rely on an external empty .xlsx file which I unzip, iterate through, and add data too as needed to create the final file.

I want to remove this dependency by turning the .xlsx file into a binary blob in the .rodata section of my executable, by turning it first into an object file like so:

$ ld -r -b binary -o template.o template.xlsx
$ objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents template.o template.o

I got this information from the blog post http://www.burtonini.com/blog/computers/ld-blobs-2007-07-13-15-50.

The second step is to link it into the binary, which I can do with ld.

How do I automate these two steps with CMake?

I have no idea at the moment how to run specific commands like the ld one above for the first step, and I have tried adding files/template.o to my target_link_libraries for the second, but ld just says:

/usr/bin/ld: cannot find -lfiles/template.o

I added the following custom command to my CMakeLists.txt:

add_custom_command(OUTPUT files/template.o
      COMMAND ld -r -b binary -o files/template.o files/template.xlsx
      COMMAND objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents files/template.o files/template.o)

and added files/template.o to the add_executable call.

Unfortunately, CMake says this:

ld: cannot open output file files/template.o: No such file or directory

It is my understanding that the OUTPUT command in the add_custom_command allows us to tell CMake what file is being created by the COMMAND commands. So I'm a bit confused now.

I updated the CMakeLists.txt file and added a target, to make sure the template file got built:

add_custom_target(run ALL
    DEPENDS template.o)

And a dependency to make sure that it gets built before the excelbuilder target:

add_dependencies(excelbuilder run)

I also updated the custom command to look like this:

add_custom_command(OUTPUT template.o
      COMMAND ld -r -b binary -o template.o ${CMAKE_CURRENT_SOURCE_DIR}/files/template.xlsx
      COMMAND objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents template.o template.o)

When I run this, the output is as follows (make VERBOSE=1)

$ make VERBOSE=1
/usr/bin/cmake -H/home/ravloony/projects/excelparser -B/home/ravloony/projects/excelparser/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/ravloony/projects/excelparser/build/CMakeFiles /home/ravloony/projects/excelparser/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory `/home/ravloony/projects/excelparser/build'
make -f src/lib/minizip/CMakeFiles/minizip_1-1.dir/build.make src/lib/minizip/CMakeFiles/minizip_1-1.dir/depend
make[2]: Entering directory `/home/ravloony/projects/excelparser/build'
cd /home/ravloony/projects/excelparser/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/ravloony/projects/excelparser /home/ravloony/projects/excelparser/src/lib/minizip /home/ravloony/projects/excelparser/build /home/ravloony/projects/excelparser/build/src/lib/minizip /home/ravloony/projects/excelparser/build/src/lib/minizip/CMakeFiles/minizip_1-1.dir/DependInfo.cmake --color=
make[2]: Leaving directory `/home/ravloony/projects/excelparser/build'
make -f src/lib/minizip/CMakeFiles/minizip_1-1.dir/build.make src/lib/minizip/CMakeFiles/minizip_1-1.dir/build
make[2]: Entering directory `/home/ravloony/projects/excelparser/build'
make[2]: Nothing to be done for `src/lib/minizip/CMakeFiles/minizip_1-1.dir/build'.
make[2]: Leaving directory `/home/ravloony/projects/excelparser/build'
/usr/bin/cmake -E cmake_progress_report /home/ravloony/projects/excelparser/build/CMakeFiles  17 18 19 20 21
[ 22%] Built target minizip_1-1
make -f CMakeFiles/run.dir/build.make CMakeFiles/run.dir/depend
make[2]: Entering directory `/home/ravloony/projects/excelparser/build'
cd /home/ravloony/projects/excelparser/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/ravloony/projects/excelparser /home/ravloony/projects/excelparser /home/ravloony/projects/excelparser/build /home/ravloony/projects/excelparser/build /home/ravloony/projects/excelparser/build/CMakeFiles/run.dir/DependInfo.cmake --color=
Dependee "/home/ravloony/projects/excelparser/build/CMakeFiles/run.dir/DependInfo.cmake" is newer than depender "/home/ravloony/projects/excelparser/build/CMakeFiles/run.dir/depend.internal".
Dependee "/home/ravloony/projects/excelparser/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/ravloony/projects/excelparser/build/CMakeFiles/run.dir/depend.internal".
Scanning dependencies of target run
make[2]: Leaving directory `/home/ravloony/projects/excelparser/build'
make -f CMakeFiles/run.dir/build.make CMakeFiles/run.dir/build
make[2]: Entering directory `/home/ravloony/projects/excelparser/build'
/usr/bin/cmake -E cmake_progress_report /home/ravloony/projects/excelparser/build/CMakeFiles 22
[ 27%] Generating template.o
ld -r -b binary -o template.o /home/ravloony/projects/excelparser/files/template.xlsx
objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents template.o template.o
make[2]: Leaving directory `/home/ravloony/projects/excelparser/build'
/usr/bin/cmake -E cmake_progress_report /home/ravloony/projects/excelparser/build/CMakeFiles  22
[ 27%] Built target run
make -f CMakeFiles/excelbuilder.dir/build.make CMakeFiles/excelbuilder.dir/depend
make[2]: Entering directory `/home/ravloony/projects/excelparser/build'
cd /home/ravloony/projects/excelparser/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/ravloony/projects/excelparser /home/ravloony/projects/excelparser /home/ravloony/projects/excelparser/build /home/ravloony/projects/excelparser/build /home/ravloony/projects/excelparser/build/CMakeFiles/excelbuilder.dir/DependInfo.cmake --color=
make[2]: Leaving directory `/home/ravloony/projects/excelparser/build'
make -f CMakeFiles/excelbuilder.dir/build.make CMakeFiles/excelbuilder.dir/build
make[2]: Entering directory `/home/ravloony/projects/excelparser/build'
Linking CXX executable excelbuilder
/usr/bin/cmake -E cmake_link_script CMakeFiles/excelbuilder.dir/link.txt --verbose=1
/usr/bin/c++   -std=c++0x  -g -ftest-coverage -fprofile-arcs -fpermissive    CMakeFiles/excelbuilder.dir/src/common/exception.cpp.o CMakeFiles/excelbuilder.dir/src/excelbuilder/retriever.cpp.o CMakeFiles/excelbuilder.dir/src/excelbuilder/xlsx.cpp.o CMakeFiles/excelbuilder.dir/src/common/config.cpp.o CMakeFiles/excelbuilder.dir/src/excelbuilder/main.cpp.o  -o excelbuilder -rdynamic src/lib/minizip/libminizip_1-1.so -ltinyxml2 -lmysqlcppconn -lboost_regex-mt -ltemplate.o -lz -Wl,-rpath,/home/ravloony/projects/excelparser/build/src/lib/minizip
/usr/bin/ld: cannot find -ltemplate.o
collect2: error: ld returned 1 exit status
make[2]: *** [excelbuilder] Error 1
make[2]: Leaving directory `/home/ravloony/projects/excelparser/build'
make[1]: *** [CMakeFiles/excelbuilder.dir/all] Error 2
make[1]: Leaving directory `/home/ravloony/projects/excelparser/build'
make: *** [all] Error 2

But file template.o has been correctly generated and is in the folder. It seems that ld is expecting a system library.

标签: c++ cmake ld
2条回答
可以哭但决不认输i
2楼-- · 2019-02-01 22:30

In the end, this is how I did it.

add_custom_command(OUTPUT template.o
      COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/files && ld -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/template.o template.xlsx
      COMMAND objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents ${CMAKE_CURRENT_BINARY_DIR}/template.o ${CMAKE_CURRENT_BINARY_DIR}/template.o)

The cd commands are there because ld sets the names of the automatically declared variables to something depending on the full path passed to the input file. So if the input file was /home/user/project/files/template.xlsx, the variable would be something like _binary_home_user_project_files_template_xlsx_start. Not cool for portable compilation.

add_library(template
        STATIC
        template.o)

tells the linker to compile the object file into the binary. This also adds a target called template.

Then

SET_SOURCE_FILES_PROPERTIES(
  template.o
  PROPERTIES
  EXTERNAL_OBJECT true
  GENERATED true
  )

to tell CMake not to compile the file, which is generated at build time.

SET_TARGET_PROPERTIES(
  template
  PROPERTIES
  LINKER_LANGUAGE C 
  )

Or else we get an error message, because CMake can't figure out from the ".o"-suffix that it is a C linker we need.

And then in my target_link_libraries step, I simply added template as a target.

target_link_libraries (excelbuilder
            ${MINIZIP_LIB_NAME}
            ${TINYXML_LIBRARIES}
            ${MYSQLCONNECTORCPP_LIBRARY}
            ${Boost_LIBRARIES}
            template
            )
查看更多
放我归山
3楼-- · 2019-02-01 22:52

To link the object file into the exectuable, add it to the list of source files in add_executable() instead of trying to add it to target_link_libraries().

For generating the object file in the first place, see add_custom_command(). In this case, you will want to use its form which specifies an OUTPUT parameter.

查看更多
登录 后发表回答