[CMake] Applying (modern) CMake to an unsual source code structure
Osipov, Michael
michael.osipov at siemens.com
Fri Aug 2 09:34:30 EDT 2019
Hi folks,
we are migrating from a homegrown Makefile generator to CMake for a
fairly large codebase in C and Fortran (77, 90) (100 000+ LoC) on HP-UX
with the HP compilers. So I am confined to CMake 3.9.
Here is the setup:
Libs: less than 10 of those, all archive libraries. The output does not
produce one .a file, but all objects will be installed and a subset of
those objects are assembled into archive libraries. E.g, libfoo =>
libfoo_1.a, libfoo_2.a, etc. It may also happen that libfoo adds an
object into an archive from libbar or uses a .c or .f file from another
lib via symlink -- now svn:externals -- yes, no kidding.
Applications: around 150 which may contain thousands lines of code in
mostly Fortran. They may apply the same scheme, link external source
code into src/ or use external objects. Though, they can also link
against those archive libs, some also link against libcurl or libjansson
dynamically.
We have a very unusual/messy code organization I cannot and will not
solve now because I am not owner/writer of the code, I take care of the
platform and the code may be old 20, 30+ years.
Granted, I have tried to list source files, compile, link. That
succeeded, but the applications never ran properly until I have applied
the old scheme.
The old scheme was to plain list all required objects and link them in
the order of appearance and that worked.
Consider make variables BHx through BHn containing:
> BH3=$(WORK_OBJ)/f1204w.bin \
> $(WORK_OBJ)/f1204x.bin \
> $(WORK_OBJ)/e1204d.bin \
> $(WORK_OBJ)/g1204b.bin \
> $(WORK_OBJ)/g1204v.bin \
> $(WORK_OBJ)/c1204g.bin \
> $(SM_OBJ_fest)/tdv700.bin \
> $(WORK_OBJ)/s1204q.bin \
> $(WORK_OBJ)/f1204v.bin \
> $(WORK_OBJ)/f1204y.bin \
> $(WORK_OBJ)/w1204b.bin \
> $(WORK_OBJ)/z1204a.bin \
> $(WORK_OBJ)/z1204b.bin \
> $(WORK_OBJ)/z1204c.bin \
> $(SGRAM_OBJ_fest)/mp411.bin \
> $(SGRAM_OBJ_fest)/mp411v.bin \
> $(SGRAM_OBJ_fest)/mp412.o \
> $(SGRAM_OBJ_fest)/mp413.bin \
> $(SGRAM_OBJ_fest)/mp413g.bin \
> $(SGRAM_OBJ_fest)/mp413s.bin
>
> BH4=$(SGRAM_OBJ_fest)/mp413v.bin \
> $(SGRAM_OBJ_fest)/mp415.bin \
> $(SGRAM_OBJ_fest)/mpmark.o \
> $(WORK_OBJ)/z1204f.bin \
> $(WORK_OBJ)/z1204g.bin \
> $(WORK_OBJ)/z1204d.bin \
> $(SGRAM_OBJ_fest)/mp414a.bin \
> $(SGRAM_OBJ_fest)/mp414.bin \
> $(WORK_OBJ)/e1204z.bin \
> $(WORK_OBJ)/r1204b.bin \
> $(SGRAM_OBJ_fest)/mp421.bin \
> $(SGRAM_OBJ_fest)/mp422g.bin \
> $(WORK_OBJ)/s1204l.bin \
> $(WORK_OBJ)/f1204s.bin \
> $(SBM_OBJ_fest)/sb_ar1102.bin \
> $(WORK_OBJ)/g1204z.bin \
> $(WORK_OBJ)/s1204z.bin \
> $(WORK_OBJ)/z1204h.bin \
> $(WORK_OBJ)/e1204w.bin \
> $(WORK_OBJ)/f1204t.bin
WORK_OBJ refers to the current object dir of the application 'di1204'
whereas SGRAM, SBM, etc. are libraries. Note that .bin has been
previously selected for Fortran object files.
Now linking together:
> di1204.p: $(BH1) $(BH2) $(BH3) $(BH4) $(BH5) $(BH6) $(BH7) $(BH8) $(BH9) $(BH10)
> $(LINKER) $(BH1) $(BH2) $(BH3) $(BH4) $(BH5) $(BH6) $(BH7) $(BH8) $(BH9) $(BH10) \
> $(LINKER_OPTIONS) $(USR_BIND_OPTIONS) -o $(WORK_BIN)/di1204.p
To retain this order I applied the following approach in CMake:
* Each breaking/grouping block is assinged a variable with source files
and an object library:
> set(DI1204_5_SRC
> src/e1204z.f
> src/r1204b.f
> )
> add_library(di1204_5 OBJECT
> ${DI1204_5_SRC}
> )
* External objects are solved with IMPORTED and properties on these:
> add_library(sm_5 OBJECT IMPORTED)
> set_property(TARGET sm_5 PROPERTY IMPORTED_OBJECTS
> ${CMAKE_INSTALL_PREFIX}/obj/libsm/tdv230.pc.c.o
> ${CMAKE_INSTALL_PREFIX}/obj/libsm/tdv450.f.o
> ${CMAKE_INSTALL_PREFIX}/obj/libsm/tdv452.f.o
> ${CMAKE_INSTALL_PREFIX}/obj/libsm/tdv469.f.o
> )
* Add these object collections with generator expressions in the order
they appear to the executable:
> add_executable(${PROJECT_NAME}
> $<TARGET_OBJECTS:di1204_1>
> $<TARGET_OBJECTS:sm_1>
> $<TARGET_OBJECTS:di1204_2>
> $<TARGET_OBJECTS:sm_2>
> $<TARGET_OBJECTS:di1204_3>
> $<TARGET_OBJECTS:sgram_1>
> $<TARGET_OBJECTS:di1204_4>
> $<TARGET_OBJECTS:sgram_2>
>... omitted for brevity
> $<TARGET_OBJECTS:sbm_11>
> $<TARGET_OBJECTS:di1204_19>
>
> )
* Link this executable in order of appearance:
> find_library(LIBSBM_1204 sbm_1204)
> find_library(LIBSBM_1102 sbm_1102)
> find_library(LIBSBM_1219 sbm_1219)
> find_library(LIBSBM_3460 sbm_3460)
> ... omitted for brevity
> find_library(LIBSGRAM_GDS000 sgram_gds000)
> find_library(LIBSM_IEC sm_iec)
> find_library(LIBSM_READIN_O sm_readin_o)
> find_library(LIBSBLM_3401 sblm_3401)
>
> set(LIBS
> ${LIBSBM_1204}
> ${LIBSBM_1102}
> ${LIBSBM_1219}
> ${LIBSBM_3460}
> ${LIBSBM_3424}
> ${LIBSBM_1512}
> ${LIBSBM_3427}
> ${LIBSBM_1236}
> ${LIBSBM_1235}
> ${LIBSBM_1510}
> ${LIBSGRAM_GDS000}
> ${LIBSM_IEC}
> ${LIBSM_READIN_O}
> ${LIBSBLM_3401}
> ${LIBCLNTSH}
> ${LIBCURL}
> ${LIBJANSSON}
> )
> target_link_libraries(${PROJECT_NAME} ${LIBS})
At the end, and by the static linking we have to do, I need to following
to work:
> install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${BIN_DIR}) (1)
> install(FILES $<TARGET_OBJECTS:standalone> DESTINATION ${OBJ_DIR}) (2)
> install(FILES ${SRC} DESTINATION ${SRC_DIR}/${PROJECT_NAME}) (3)
> foreach(prog_lib_name ${PROG_LIB_NAMES}) (3)
> install(
> DIRECTORY ${CMAKE_INSTALL_PREFIX}/progs/${prog_lib_name}/src/
> DESTINATION ${SRC_DIR}/${prog_lib_name})
> endforeach()
(1) Install the executable
(2) Install all internal object files of this appliction for others to use
(3) Install the source code of this application for the debugger
(4) Install the source code of the statically linked libraries for this
application for the debugger
With that approach the applications run just like before on a new
machine, but with CMake now.
My question now is:
* Is that a reasonable way to solve this in CMake?
* Is there a yet better approach to this?
Best regards,
Michael
More information about the CMake
mailing list