Discussion:
[Development] Improving CMake support for static builds
Kyle Edwards
2018-10-12 19:11:43 UTC
Permalink
Hello everyone,

New Qt developer here. I'm trying to improve Qt's support for static
builds using CMake - specifically, encoding transitive dependencies in
the *Config.cmake files. I see that these files already have inter-
module dependencies encoded in the exported targets'
INTERFACE_LINK_LIBRARIES property, but there is no information for the
flags needed to link against system libraries (freetype, harfbuzz,
etc.)

With dynamic builds, this isn't an issue, because the Qt modules
themselves link against these libraries. However, static builds need
this information to generate a fully linked binary. I've started a
patch to add this support, but I'm not very familiar with Qt's
buildsystem, and I was hoping someone could help me figure out how to
get the information that I need. The start of my patch is below:

-------------------------------------------------------------

diff --git a/mkspecs/features/create_cmake.prf
b/mkspecs/features/create_cmake.prf
index 2ed708e..2d5ab55 100644
--- a/mkspecs/features/create_cmake.prf
+++ b/mkspecs/features/create_cmake.prf
@@ -180,6 +180,7 @@ CMAKE_MKSPEC = $$[QMAKE_XSPEC]
 sorted_deps = $$sort_depends(QT.$${MODULE}.depends, QT.)
 mod_deps =
 lib_deps =
+mod_link_flags =
 aux_mod_deps =
 aux_lib_deps =
 # Until CMake 3.0 is the minimum requirement of Qt 5, we need to
filter
@@ -197,6 +198,7 @@ for (dep, sorted_deps) {
 }
 CMAKE_MODULE_DEPS = $$join(mod_deps, ";")
 CMAKE_QT5_MODULE_DEPS = $$join(lib_deps, ";")
+CMAKE_MODULE_LINK_FLAGS = $$join(mod_link_flags, ";")
 CMAKE_INTERFACE_MODULE_DEPS = $$join(aux_mod_deps, ";")
 CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";")
 
diff --git a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
index 3ed6dd5..a320902 100644
--- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
+++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
@@ -64,6 +64,11 @@
macro(_populate_$${CMAKE_MODULE_NAME}_target_properties Configuration
LIB_LOCATI
 !!IF !isEmpty(CMAKE_LIB_SONAME)
         \"IMPORTED_SONAME_${Configuration}\" \"$${CMAKE_LIB_SONAME}\"
 !!ENDIF
+!!IF !isEmpty(CMAKE_STATIC_TYPE)
+!!IF !isEmpty(CMAKE_MODULE_LINK_FLAGS)
+        \"INTERFACE_LINK_OPTIONS\"
\"${_Qt5$${CMAKE_MODULE_NAME}_LIB_LINK_FLAGS}\"
+!!ENDIF
+!!ENDIF
         # For backward compatibility with CMake < 2.8.12
         \"IMPORTED_LINK_INTERFACE_LIBRARIES_${Configuration}\"
\"${_Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES}\"
     )
@@ -215,6 +220,10 @@ if (NOT TARGET Qt5::$${CMAKE_MODULE_NAME})
 !!ENDIF
 
 !!IF !isEmpty(CMAKE_STATIC_TYPE)
+!!IF !isEmpty(CMAKE_MODULE_LINK_FLAGS)
+    set(_Qt5$${CMAKE_MODULE_NAME}_LIB_LINK_FLAGS
\"$${CMAKE_MODULE_LINK_FLAGS}\")
+
+!!ENDIF
     add_library(Qt5::$${CMAKE_MODULE_NAME} STATIC IMPORTED)
     set_property(TARGET Qt5::$${CMAKE_MODULE_NAME} PROPERTY
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX")
 !!ELSE

-------------------------------------------------------------

I've figured out how to pass information into Qt5BasicConfig.cmake.in
from create_cmake.prf, but I'm not sure where to get the link flags
that need to be passed in. Any advice would be appreciated.

Kyle
Matthew Woehlke
2018-10-12 19:23:18 UTC
Permalink
Post by Kyle Edwards
New Qt developer here. I'm trying to improve Qt's support for static
builds using CMake [...]
See also https://bugreports.qt.io/browse/QTBUG-38913. This has been
languishing for entirely too long; it will be great if someone can
finally fix it!

Hopefully someone with more qmake knowledge can help?
--
Matthew
Jean-Michaël Celerier
2018-10-12 18:32:10 UTC
Permalink
Post by Kyle Edwards
but there is no information for the
flags needed to link against system libraries (freetype, harfbuzz,
etc.)

it would be so nice to add them ! currently I have to use the following
chthonic horror:
https://github.com/OSSIA/score/blob/master/base/app/StaticApp.cmake

I think that in Qt the pkg-config files are generated by a perl script,
maybe the same could be used for static builds ?

-------
Jean-Michaël Celerier
http://www.jcelerier.name
Post by Kyle Edwards
Hello everyone,
New Qt developer here. I'm trying to improve Qt's support for static
builds using CMake - specifically, encoding transitive dependencies in
the *Config.cmake files. I see that these files already have inter-
module dependencies encoded in the exported targets'
INTERFACE_LINK_LIBRARIES property, but there is no information for the
flags needed to link against system libraries (freetype, harfbuzz,
etc.)
With dynamic builds, this isn't an issue, because the Qt modules
themselves link against these libraries. However, static builds need
this information to generate a fully linked binary. I've started a
patch to add this support, but I'm not very familiar with Qt's
buildsystem, and I was hoping someone could help me figure out how to
-------------------------------------------------------------
diff --git a/mkspecs/features/create_cmake.prf
b/mkspecs/features/create_cmake.prf
index 2ed708e..2d5ab55 100644
--- a/mkspecs/features/create_cmake.prf
+++ b/mkspecs/features/create_cmake.prf
@@ -180,6 +180,7 @@ CMAKE_MKSPEC = $$[QMAKE_XSPEC]
sorted_deps = $$sort_depends(QT.$${MODULE}.depends, QT.)
mod_deps =
lib_deps =
+mod_link_flags =
aux_mod_deps =
aux_lib_deps =
# Until CMake 3.0 is the minimum requirement of Qt 5, we need to
filter
@@ -197,6 +198,7 @@ for (dep, sorted_deps) {
}
CMAKE_MODULE_DEPS = $$join(mod_deps, ";")
CMAKE_QT5_MODULE_DEPS = $$join(lib_deps, ";")
+CMAKE_MODULE_LINK_FLAGS = $$join(mod_link_flags, ";")
CMAKE_INTERFACE_MODULE_DEPS = $$join(aux_mod_deps, ";")
CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";")
diff --git a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
index 3ed6dd5..a320902 100644
--- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
+++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
@@ -64,6 +64,11 @@
macro(_populate_$${CMAKE_MODULE_NAME}_target_properties Configuration
LIB_LOCATI
!!IF !isEmpty(CMAKE_LIB_SONAME)
\"IMPORTED_SONAME_${Configuration}\" \"$${CMAKE_LIB_SONAME}\"
!!ENDIF
+!!IF !isEmpty(CMAKE_STATIC_TYPE)
+!!IF !isEmpty(CMAKE_MODULE_LINK_FLAGS)
+ \"INTERFACE_LINK_OPTIONS\"
\"${_Qt5$${CMAKE_MODULE_NAME}_LIB_LINK_FLAGS}\"
+!!ENDIF
+!!ENDIF
# For backward compatibility with CMake < 2.8.12
\"IMPORTED_LINK_INTERFACE_LIBRARIES_${Configuration}\"
\"${_Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES}\"
)
@@ -215,6 +220,10 @@ if (NOT TARGET Qt5::$${CMAKE_MODULE_NAME})
!!ENDIF
!!IF !isEmpty(CMAKE_STATIC_TYPE)
+!!IF !isEmpty(CMAKE_MODULE_LINK_FLAGS)
+ set(_Qt5$${CMAKE_MODULE_NAME}_LIB_LINK_FLAGS
\"$${CMAKE_MODULE_LINK_FLAGS}\")
+
+!!ENDIF
add_library(Qt5::$${CMAKE_MODULE_NAME} STATIC IMPORTED)
set_property(TARGET Qt5::$${CMAKE_MODULE_NAME} PROPERTY
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX")
!!ELSE
-------------------------------------------------------------
I've figured out how to pass information into Qt5BasicConfig.cmake.in
from create_cmake.prf, but I'm not sure where to get the link flags
that need to be passed in. Any advice would be appreciated.
Kyle
_______________________________________________
Development mailing list
http://lists.qt-project.org/mailman/listinfo/development
Kyle Edwards
2018-10-12 20:35:34 UTC
Permalink
Post by Jean-Michaël Celerier
it would be so nice to add them ! currently I have to use the
following chthonic horror:  https://github.com/OSSIA/score/blob/maste
r/base/app/StaticApp.cmake
Yep, this is exactly what I'm trying to fix!
Kyle
Thiago Macieira
2018-10-12 21:37:39 UTC
Permalink
Post by Kyle Edwards
Post by Kyle Edwards
but there is no information for the
flags needed to link against system libraries (freetype, harfbuzz,
etc.)
it would be so nice to add them ! currently I have to use the following
https://github.com/OSSIA/score/blob/master/base/app/StaticApp.cmake
I think that in Qt the pkg-config files are generated by a perl script,
maybe the same could be used for static builds ?
syncqt only generates the headers and the .pri files that qmake uses to find
the headers.

The pkg-config files and the libtool files are generated by qmake itself.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Kyle Edwards
2018-10-16 16:02:21 UTC
Permalink
Post by Thiago Macieira
Post by Kyle Edwards
 but there is no information for the
flags needed to link against system libraries (freetype, harfbuzz,
etc.)
it would be so nice to add them ! currently I have to use the following
https://github.com/OSSIA/score/blob/master/base/app/StaticApp.cmake
I think that in Qt the pkg-config files are generated by a perl script,
maybe the same could be used for static builds ?
syncqt only generates the headers and the .pri files that qmake uses
to find 
the headers.
The pkg-config files and the libtool files are generated by qmake itself.
I've done a lot of experimentation, and I've gotten my code to look
like this:

-------------------------------------------------------------

diff --git a/mkspecs/features/create_cmake.prf
b/mkspecs/features/create_cmake.prf
index 2ed708e..abab6ea 100644
--- a/mkspecs/features/create_cmake.prf
+++ b/mkspecs/features/create_cmake.prf
@@ -180,6 +180,8 @@ CMAKE_MKSPEC = $$[QMAKE_XSPEC]
 sorted_deps = $$sort_depends(QT.$${MODULE}.depends, QT.)
 mod_deps =
 lib_deps =
+mod_link_flags =
+mod_libs =
 aux_mod_deps =
 aux_lib_deps =
 # Until CMake 3.0 is the minimum requirement of Qt 5, we need to
filter
@@ -195,8 +197,19 @@ for (dep, sorted_deps) {
         aux_lib_deps += Qt5::$$cdep
     }
 }
+for (lib, QT.$${MODULE}_private.libraries) {
+    for (flag, QMAKE_LIBS_$$upper($${lib})) {
+        contains(flag, "^-l.*$") {
+            mod_libs *= $$str_member($$flag, 2, -1)
+        } else {
+            mod_link_flags *= flag
+        }
+    }
+}
 CMAKE_MODULE_DEPS = $$join(mod_deps, ";")
 CMAKE_QT5_MODULE_DEPS = $$join(lib_deps, ";")
+CMAKE_MODULE_LINK_FLAGS = $$join(mod_link_flags, ";")
+CMAKE_MODULE_LIBS = $$join(mod_libs, ";")
 CMAKE_INTERFACE_MODULE_DEPS = $$join(aux_mod_deps, ";")
 CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";")
 
diff --git a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
index 3ed6dd5..444bf0f 100644
--- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
+++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in
@@ -67,6 +67,16 @@
macro(_populate_$${CMAKE_MODULE_NAME}_target_properties Configuration
LIB_LOCATI
         # For backward compatibility with CMake < 2.8.12
         \"IMPORTED_LINK_INTERFACE_LIBRARIES_${Configuration}\"
\"${_Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES}\"
     )
+!!IF !isEmpty(CMAKE_STATIC_TYPE)
+!!IF !isEmpty(CMAKE_MODULE_LINK_FLAGS)
+
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL \"3.13\")
+        set_target_properties(Qt5::$${CMAKE_MODULE_NAME} PROPERTIES
+            \"INTERFACE_LINK_OPTIONS\"
\"${_Qt5$${CMAKE_MODULE_NAME}_LINK_FLAGS}\"
+        )
+    endif()
+!!ENDIF
+!!ENDIF
 
 !!IF !isEmpty(CMAKE_WINDOWS_BUILD)
 !!IF isEmpty(CMAKE_LIB_DIR_IS_ABSOLUTE)
@@ -215,6 +225,26 @@ if (NOT TARGET Qt5::$${CMAKE_MODULE_NAME})
 !!ENDIF
 
 !!IF !isEmpty(CMAKE_STATIC_TYPE)
+!!IF !isEmpty(CMAKE_MODULE_LINK_FLAGS)
+    set(_Qt5$${CMAKE_MODULE_NAME}_LINK_FLAGS
\"$${CMAKE_MODULE_LINK_FLAGS}\")
+
+!!ENDIF
+!!IF !isEmpty(CMAKE_MODULE_LIBS)
+    set(_Qt5$${CMAKE_MODULE_NAME}_LIBS \"$${CMAKE_MODULE_LIBS}\")
+
+    foreach(_l ${_Qt5$${CMAKE_MODULE_NAME}_LIBS})
+        find_library(_Qt5$${CMAKE_MODULE_NAME}_${_l}_lpath ${_l})
+        if(_Qt5$${CMAKE_MODULE_NAME}_${_l}_lpath)
+            set(_Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES
+                ${_Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES}
+                ${_Qt5$${CMAKE_MODULE_NAME}_${_l}_lpath}
+            )
+        else()
+            message(FATAL_ERROR \"Could not find -l${_l}\")
+        endif()
+    endforeach()
+
+!!ENDIF
     add_library(Qt5::$${CMAKE_MODULE_NAME} STATIC IMPORTED)
     set_property(TARGET Qt5::$${CMAKE_MODULE_NAME} PROPERTY
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX")
 !!ELSE

-------------------------------------------------------------

So anything that links against a static Qt module in CMake will inherit
all of the "libs" that the module depends on. However, the downstream
targets still aren't receiving crucial libraries like -lpthread, which
is still causing link errors. Looking through the qmake scripts, it
looks like "thread" is a "feature" rather than a "lib", and I'm not
sure what would be the best approach to include "features" in the CMake
script. Would someone who's more familiar with the workings of the
qmake scripts be able to assist me here?

Kyle

Loading...