[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