Issue
I am using png++ lib and it requires to compile via
g++ -c example.cpp `libpng-config --cflags `
g++ -o example example.o `libpng-config --ldflags`
And it works fine, but i use clion so i want to run it with CMake(i know that i can just run shell but it is not good-looking option). I have no idea how to do this. I have tried 2 ways but neither has worked. My guess that problem in back ticks(`). Truly, i don't really understand what they mean
I have tried these CMakeLists
cmake_minimum_required(VERSION 3.27)
project(image_proc)
set(CMAKE_CXX_STANDARD 17)
add_executable(image_proc src/main.cpp src/core.h)
add_compile_options(`libpng-config --cflags`)
add_link_options(`libpng-config --ldflags`)
cmake_minimum_required(VERSION 3.27)
project(image_proc)
set(CMAKE_CXX_STANDARD 17)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} `libpng-config --cflags`")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} `libpng-config --ldflags`")
add_executable(image_proc src/main.cpp src/core.h)
and expected to compile properly. Instead i receive output
====================[ Build | image_proc | Debug ]==============================
/home/saprykin/.local/share/JetBrains/Toolbox/apps/clion-nova/bin/cmake/linux/x64/bin/cmake --build /home/saprykin/image_proc/cmake-build-debug --target image_proc -j 3
[1/1] Linking CXX executable image_proc
FAILED: image_proc
: && /usr/bin/c++ -g CMakeFiles/image_proc.dir/src/main.cpp.o -o image_proc && :
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::info_base::info_base(png::io_base&, png_struct_def*)':
/usr/local/include/png++/info_base.hpp:55: undefined reference to `png_create_info_struct'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::info::write() const':
/usr/local/include/png++/info.hpp:126: undefined reference to `png_set_PLTE'
/usr/bin/ld: /usr/local/include/png++/info.hpp:133: undefined reference to `png_set_tRNS'
/usr/bin/ld: /usr/local/include/png++/info.hpp:147: undefined reference to `png_set_gAMA'
/usr/bin/ld: /usr/local/include/png++/info.hpp:157: undefined reference to `png_write_info'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::info::sync_ihdr() const':
/usr/local/include/png++/info.hpp:172: undefined reference to `png_set_IHDR'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::end_info::destroy()':
/usr/local/include/png++/end_info.hpp:56: undefined reference to `png_destroy_info_struct'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::end_info::write() const':
/usr/local/include/png++/end_info.hpp:66: undefined reference to `png_write_end'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::io_base::set_swap() const':
/usr/local/include/png++/io_base.hpp:315: undefined reference to `png_set_swap'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::io_base::set_interlace_handling() const':
/usr/local/include/png++/io_base.hpp:379: undefined reference to `png_set_interlace_handling'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::io_base::raise_error()':
/usr/local/include/png++/io_base.hpp:449: undefined reference to `png_set_longjmp_fn'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::io_base::raise_error(png_struct_def*, char const*)':
/usr/local/include/png++/io_base.hpp:454: undefined reference to `png_get_error_ptr'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::writer(std::basic_ofstream<char, std::char_traits<char> >&)':
/usr/local/include/png++/writer.hpp:78: undefined reference to `png_create_write_struct'
/usr/bin/ld: /usr/local/include/png++/writer.hpp:80: undefined reference to `png_set_write_fn'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::~writer()':
/usr/local/include/png++/writer.hpp:86: undefined reference to `png_destroy_write_struct'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::write_info() const':
/usr/local/include/png++/writer.hpp:106: undefined reference to `png_set_longjmp_fn'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::write_row(unsigned char*)':
/usr/local/include/png++/writer.hpp:118: undefined reference to `png_set_longjmp_fn'
/usr/bin/ld: /usr/local/include/png++/writer.hpp:122: undefined reference to `png_write_row'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::write_end_info() const':
/usr/local/include/png++/writer.hpp:130: undefined reference to `png_set_longjmp_fn'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::write_data(png_struct_def*, unsigned char*, unsigned long)':
/usr/local/include/png++/writer.hpp:140: undefined reference to `png_get_error_ptr'
/usr/bin/ld: /usr/local/include/png++/writer.hpp:143: undefined reference to `png_get_io_ptr'
/usr/bin/ld: CMakeFiles/image_proc.dir/src/main.cpp.o: in function `png::writer<std::basic_ofstream<char, std::char_traits<char> > >::flush_data(png_struct_def*)':
/usr/local/include/png++/writer.hpp:169: undefined reference to `png_get_error_ptr'
/usr/bin/ld: /usr/local/include/png++/writer.hpp:172: undefined reference to `png_get_io_ptr'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
Solution
The "arguments in backticks" are not arguments yet. They are command substitutions to be made by the shell. Only after the substitution do they become arguments.
Since you're writing CMake code, not shell code, you have to do those substitutions in a way CMake expresses them. But, ideally, you shouldn't be doing any of it manually. CMake is supposed to handle it for you - that's what it is there for!
Since libpng-config
gives you configuration information for the popular libpng library, perhaps CMake already has a Find Module for libPNG? It does:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
find_package(PNG REQUIRED)
# When building an executable - link PRIVATE-ly
add_executable(my_executable ...)
target_link_libraries(my_executable PRIVATE PNG::PNG)
# When building a library, link PUBLIC-ly
add_library(my_library ...)
target_link_libraries(my_library PUBLIC PNG::PNG)
The command find_package(FOO [...])
means "Search for FindFOO
module, pass arguments to it, and execute the code within". That code is supposed to find the "FOO" package. Here, "PNG" is short for "libpng". The convention is that a Find Module for libFoo
would be called FindFOO
or FindFoo
.
You want to use a higher minimum CMake version declaration. The version I show is the minimum needed to use the features I demonstrate, nothing more.
Since you're building an executable, only the part going with add_executable
is needed. The add_library
part is an example of how to do it when building a library.
This has the benefit of working across platforms, whereas libpng-config
is a Unix thing.
The FindPNG
module (accessed via find_package
command) sets up an IMPORTED library target. That target carries target properties with the necessary include paths, link libraries, and compilation options needed to use the library.
The target_link_libraries
command does more than merely linking with a library. PNG::PNG
is a target created by the FindPNG
module, and that target has all the information needed to use the library.
Another benefit of using find_package
is that there are common methods of passing hints to Find modules, so if someone using your project has libpng in a non-standard location, they can easily pass the location of the library to FindPNG via CMAKE variables from the command line or from the cache or from the json config file. The latter two methods are commonly used by the IDEs, for example.
Even though it's not recommended and would be considered a hack and in bad style, it is of course possible to do the command substitution manually. Let's call libpng-config
explicitly then, just like the shell would:
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
execute_process(
COMMAND libpng-config --cflags
ECHO_OUTPUT_VARIABLE LIBPNG_CFLAGS
)
execute_process(
COMMAND libpng-config --ldflags
ECHO_OUTPUT_VARIABLE LIBPNG_LDFLAGS
)
add_executable(my_executable ...)
target_compile_options(my_executable PRIVATE ${LIBPNG_CFLAGS})
target_link_options(my_executable PRIVATE ${LIBPNG_LDFLAGS})
As you can see, using the FindPNG
module via find_package
is way easier.
Why FindPNG works here? Because my lib requires this module?
FindPNG works here because it's a Find module written to look for the PNG library (libpng). There are many such find modules, for various common libraries, and they take a lot of drudgery out of adding popular dependencies to a project.
Since your projects needs libpng, you can use the FindPNG module.
See:
- the Finding Packages chapter in Mastering CMake, and
- the list of Find Modules in CMake documentation.
Backticks (alternatively $( ... )
) denote shell command substitution. The entire backticks "phrase" is replaced by the output of the command.
The way bash (or most any Unix shell) sees it is as follows:
- Input command line:
g++ -c example.cpp `libpng-config --cflags`
, or:
g++ -c example.cpp $(libpng-config --cflags)
- Command substitution is needed. Runs
libpng-config --cflags
and substitutes its output into the command line. - Now the command line looks like (for example):
g++ -c example.cpp -I/usr/include/libpng16
- Since there are no more substitutions to perform, the
g++
process is spawned with the given command line.
Answered By - Kuba hasn't forgotten Monica Answer Checked By - Mildred Charles (WPSolving Admin)