summaryrefslogtreecommitdiff
path: root/eclass/qt6-build.eclass
blob: 31bdf1e011678bfa0c1e3c46853684c37b3f4f55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# Copyright 2021-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

# @ECLASS: qt6-build.eclass
# @MAINTAINER:
# qt@gentoo.org
# @SUPPORTED_EAPIS: 8
# @PROVIDES: cmake
# @BLURB: Eclass for Qt6 split ebuilds.
# @DESCRIPTION:
# This eclass contains various functions that are used when building Qt6.

case ${EAPI} in
	8) ;;
	*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac

if [[ -z ${_QT6_BUILD_ECLASS} ]]; then
_QT6_BUILD_ECLASS=1

[[ ${CATEGORY} != dev-qt ]] &&
	die "${ECLASS} is only to be used for building Qt6"

inherit cmake flag-o-matic toolchain-funcs
[[ ${EAPI} == 8 ]] && inherit eapi9-pipestatus

# @ECLASS_VARIABLE: QT6_BUILD_TYPE
# @DESCRIPTION:
# Read only variable set based on PV to one of:
#  - release: official 6.x.x releases
#  - pre-release: development 6.x.x_rc/beta/alpha releases
#  - live: *.9999 (dev branch), 6.x.9999 (stable branch)

# @ECLASS_VARIABLE: QT6_MODULE
# @PRE_INHERIT
# @DESCRIPTION:
# The upstream name of the module this package belongs to.
# Used for SRC_URI and EGIT_REPO_URI.
: "${QT6_MODULE:=${PN}}"

# @ECLASS_VARIABLE: QT6_RESTRICT_TESTS
# @DEFAULT_UNSET
# @PRE_INHERIT
# @DESCRIPTION:
# If set to a non-empty value, will not add IUSE="test" and set
# RESTRICT="test" instead.  Primarily intended for ebuilds where
# running tests is unmaintained (or missing) rather than just
# temporarily restricted not to have a broken USE (bug #930266).

if [[ ${PV} == *.9999 ]]; then
	inherit git-r3
	EGIT_REPO_URI=(
		"https://code.qt.io/qt/${QT6_MODULE}.git"
		"https://github.com/qt/${QT6_MODULE}.git"
	)

	QT6_BUILD_TYPE=live
	EGIT_BRANCH=dev
	[[ ${PV} == 6.*.9999 ]] && EGIT_BRANCH=${PV%.9999}
else
	QT6_BUILD_TYPE=release
	_QT6_SRC=official
	_QT6_SUBDIR=

	if [[ ${PV} == *_@(alpha|beta|rc)* ]]; then
		QT6_BUILD_TYPE=pre-release
		_QT6_SRC=development

		# TODO?: drop _QT6_SUBDIR if no longer used for 6.9, unknown
		# if this was a one-time mistake or a permanent change
		ver_test ${PV} -ge 6.8 && _QT6_SUBDIR=src/
	fi

	_QT6_P=${QT6_MODULE}-everywhere-src-${PV/_/-}
	SRC_URI="https://download.qt.io/${_QT6_SRC}_releases/qt/${PV%.*}/${PV/_/-}/${_QT6_SUBDIR}submodules/${_QT6_P}.tar.xz"
	S=${WORKDIR}/${_QT6_P}

	unset _QT6_P _QT6_SRC _QT6_SUBDIR
fi
readonly QT6_BUILD_TYPE

HOMEPAGE="https://www.qt.io/"
LICENSE="|| ( GPL-2 GPL-3 LGPL-3 ) FDL-1.3"
SLOT="6/${PV%%_*}"
IUSE="custom-cflags"

if [[ ${QT6_RESTRICT_TESTS} ]]; then
	RESTRICT="test"
else
	IUSE+=" test"
	RESTRICT="!test? ( test )"
fi

BDEPEND="
	dev-lang/perl
	virtual/pkgconfig
"

######  Phase functions  ######

# @FUNCTION: qt6-build_src_unpack
# @DESCRIPTION:
# Run git-r3_src_unpack if needed (live), then default to unpack
# e.g. patchsets in live ebuilds.
qt6-build_src_unpack() {
	[[ ${QT6_BUILD_TYPE} == live ]] && git-r3_src_unpack

	default
}

# @FUNCTION: qt6-build_src_prepare
# @DESCRIPTION:
# Run cmake_src_prepare, prepare the environment (such as set
# QT6_PREFIX, QT6_LIBDIR, and others), and handle anything else
# generic as needed.
qt6-build_src_prepare() {
	cmake_src_prepare

	if [[ -e CMakeLists.txt ]]; then
		# throw an error rather than skip if *required* conditions are not met
		sed -e '/message(NOTICE.*Skipping/s/NOTICE/FATAL_ERROR/' \
			-i CMakeLists.txt || die
	fi

	if in_iuse test && use test && [[ -e tests/auto/CMakeLists.txt ]]; then
		# .cmake files tests causing a self-dependency in many modules,
		# and that sometimes install additional test junk
		sed -i '/add_subdirectory(cmake)/d' tests/auto/CMakeLists.txt || die
	fi

	_qt6-build_prepare_env

	if use !custom-cflags; then
		_qt6-build_sanitize_cpu_flags

		# LTO cause test failures in several components (e.g. qtcharts,
		# multimedia, scxml, wayland, webchannel, ...).
		#
		# Exact extent/causes unknown, but for some related-sounding bugs:
		# https://bugreports.qt.io/browse/QTBUG-112332
		# https://bugreports.qt.io/browse/QTBUG-115731
		#
		# Does not manifest itself with clang:16 (did with gcc-13.2.0), but
		# still assumed to be generally unsafe either way in current state.
		filter-lto
	fi
}

# @FUNCTION: qt6-build_src_configure
# @DESCRIPTION:
# Run cmake_src_configure and handle anything else generic as needed.
qt6-build_src_configure() {
	if [[ ${PN} == qttranslations ]]; then
		# does not compile anything, further options would be unrecognized
		cmake_src_configure
		return
	fi

	local defaultcmakeargs=(
		# cmake defaults to "STATUS" but Qt changes that to "NOTICE" which
		# hides a lot of information that is useful for bug reports
		--log-level=STATUS
		# ...but dev messages are noisy and not really useful downstream
		-Wno-dev
		# see _qt6-build_create_user_facing_links
		-DINSTALL_PUBLICBINDIR="${QT6_PREFIX}"/bin
		# note that if qtbase was built with tests, this is default ON
		-DQT_BUILD_TESTS=$(in_iuse test && use test && echo ON || echo OFF)
		# avoid appending -O2 after user's C(XX)FLAGS (bug #911822)
		-DQT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS=ON
	)

	if [[ ${mycmakeargs@a} == *a* ]]; then
		local mycmakeargs=("${defaultcmakeargs[@]}" "${mycmakeargs[@]}")
	else
		local mycmakeargs=("${defaultcmakeargs[@]}")
	fi

	cmake_src_configure
}

# @FUNCTION: qt6-build_src_test
# @USAGE: [<cmake_src_test argument>...]
# @DESCRIPTION:
# Run cmake_src_test and handle anything else generic as-needed.
qt6-build_src_test() {
	local -x QML_IMPORT_PATH=${BUILD_DIR}${QT6_QMLDIR#"${QT6_PREFIX}"}
	local -x QTEST_FUNCTION_TIMEOUT=900000 #914737
	local -x QT_QPA_PLATFORM=offscreen

	# TODO?: CMAKE_SKIP_TESTS skips a whole group of tests and, when
	# only want to skip a sepcific sub-test, the BLACKLIST files
	# could potentially be modified by implementing a QT6_SKIP_TESTS

	cmake_src_test "${@}"
}

# @FUNCTION: qt6-build_src_install
# @DESCRIPTION:
# Run cmake_src_install and handle anything else generic as needed.
qt6-build_src_install() {
	cmake_src_install

	_qt6-build_create_user_facing_links

	# hack: trim typical junk with currently no known "proper" way
	# to avoid that primarily happens with tests (e.g. qt5compat and
	# qtsvg tests, but qtbase[gui,-test] currently does some too)
	rm -rf -- "${D}${QT6_PREFIX}"/tests \
		"${D}${QT6_LIBDIR}/objects-${CMAKE_BUILD_TYPE}" || die
}

######  Public helpers  ######

# @FUNCTION: qt_feature
# @USAGE: <flag> [feature]
# @DESCRIPTION:
# <flag> is the name of a flag in IUSE.
qt_feature() {
	[[ ${#} -ge 1 ]] || die "${FUNCNAME}() requires at least one argument"

	echo "-DQT_FEATURE_${2:-${1}}=$(usex ${1} ON OFF)"
}

######  Internal functions  ######

# @FUNCTION: _qt6-build_create_user_facing_links
# @INTERNAL
# @DESCRIPTION:
# Create links for user facing tools (bug #863395) as suggested in:
# https://doc.qt.io/qt-6/packaging-recommendations.html
_qt6-build_create_user_facing_links() {
	# user_facing_tool_links.txt is always created (except for qttranslations)
	# even if no links (empty), if missing will assume that it is an error
	[[ ${PN} == qttranslations ]] && return

	# loop and match using paths (upstream suggests `xargs ln -s < ${links}`
	# but, for what it is worth, that will fail if paths have spaces)
	local link
	while IFS= read -r link; do
		if [[ -z ${link} ]]; then
			continue
		elif [[ ${link} =~ ^("${QT6_PREFIX}"/.+)\ ("${QT6_PREFIX}"/bin/.+) ]]
		then
			dosym -r "${BASH_REMATCH[1]#"${EPREFIX}"}" \
				"${BASH_REMATCH[2]#"${EPREFIX}"}"
		else
			die "unrecognized line '${link}' in '${links}'"
		fi
	done < "${BUILD_DIR}"/user_facing_tool_links.txt || die
}

# @FUNCTION: _qt6-build_prepare_env
# @INTERNAL
# @DESCRIPTION:
# Prepares the environment for building Qt.
_qt6-build_prepare_env() {
	# setup installation directories
	# note: keep paths in sync with qmake-utils.eclass
	readonly QT6_PREFIX=${EPREFIX}/usr
	readonly QT6_DATADIR=${QT6_PREFIX}/share/qt6
	readonly QT6_LIBDIR=${QT6_PREFIX}/$(get_libdir)

	readonly QT6_ARCHDATADIR=${QT6_LIBDIR}/qt6

	readonly QT6_BINDIR=${QT6_ARCHDATADIR}/bin
	readonly QT6_DOCDIR=${QT6_PREFIX}/share/qt6-doc
	readonly QT6_EXAMPLESDIR=${QT6_DATADIR}/examples
	readonly QT6_HEADERDIR=${QT6_PREFIX}/include/qt6
	readonly QT6_IMPORTDIR=${QT6_ARCHDATADIR}/imports
	readonly QT6_LIBEXECDIR=${QT6_ARCHDATADIR}/libexec
	readonly QT6_MKSPECSDIR=${QT6_ARCHDATADIR}/mkspecs
	readonly QT6_PLUGINDIR=${QT6_ARCHDATADIR}/plugins
	readonly QT6_QMLDIR=${QT6_ARCHDATADIR}/qml
	readonly QT6_SYSCONFDIR=${EPREFIX}/etc/xdg
	readonly QT6_TRANSLATIONDIR=${QT6_DATADIR}/translations
}

# @FUNCTION: _qt6-build_sanitize_cpu_flags
# @INTERNAL
# @DESCRIPTION:
# Qt hardly supports use of -mno-* or -march=native for unusual CPUs
# (or VMs) that support incomplete x86-64 feature levels among other
# issues such as CPUs with buggy rdrand, and attempts to allow this
# anyway has worked poorly.  This strips CPU instructions related
# -m* flags and replaces with the highest -march=x86-64-v* usable as
# a non-ideal solution (bug #898644,#908420,#913400,#922498,#933374).
_qt6-build_sanitize_cpu_flags() {
	# less of an issue with non-amd64, will revisit only if needed
	use amd64 || return 0

	local cpuflags=(
		# ideally update whenever compilers gain new ones, or could
		# be offloaded to flag-o-matic if needed by other ebuilds
		# grep -B 1 'Var(ix86_isa_flags' gcc/config/i386/i386.opt | grep ^m | grep -Ev '^m(16|32|64|x32)'
		3dnow 3dnowa abm adx aes amx-avx512 amx-bf16 amx-complex
		amx-fp16 amx-fp8 amx-int8 amx-movrs amx-tf32 amx-tile
		amx-transpose apxf avx avx10.1-256 avx10.1-512 avx10.2-256
		avx10.2-512 avx2 avx512bf16 avx512bitalg avx512bw avx512cd
		avx512dq avx512f avx512fp16 avx512ifma avx512vbmi avx512vbmi2
		avx512vl avx512vnni avx512vp2intersect avx512vpopcntdq avxifma
		avxneconvert avxvnni avxvnniint16 avxvnniint8 bmi bmi2 cldemote
		clflushopt clwb clzero cmpccxadd crc32 cx16 enqcmd evex512 f16c
		fma fma4 fsgsbase fxsr gfni hle hreset kl lwp lzcnt mmx movbe
		movdir64b movdiri movrs mwait mwaitx pclmul pconfig pku popcnt
		prefetchi prfchw ptwrite raoint rdpid rdrnd rdseed rtm sahf
		serialize sgx sha sha512 shstk sm3 sm4 sse sse2 sse3 sse4
		sse4.1 sse4.2 sse4a ssse3 tbm tsxldtrk uintr usermsr vaes
		vpclmulqdq waitpkg wbnoinvd widekl xop xsave xsavec xsaveopt
		xsaves
	)

	# determine and the highest(known) usable x86-64 feature level
	# so users will not lose *all* CPU-specific optimizations
	local march=$(
		$(tc-getCXX) -E -P ${CXXFLAGS} ${CPPFLAGS} - <<-EOF | tail -n 1
			default
			#if (__CRC32__ + __LAHF_SAHF__ + __POPCNT__ + __SSE3__ + __SSE4_1__ + __SSE4_2__ + __SSSE3__) == 7
			x86-64-v2
			#  if (__AVX__ + __AVX2__ + __BMI__ + __BMI2__ + __F16C__ + __FMA__ + __LZCNT__ + __MOVBE__ + __XSAVE__) == 9
			x86-64-v3
			#    if (__AVX512BW__ + __AVX512CD__ + __AVX512DQ__ + __AVX512F__ + __AVX512VL__ + __EVEX256__ + __EVEX512__) == 7
			x86-64-v4
			#    endif
			#  endif
			#endif
		EOF
		pipestatus || die
	)

	filter-flags '-march=*' "${cpuflags[@]/#/-m}" "${cpuflags[@]/#/-mno-}"
	[[ ${march} == x86-64* ]] && append-flags $(test-flags-CXX -march="${march}")
	einfo "C(XX)FLAGS adjusted due to frequent -march=*/-m* issues with Qt:"
	einfo "    \"${CXXFLAGS}\""
	einfo "(can override with USE=custom-cflags, but no support will be given)"
}

fi

EXPORT_FUNCTIONS src_unpack src_prepare src_configure src_test src_install