Static Linking DCMTK

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
michael127001
Posts: 3
Joined: Mon, 2022-05-30, 08:28

Static Linking DCMTK

#1 Post by michael127001 »

Hi, all:

I'm attempting to write my own DcmStorageSCP using DCMTK (latest GitHub source as of 6/2/2022). My server doesn't care about the entire DICOM file though, just a few parameters of it, which the program will save to an XML file. One of the requirements is that the included libraries must be statically linked (or very easy to distribute). I believe I have successfully built a static version of DCMTK, but I am having issues linking in my CMake file. The following image details my ccmake configuration:

Image

As expected the toolkit is installed to /usr/local/dcmtk and /usr/local/dcmtk/lib contains static (.a) libraries.

Question 1)
Using this setup, will I still need to include openjpeg, iconv, icu, threads, tiff, xml, and zlib into my own project cmake? I'm under the impression DCMTK_LINK_STATIC will magically do this for me.

I already have a bit of source code written for my own DcmStorageSCP using dynamic linking previously, though it has turned into more of a "Hello, World!" with extra includes. My CMakeLists.txt includes the following:

Code: Select all

#Generated by VisualGDB project wizard.-3.6.7-install/usr/local/lib
#Note: VisualGDB will automatically update this file when you add new sources to the project.

cmake_minimum_required(VERSION 2.7)
project(IOLMasterDICOM)

set(DCMTK_DIR "/usr/local/dcmtk")
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(DCMTK_USE_STATIC_LIBS "ON")

find_package(DCMTK REQUIRED)
message("DCMTK_INCLUDE_DIRS: " ${DCMTK_INCLUDE_DIRS})
message("DCMTK_LIBRARIES: " ${DCMTK_LIBRARIES})
message("DCMTK_DIR: " ${DCMTK_DIR})

# add_executable(IOLMasterDICOM tinyxml.h tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp tinystr.h  IOLMDcmStorageSCP.h IOLMDcmStorageSCP.cpp IOLMasterDICOM.cpp)
add_executable(IOLMasterDICOM IOLMasterDICOM.cpp)
target_include_directories(IOLMasterDICOM PRIVATE  ${DCMTK_INCLUDE_DIRS})
target_link_libraries(IOLMasterDICOM ${DCMTK_LIBRARIES})
FILE(COPY storescp.cfg DESTINATION "${CMAKE_BINARY_DIR}")
set_property(TARGET IOLMasterDICOM PROPERTY CXX_STANDARD 17)
This cmake does execute successfully with the following output:

Code: Select all

Running CMake: cmake ../../.. -G "Ninja" -DCMAKE_BUILD_TYPE=DEBUG
-- Trying to find DCMTK expecting DCMTKConfig.cmake
-- Trying to find DCMTK expecting DCMTKConfig.cmake - ok
DCMTK_INCLUDE_DIRS: /usr/local/dcmtk/include
DCMTK_LIBRARIES: ofstdoflogdcmdatai2ddcmxmldcmimgledcmimagedcmjpegijg8ijg12ijg16dcmjplsdcmtkcharlsdcmtlsdcmnetdcmsrcmrdcmdsigdcmwlmdcmqrdbdcmpstatdcmrtdcmioddcmfgdcmsegdcmtractdcmpmapdcmect
DCMTK_DIR: /usr/local/dcmtk/lib/cmake/dcmtk
-- Configuring done
-- Generating done
-- Build files have been written to: /home/x/IOLMasterDICOM/build/VisualGDB/Debug

========== Project Configuration Summary ==========
    IOLMasterDICOM  configured in 00:01
========== Configuration: 1 Succeeded, 0 Failed, 0 Skipped ==========
Except building the project fails spectacularly:

Code: Select all

Run "ninja " in directory "/home/x/IOLMasterDICOM/build/VisualGDB/Debug" on x@x-linux (SSH)
ninja 

[0/1] Linking CXX executable IOLMasterDICOM
[1/1] Linking CXX executable IOLMasterDICOM
FAILED: IOLMasterDICOM 
: && /usr/bin/c++  -g  -rdynamic CMakeFiles/IOLMasterDICOM.dir/IOLMasterDICOM.cpp.o  -o IOLMasterDICOM  -lofstd  -loflog  -ldcmdata  -li2d  -ldcmxml  -ldcmimgle  -ldcmimage  -ldcmjpeg  -lijg8  -lijg12  -lijg16  -ldcmjpls  -ldcmtkcharls  -ldcmtls  -ldcmnet  -ldcmsr  -lcmr  -ldcmdsig  -ldcmwlm  -ldcmqrdb  -ldcmpstat  -ldcmrt  -ldcmiod  -ldcmfg  -ldcmseg  -ldcmtract  -ldcmpmap  -ldcmect && :
/usr/bin/ld: cannot find -lofstd
/usr/bin/ld: cannot find -loflog
/usr/bin/ld: cannot find -ldcmdata
/usr/bin/ld: cannot find -ldcmxml
/usr/bin/ld: cannot find -ldcmimgle
/usr/bin/ld: cannot find -ldcmimage
/usr/bin/ld: cannot find -ldcmjpeg
/usr/bin/ld: cannot find -ldcmjpls
/usr/bin/ld: cannot find -ldcmtkcharls
/usr/bin/ld: cannot find -ldcmtls
/usr/bin/ld: cannot find -ldcmnet
/usr/bin/ld: cannot find -ldcmsr
/usr/bin/ld: cannot find -ldcmdsig
/usr/bin/ld: cannot find -ldcmwlm
/usr/bin/ld: cannot find -ldcmqrdb
/usr/bin/ld: cannot find -ldcmpstat
/usr/bin/ld: cannot find -ldcmrt
/usr/bin/ld: cannot find -ldcmiod
/usr/bin/ld: cannot find -ldcmfg
/usr/bin/ld: cannot find -ldcmseg
/usr/bin/ld: cannot find -ldcmtract
/usr/bin/ld: cannot find -ldcmpmap
/usr/bin/ld: cannot find -ldcmect
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
-------------------------------------------------------------
Command exited with code 1
Executable: ninja
Arguments: 
Directory: /home/x/IOLMasterDICOM/build/VisualGDB/Debug
Command-line action failed

========== Project Build Summary ==========
    IOLMasterDICOM  built in 00:00
========== Build: 0 Succeeded, 1 Failed, 0 Skipped ==========
I've found I can use find_library to fetch the libs, though I presume there is a better approach than manually attaching every library in the lib folder.

Question 2)
What gives? The install directory contains all the files needed to statically link DCMTK and the cmake identifies the folder. Why does target_link_libraries link dynamically causing my build to fail?

To summarize my goal again, I'm basically trying to create a binary in the likes of storescp without any unnecessary installs for distribution to NAS servers. Been pulling my hair out for some time permuting many different fixes, so any help is appreciated greatly :D

michael127001
Posts: 3
Joined: Mon, 2022-05-30, 08:28

Re: Static Linking DCMTK

#2 Post by michael127001 »

Update:

Built openjpeg from source to allow static linking then I was able to modify my CmakeLists.txt as such to build statically:

Code: Select all

#Generated by VisualGDB project wizard.-3.6.7-install/usr/local/lib
#Note: VisualGDB will automatically update this file when you add new sources to the project.

cmake_minimum_required(VERSION 2.7)
project(IOLMasterDICOM)

SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")

find_package(ZLIB REQUIRED)
message("ZLIB_INCLUDE_DIRS: " ${ZLIB_INCLUDE_DIRS})
message("ZLIB_LIBRARIES: " ${ZLIB_LIBRARIES})

add_executable(IOLMasterDICOM tinyxml.h tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp tinystr.h  IOLMDcmStorageSCP.h IOLMDcmStorageSCP.cpp IOLMasterDICOM.cpp)
target_include_directories(IOLMasterDICOM PRIVATE ${DCMTK_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS})

# Link DCMTK
set(DCM_LIBS dcmect;dcmpmap;dcmtract;dcmseg;dcmfg;dcmiod;dcmrt;dcmpstat;dcmqrdb;dcmwlm;dcmdsig;dcmsr;dcmnet;dcmtls;dcmjpls;dcmjpeg;dcmimage;dcmimgle;dcmdata;oflog;ofstd)
foreach(X IN LISTS DCM_LIBS)
	find_library(NO_CACHE NAMES ${X} HINTS "/usr/local/dcmtk/lib" REQUIRED)
	target_link_libraries(IOLMasterDICOM "/usr/local/dcmtk/lib/lib${X}.a" -static)
endforeach()

# Link DCMTK Dependencies
set(EXTERNAL_LIBS openjp2;i2d;charset;ijg12;ijg16;ijg8;png16)
foreach(X IN LISTS EXTERNAL_LIBS)
	find_library(NO_CACHE NAMES ${X} HINTS "/usr/local/lib" REQUIRED)
	target_link_libraries(IOLMasterDICOM "/usr/local/lib/lib${X}.a" -static)
endforeach()

target_link_libraries(IOLMasterDICOM -lpthread ${ZLIB_LIBRARIES} -static)

FILE(COPY storescp.cfg DESTINATION "${CMAKE_BINARY_DIR}")
set_property(TARGET IOLMasterDICOM PROPERTY CXX_STANDARD 17)
I was able to copy the executable folder to my Windows machine and run it using WSL2. Hooray! :D Now I must ask, is there a better way? Specifying all the libraries myself seems a exact. Also, what does the DCMTK_LINK_STATIC do?

J. Riesmeier
DCMTK Developer
Posts: 2501
Joined: Tue, 2011-05-03, 14:38
Location: Oldenburg, Germany
Contact:

Re: Static Linking DCMTK

#3 Post by J. Riesmeier »

You don't need OpenJPEG for building or using the public, freely available DCMTK. It might be used by some private DCMTK extensions that can be licensed separately from OFFIS.

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

Re: Static Linking DCMTK

#4 Post by Marco Eichelberg »

Also, what does the DCMTK_LINK_STATIC do?
DCMTK_LINK_STATIC tries to create truly static DCMTK command line tool binaries, i.e. it calls gcc -static. Such binaries are not ELF binaries anymore and depend on no shared object (i.e. just require a compatible kernel), but unfortunately under Linux these binaries cause issues if executed on a machine with a different glibc version.

michael127001
Posts: 3
Joined: Mon, 2022-05-30, 08:28

Re: Static Linking DCMTK

#5 Post by michael127001 »

Marco Eichelberg wrote: Thu, 2022-06-09, 15:21
Also, what does the DCMTK_LINK_STATIC do?
DCMTK_LINK_STATIC tries to create truly static DCMTK command line tool binaries, i.e. it calls gcc -static. Such binaries are not ELF binaries anymore and depend on no shared object (i.e. just require a compatible kernel), but unfortunately under Linux these binaries cause issues if executed on a machine with a different glibc version.
Actually meant to follow up about this. I did end up creating a static executable to run on most Linux machines. The SCP server would start up nicely, but on the first association: Segmentation Fault! Turns out every internet source was correct and static linking C runtime libraries is a bad idea :D

For any frantically googling developers looking to static link with as little knowledge coming into it as I did, I'll explain my process. The CMakeLists I provided earlier does build a static application, but it will not work properly. I adjusted my build script to only link the static libraries from DCMTK, which would have done the trick if my target machine didn't have ancient runtimes (/lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found :(). This has nothing to do with DCMTK itself, but those looking to static link may run into the same issue. Ended with setting up a virtual machine using a Linux version a little bit older than my target to guarantee the backwards compatibility. On each build, VisualGDB uploads the source to the VM and the tools there build the executable. With a little more configuration, VisualGDB automatically deploys the build and debugs on the target machine, all using SSH.

Final CMakeLists looked something like this:

Code: Select all

#Generated by VisualGDB project wizard.
#Note: VisualGDB will automatically update this file when you add new sources to the project.

cmake_minimum_required(VERSION 3.17)
project(IOLMasterDICOM)

find_package(ZLIB REQUIRED)
message("ZLIB_INCLUDE_DIRS: " ${ZLIB_INCLUDE_DIRS})
message("ZLIB_LIBRARIES:    " ${ZLIB_LIBRARIES})

find_package(DCMTK REQUIRED)
message("DCMTK_INCLUDE_DIRS:	" ${DCMTK_INCLUDE_DIRS})
message("DCMTK_LIBRARIES:		" ${DCMTK_LIBRARIES})
message("DCMTK_FOUND:			" ${DCMTK_FOUND})
message("DCMTK_DIR:				" ${DCMTK_DIR})

set(DCMTK_LIB64_DIR "/usr/local/dcmtk/lib64")

add_executable(IOLMasterDICOM tinyxml.h tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp tinystr.h IOLMDcmStorageSCP.h IOLMDcmStorageSCP.cpp IOLMasterDICOM.cpp)
target_include_directories(IOLMasterDICOM PRIVATE ${DCMTK_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS})

# Link DCMTK
set(DCM_LIBS dcmect;dcmpmap;dcmtract;dcmseg;dcmfg;dcmiod;dcmrt;dcmpstat;dcmqrdb;dcmwlm;dcmdsig;dcmsr;dcmnet;dcmtls;dcmjpls;dcmjpeg;dcmimage;dcmimgle;dcmdata;oflog;ofstd)
foreach(X IN LISTS DCM_LIBS)
	find_library(NO_CACHE NAMES ${X} HINTS ${DCMTK_LIB64_DIR} REQUIRED)
	target_link_libraries(IOLMasterDICOM "${DCMTK_LIB64_DIR}/lib${X}.a")
endforeach()

# Additional DCMTK libs
set(ADDITIONAL_LIBS i2d;ijg12;ijg16;ijg8)
foreach(X IN LISTS ADDITIONAL_LIBS)
	find_library(NO_CACHE NAMES ${X} HINTS ${DCMTK_LIB64_DIR} REQUIRED)
	target_link_libraries(IOLMasterDICOM "${DCMTK_LIB64_DIR}/lib${X}.a")
endforeach()

target_link_libraries(IOLMasterDICOM -lpthread ${ZLIB_LIBRARIES})

FILE(COPY storescp.cfg DESTINATION "${CMAKE_BINARY_DIR}")
set_property(TARGET IOLMasterDICOM PROPERTY CXX_STANDARD 17)
Was a bit annoyed target_link_libraries would try to find all the shared libs for DCMTK with ${DCMTK_LIBRARIES} because I had to use the for loops to link each library instead. There's probably some way to tell find_package to just grab the static libs, but this worked sooner than I could find the magic keyword to do so. Didn't care that zlib was dynamically linked since it's a pretty well distributed library.

Thanks to the DCMTK team :)

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

Re: Static Linking DCMTK

#6 Post by Marco Eichelberg »

You might have noticed that with DCMTK 3.6.7 we have introduced "DCMTK_PORTABLE_LINUX_BINARIES" as another build option. This tries to link all libraries except glibc statically and should result in fairly portable binaries.

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot] and 1 guest