Skip to content

Commit

Permalink
Add qt_host option to enable cross-platform Qt Builds using host SD…
Browse files Browse the repository at this point in the history
…K tools
  • Loading branch information
Doekin committed Jan 22, 2025
1 parent c61d7bb commit 2857d30
Show file tree
Hide file tree
Showing 17 changed files with 102 additions and 70 deletions.
64 changes: 55 additions & 9 deletions xmake/modules/detect/sdks/find_qt.lua
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,21 @@ function _find_qmake(sdkdir, sdkver)
end

-- get qt environment
function _get_qtenvs(qmake)
function _get_qtenvs(qmake, sdkdir)
local envs = {}
local run_args = {"-query"}
if sdkdir then
local conf_paths = {path.join(sdkdir, "bin", "target_qt.conf"), path.join(sdkdir, "bin", "qt.conf")}
for _, conf_path in ipairs(conf_paths) do
if os.isfile(conf_path) then
table.join2(run_args, {"-qtconf", conf_path})
break
end
end
end
local results = try {
function ()
return os.iorunv(qmake, {"-query"})
return os.iorunv(qmake, run_args)
end,
catch {
function (errors)
Expand All @@ -215,24 +225,43 @@ function _get_qtenvs(qmake)
end
end

-- Verify and correct the Qt SDK version for cross-compiling.
-- qmake reports its own version (QT_VERSION), not the version specified in the SDK's configuration files.
function _tryfix_sdkver_for_cross(sdkdir, sdkver)
local qconfig_path = sdkdir and path.join(sdkdir, "mkspecs", "qconfig.pri")
if not sdkver or not os.isfile(qconfig_path) then
return sdkver
end
-- Extract the actual SDK version from qconfig.pri
local actual_sdkver = io.readfile(qconfig_path):match("QT_VERSION%s*=%s*(%S+)") -- Expected format: QT_VERSION = x.y.z
if not actual_sdkver then
return sdkver
end
if sdkver ~= actual_sdkver then
wprint("Host Qt SDK version (%s) differs from Target Qt SDK version (%s). To prevent build issues, please ensure both use the same version.", sdkver, actual_sdkver);
end
return actual_sdkver
end

-- find qt sdk toolchains
function _find_qt(sdkdir, sdkver)
function _find_qt(sdkdir, sdkver, sdkdir_host)

-- find qmake
local qmake = _find_qmake(sdkdir, sdkver)
local qmake = _find_qmake(sdkdir_host or sdkdir, sdkver)
if not qmake then
return
end

-- get qt environments
local qtenvs = _get_qtenvs(qmake)
local located_sdkdir = sdkdir and _find_sdkdir(sdkdir, sdkver)
local qtenvs = _get_qtenvs(qmake, located_sdkdir or sdkdir)
if not qtenvs then
return
end

-- get qt toolchains
sdkdir = qtenvs.QT_INSTALL_PREFIX
local sdkver = qtenvs.QT_VERSION
local sdkver = _tryfix_sdkver_for_cross(sdkdir, qtenvs.QT_VERSION)
local bindir = qtenvs.QT_INSTALL_BINS
local libexecdir = qtenvs.QT_INSTALL_LIBEXECS
local qmldir = qtenvs.QT_INSTALL_QML
Expand All @@ -259,6 +288,16 @@ function _find_qt(sdkdir, sdkver)
-- TODO
end
end

if sdkdir_host then
local located_sdkdir_host = _find_sdkdir(sdkdir_host, sdkver)
local qtenvs_host = _get_qtenvs(qmake, located_sdkdir_host or sdkdir_host)
if qtenvs_host then
bindir_host = qtenvs_host.QT_HOST_BINS or qtenvs_host.QT_INSTALL_BINS or bindir_host
libexecdir_host = qtenvs_host.QT_HOST_LIBEXECS or qtenvs_host.QT_INSTALL_LIBEXECS or libexecdir_host
end
end

return {sdkdir = sdkdir, bindir = bindir, bindir_host = bindir_host, libexecdir = libexecdir, libexecdir_host = libexecdir_host, libdir = libdir, includedir = includedir, qmldir = qmldir, pluginsdir = pluginsdir, mkspecsdir = mkspecsdir, sdkver = sdkver}
end

Expand All @@ -282,13 +321,16 @@ function main(sdkdir, opt)

-- attempt to load cache first
local key = "detect.sdks.find_qt"
local cacheinfo = detectcache:get(key) or {}
local cacheinfo = (sdkdir and detectcache:get2(key, sdkdir)) or detectcache:get(key) or {}
if not opt.force and cacheinfo.qt and cacheinfo.qt.sdkdir and os.isdir(cacheinfo.qt.sdkdir) then
return cacheinfo.qt
end

-- find qt
local qt = _find_qt(sdkdir or config.get("qt") or global.get("qt") or config.get("sdk"), opt.version or config.get("qt_sdkver"))
local sdkdir = sdkdir or config.get("qt") or global.get("qt") or config.get("sdk")
local sdkver = opt.version or config.get("qt_sdkver")
local sdkdir_host = opt.sdkdir_host or config.get("qt_host") or global.get("qt_host")
local qt = _find_qt(sdkdir, sdkver, sdkdir_host)
if qt then

-- save to config
Expand All @@ -314,7 +356,11 @@ function main(sdkdir, opt)

-- save to cache
cacheinfo.qt = qt or false
detectcache:set(key, cacheinfo)
if sdkdir then
detectcache:set2(key, sdkdir, cacheinfo)
else
detectcache:set(key, cacheinfo)
end
detectcache:save()
return qt
end
2 changes: 2 additions & 0 deletions xmake/platforms/bsd/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ platform("bsd")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {nil, "qt_sdkver", "kv", "auto", "The Qt SDK Version" }
}

Expand All @@ -48,6 +49,7 @@ platform("bsd")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
}
}

Expand Down
2 changes: 2 additions & 0 deletions xmake/platforms/haiku/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ platform("haiku")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {nil, "qt_sdkver", "kv", "auto", "The Qt SDK Version" }
}

Expand All @@ -46,6 +47,7 @@ platform("haiku")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
}
}

Expand Down
2 changes: 2 additions & 0 deletions xmake/platforms/linux/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ platform("linux")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {nil, "qt_sdkver", "kv", "auto", "The Qt SDK Version" }
, {category = "Vcpkg Configuration" }
, {nil, "vcpkg", "kv", "auto", "The Vcpkg Directory" }
Expand All @@ -49,6 +50,7 @@ platform("linux")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {category = "Vcpkg Configuration" }
, {nil, "vcpkg", "kv", "auto", "The Vcpkg Directory" }
}
Expand Down
2 changes: 2 additions & 0 deletions xmake/platforms/macosx/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ platform("macosx")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {nil, "qt_sdkver", "kv", "auto", "The Qt SDK Version" }
, {category = "Vcpkg Configuration" }
, {nil, "vcpkg", "kv", "auto", "The Vcpkg Directory" }
Expand All @@ -63,6 +64,7 @@ platform("macosx")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {category = "Vcpkg Configuration" }
, {nil, "vcpkg", "kv", "auto", "The Vcpkg Directory" }
}
Expand Down
2 changes: 2 additions & 0 deletions xmake/platforms/windows/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ platform("windows")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {nil, "qt_sdkver", "kv", "auto", "The Qt SDK Version" }
, {category = "WDK Configuration" }
, {nil, "wdk", "kv", "auto", "The WDK Directory" }
Expand All @@ -71,6 +72,7 @@ platform("windows")
, {nil, "cuda", "kv", "auto", "The Cuda SDK Directory" }
, {category = "Qt SDK Configuration" }
, {nil, "qt", "kv", "auto", "The Qt SDK Directory" }
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
, {category = "WDK Configuration" }
, {nil, "wdk", "kv", "auto", "The WDK Directory" }
, {category = "Vcpkg Configuration" }
Expand Down
15 changes: 3 additions & 12 deletions xmake/rules/qt/deploy/android.lua
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,7 @@ function main(target, opt)
end

-- get androiddeployqt
local androiddeployqt = path.join(qt.bindir, "androiddeployqt" .. (is_host("windows") and ".exe" or ""))
if not os.isexec(androiddeployqt) and qt.bindir_host then
androiddeployqt = path.join(qt.bindir_host, "androiddeployqt" .. (is_host("windows") and ".exe" or ""))
end
local androiddeployqt = find_file("androiddeployqt" .. (is_host("windows") and ".exe" or ""), {qt.bindir_host, qt.bindir})
assert(os.isexec(androiddeployqt), "androiddeployqt not found!")

-- get working directory
Expand Down Expand Up @@ -152,18 +149,12 @@ function main(target, opt)
settings_file:print(' "target-architecture": "%s",', target_arch)
settings_file:print(' "qml-root-path": "%s",', _escape_path(os.projectdir()))
-- for 6.2.x
local qmlimportscanner = path.join(qt.libexecdir, "qmlimportscanner")
if not os.isexec(qmlimportscanner) and qt.libexecdir_host then
qmlimportscanner = path.join(qt.libexecdir_host, "qmlimportscanner")
end
local qmlimportscanner = find_file("qmlimportscanner" .. (is_host("windows") and ".exe" or ""), {qt.libexecdir_host, qt.libexecdir})
if os.isexec(qmlimportscanner) then
settings_file:print(' "qml-importscanner-binary": "%s",', _escape_path(qmlimportscanner))
end
-- for 6.3.x
local rcc = path.join(qt.bindir, "rcc")
if not os.isexec(rcc) and qt.bindir_host then
rcc = path.join(qt.bindir_host, "rcc")
end
local rcc = find_file("rcc" .. (is_host("windows") and ".exe" or ""), {qt.bindir_host, qt.bindir})
if os.isexec(rcc) then
settings_file:print(' "rcc-binary": "%s",', _escape_path(rcc))
end
Expand Down
3 changes: 2 additions & 1 deletion xmake/rules/qt/deploy/macosx.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import("core.base.option")
import("core.project.config")
import("core.project.depend")
import("core.tool.toolchain")
import("lib.detect.find_file")
import("lib.detect.find_path")
import("detect.sdks.find_qt")
import("utils.progress")
Expand Down Expand Up @@ -91,7 +92,7 @@ function main(target, opt)
local qt = assert(find_qt(), "Qt SDK not found!")

-- get macdeployqt
local macdeployqt = path.join(qt.bindir, "macdeployqt")
local macdeployqt = find_file("macdeployqt" .. (is_host("windows") and ".exe" or ""), {qt.bindir_host, qt.bindir})
assert(os.isexec(macdeployqt), "macdeployqt not found!")

-- generate target app
Expand Down
6 changes: 5 additions & 1 deletion xmake/rules/qt/env/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ rule("qt.env")

local qmlimportpath = target:values("qt.env.qmlimportpath") or {}
if target:is_plat("windows") or (target:is_plat("mingw") and is_host("windows")) then
target:add("runenvs", "PATH", qt.bindir)
for _, dir in ipairs({qt.bindir_host, qt.bindir}) do
if dir then
target:add("runenvs", "PATH", dir)
end
end
table.insert(qmlimportpath, qt.qmldir)
-- add targetdir in QML2_IMPORT_PATH in case of the user have qml plugins
table.insert(qmlimportpath, target:targetdir())
Expand Down
3 changes: 2 additions & 1 deletion xmake/rules/qt/install/mingw.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import("core.base.option")
import("core.project.config")
import("core.tool.toolchain")
import("lib.detect.find_path")
import("lib.detect.find_file")
import("detect.sdks.find_qt")

-- get install directory
Expand All @@ -42,7 +43,7 @@ function main(target, opt)
local qt = assert(find_qt(), "Qt SDK not found!")

-- get windeployqt
local windeployqt = path.join(qt.bindir, "windeployqt.exe")
local windeployqt = find_file("windeployqt" .. (is_host("windows") and ".exe" or ""), {qt.bindir_host, qt.bindir})
assert(os.isexec(windeployqt), "windeployqt.exe not found!")

-- find qml directory
Expand Down
7 changes: 4 additions & 3 deletions xmake/rules/qt/install/windows.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import("core.base.option")
import("core.project.config")
import("core.tool.toolchain")
import("lib.detect.find_file")
import("lib.detect.find_path")
import("detect.sdks.find_qt")

Expand All @@ -42,7 +43,7 @@ function main(target, opt)
local qt = assert(find_qt(), "Qt SDK not found!")

-- get windeployqt
local windeployqt = path.join(qt.bindir, "windeployqt.exe")
local windeployqt = find_file("windeployqt" .. (is_host("windows") and ".exe" or ""), {qt.bindir_host, qt.bindir})
assert(os.isexec(windeployqt), "windeployqt.exe not found!")

-- find qml directory
Expand Down Expand Up @@ -73,9 +74,9 @@ function main(target, opt)
end
-- bind qt bin path
-- https://github.com/xmake-io/xmake/issues/4297
if qt.bindir then
if qt.bindir_host or qt.bindir then
envs = envs or {}
envs.PATH = {qt.bindir}
envs.PATH = {qt.bindir_host, qt.bindir}
local curpath = os.getenv("PATH")
if curpath then
table.join2(envs.PATH, path.splitenv(curpath))
Expand Down
6 changes: 5 additions & 1 deletion xmake/rules/qt/load.lua
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,11 @@ function main(target, opt)
target:add("linkdirs", qt.libdir)
target:add("syslinks", "ws2_32", "gdi32", "ole32", "advapi32", "shell32", "user32", "opengl32", "imm32", "winmm", "iphlpapi")
-- for debugger, https://github.com/xmake-io/xmake-vscode/issues/225
target:add("runenvs", "PATH", qt.bindir)
for _, dir in ipairs({qt.bindir_host, qt.bindir}) do
if dir then
target:add("runenvs", "PATH", dir)
end
end
elseif target:is_plat("mingw") then
target:set("frameworks", nil)
-- we need to fix it, because gcc maybe does not work on latest mingw when `-isystem D:\a\_temp\msys64\mingw64\include` is passed.
Expand Down
9 changes: 2 additions & 7 deletions xmake/rules/qt/moc/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,11 @@ rule("qt.moc")
set_extensions(".h", ".hpp")
before_buildcmd_file(function (target, batchcmds, sourcefile, opt)
import("core.tool.compiler")
import("lib.detect.find_file")

-- get moc
local qt = assert(target:data("qt"), "Qt not found!")
local moc = path.join(qt.bindir, is_host("windows") and "moc.exe" or "moc")
if not os.isexec(moc) and qt.libexecdir then
moc = path.join(qt.libexecdir, is_host("windows") and "moc.exe" or "moc")
end
if not os.isexec(moc) and qt.libexecdir_host then
moc = path.join(qt.libexecdir_host, is_host("windows") and "moc.exe" or "moc")
end
local moc = find_file(is_host("windows") and "moc.exe" or "moc"), {qt.bindir_host, qt.libexecdir_host, qt.bindir, qt.libexecdir})
assert(moc and os.isexec(moc), "moc not found!")

-- get c++ source file for moc
Expand Down
10 changes: 3 additions & 7 deletions xmake/rules/qt/qmltyperegistrar/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,13 @@ rule("qt.qmltyperegistrar")
set_extensions(".h", ".hpp")

on_config(function(target)
import("lib.detect.find_file")

-- get qt
local qt = assert(target:data("qt"), "Qt not found!")

-- get qmltyperegistrar
local qmltyperegistrar = path.join(qt.bindir, is_host("windows") and "qmltyperegistrar.exe" or "qmltyperegistrar")
if not os.isexec(qmltyperegistrar) and qt.libexecdir then
qmltyperegistrar = path.join(qt.libexecdir, is_host("windows") and "qmltyperegistrar.exe" or "qmltyperegistrar")
end
if not os.isexec(qmltyperegistrar) and qt.libexecdir_host then
qmltyperegistrar = path.join(qt.libexecdir_host, is_host("windows") and "qmltyperegistrar.exe" or "qmltyperegistrar")
end
local qmltyperegistrar = find_file(is_host("windows") and "qmltyperegistrar.exe" or "qmltyperegistrar"), {qt.bindir_host, qt.libexecdir_host, qt.bindir, qt.libexecdir})
assert(qmltyperegistrar and os.isexec(qmltyperegistrar), "qmltyperegistrar not found!")

-- set targetdir
Expand Down
9 changes: 2 additions & 7 deletions xmake/rules/qt/qrc/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,11 @@ rule("qt.qrc")
add_deps("qt.env")
set_extensions(".qrc")
on_config(function (target)
import("lib.detect.find_file")

-- get rcc
local qt = assert(target:data("qt"), "Qt not found!")
local rcc = path.join(qt.bindir, is_host("windows") and "rcc.exe" or "rcc")
if not os.isexec(rcc) and qt.libexecdir then
rcc = path.join(qt.libexecdir, is_host("windows") and "rcc.exe" or "rcc")
end
if not os.isexec(rcc) and qt.libexecdir_host then
rcc = path.join(qt.libexecdir_host, is_host("windows") and "rcc.exe" or "rcc")
end
local rcc = find_file(is_host("windows") and "rcc.exe" or "rcc"), {qt.bindir_host, qt.libexecdir_host, qt.bindir, qt.libexecdir})
assert(os.isexec(rcc), "rcc not found!")

-- save rcc
Expand Down
Loading

0 comments on commit 2857d30

Please sign in to comment.