Heresy 從去年年初開始玩 Gitlab CI/CD 一段時間後,就開始試著把建置環境移動到 Docker(官網)上。在 Linux 上的問題不算大,但是在 Windows 上卻常常碰到一些問題(參考一、參考二);前一陣子,甚至因為微軟自己的安全性更新,搞到整個不能用(參考)…
雖然說零零星星有些問題,但是大致上都還是可以使用的;在可以正常運作的情況下,感覺也算是相當地方便~
不過,對 Heresy 來說,這部分還有一個很大的問題,就是 Qt SDK(官網)。
由於 Qt 本身的安裝程式框架(Qt Installer Framework、QTIFW、官方文件)是有支援使用腳本(script)來做控制的(官方文件),所以技術上的確是可以在只有命令提示字元的情況下,做到自動化的安裝。
Heresy 之前在《在 Windows 命令提示字元安裝 Qt SDK》一文中,也有整理當時可以用的安裝方法。
本來以為這樣就沒事了?結果沒想到過了一個月,Qt 更新的安裝程式就多了一些選項,導致既有的安裝腳本無法運作了…這部分 Heresy 也有寫在《Qt 安裝腳本更新》裡。
當時 Heresy 其實就已經認知到了,如果要維持 Qt SDK 在最新版的話,那看來這個安裝腳本得常常修改了…
果不其然,在前一陣子因為 Windows 2020/02 安全性更新、必須要重新建置 Docker images 的時候(參考),Qt 的安裝就完全卡住了… orz
查了一下資料,可以發現這個問題是因為 Qt 從 2020 開始,就強制要求使用者需要有 Qt 的帳號、並且在安裝時需要登入的關係…而離線安裝似乎也將會變成是商業授權才提供的方案了。
(官方公告《Qt offering changes 2020》、另一個潛在的問題是以後 LTS 是商業授權才有的了)
而這也導致了「離線安裝程式」也需要「上線登入」才能使用的搞笑狀況…
總之,解決方法呢?Heresy 這邊是認命去註冊了一個 Qt 的帳號,然後在本機執行了一次安裝程式、並且進行登入;這樣在電腦裡面,就會建立出「qtaccount.ini」這個儲存使用者帳號資訊的檔案。
他的預設路徑是在
C:UsersHeresyAppDataRoamingQtqtaccount.ini
如果要快速進入,可以直接在檔案總管使用「%APPDATA%Qt」這個路徑進去。
而在 Docker 內安裝的時候,他會去找對應路徑下的檔案,所以必須要將這個檔案放到:
C:/Users/ContainerAdministrator/AppData/Roaming/Qt/qtlicenses.ini
如此一來,Qt SDK Installer 就可以讀取這邊的資訊,而不需要使用者輸入帳號密碼了。
(qtaccount.ini 內的不會儲存密碼,而是儲存處理過的登入資訊,所以相對安全一點)
所以,如果是要在 .Net Framework SDK 的基礎影像上安裝 Qt SDK 的話,Dockerfile 大致上可以寫成下面的樣子:
# escape=` # Use the latest Windows Server Core image with .NET Framework 4.8. FROM mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-1909 # Restore the default Windows shell for correct batch processing. SHELL ["cmd", "/S", "/C"] # Install Qt 5 ADD http://download.qt.io/official_releases/online_installers/qt-unified-windows-x86-online.exe C:TEMPqt.exe ADD qt-install.qs C:TEMPqt-install.qs ADD qtaccount.ini C:UsersContainerAdministratorAppDataRoamingQtqtaccount.ini RUN C:TEMPqt.exe -v --script C:TEMPqt-install.qs # env setting ENV QTDIR C:Qt5.13.1msvc2017_64 # clean download files RUN del C:TEMP* /q # Start developer command prompt with any other commands specified. ENTRYPOINT c:BuildToolsVCAuxiliaryBuildvcvarsall.bat x64 &&
其中,qt-install.qs 這個安裝的腳本和之前的版本比起來,也又需要再做一些修改了。
目前 Heresy 這邊使用的安裝腳本檔案是:
function Controller() {
installer.autoRejectMessageBoxes();
installer.setMessageBoxAutomaticAnswer("installationError", QMessageBox.Retry);
installer.setMessageBoxAutomaticAnswer("installationErrorWithRetry", QMessageBox.Retry);
installer.setMessageBoxAutomaticAnswer("DownloadError", QMessageBox.Retry);
installer.setMessageBoxAutomaticAnswer("archiveDownloadError", QMessageBox.Retry);
installer.installationFinished.connect(function() {
gui.clickButton(buttons.NextButton);
})} Controller.prototype.WelcomePageCallback = function() {
gui.clickButton(buttons.NextButton, 3000); } Controller.prototype.DynamicTelemetryPluginFormCallback = function() {
gui.currentPageWidget().TelemetryPluginForm.statisticGroupBox.disableStatisticRadioButton.checked = true;
gui.clickButton(buttons.NextButton, 3000); } Controller.prototype.CredentialsPageCallback = function() {
gui.clickButton(buttons.NextButton); }Controller.prototype.ObligationsPageCallback = function() {Controller.prototype.IntroductionPageCallback = function() {
var page = gui.pageWidgetByObjectName("ObligationsPage");
page.obligationsAgreement.setChecked(true);
page.completeChanged();
gui.clickButton(buttons.NextButton); }
gui.clickButton(buttons.NextButton); } Controller.prototype.TargetDirectoryPageCallback = function() {
gui.currentPageWidget().TargetDirectoryLineEdit.setText("C:/Qt/");
gui.clickButton(buttons.NextButton); }Controller.prototype.PerformInstallationPageCallback = function() {Controller.prototype.ComponentSelectionPageCallback = function() {
gui.clickButton(buttons.CommitButton); }
var page = gui.pageWidgetByObjectName("ComponentSelectionPage");
var archiveCheckBox = gui.findChild(page, "Archive");
var latestCheckBox = gui.findChild(page, "Latest releases");
var fetchButton = gui.findChild(page, "FetchCategoryButton");
archiveCheckBox.click();
latestCheckBox.click();
fetchButton.click();
var widget = gui.currentPageWidget();
widget.deselectAll();
widget.selectComponent("qt.qt5.5131.win64_msvc2017_64");
gui.clickButton(buttons.NextButton); } Controller.prototype.LicenseAgreementPageCallback = function() {
gui.currentPageWidget().AcceptLicenseRadioButton.setChecked(true);
gui.clickButton(buttons.NextButton); } Controller.prototype.StartMenuDirectoryPageCallback = function() {
gui.clickButton(buttons.NextButton); } Controller.prototype.ReadyForInstallationPageCallback = function(){
gui.clickButton(buttons.NextButton); } Controller.prototype.FinishedPageCallback = function() {
var checkBoxForm = gui.currentPageWidget().LaunchQtCreatorCheckBoxForm
if (checkBoxForm && checkBoxForm.launchQtCreatorCheckBox) {
checkBoxForm.launchQtCreatorCheckBox.checked = false;
}
gui.clickButton(buttons.FinishButton); }
和之前的版本相比,主要是加入了一些錯誤自動重試,以及 ObligationsPageCallback 和 PerformInstallationPageCallback 這兩個函式。
理論上,這樣就可以正確地在微軟提供的 .Net Framework SDK 的 Docker 基礎影像上、安裝 Qt 5.13 的 MSVC 2017 x64 版 SDK 了。
這部分的檔案可以參考:https://github.com/KHeresy/QtSDK-WindowsDocker
不過實際上,這樣的腳本並沒有去安裝 Visual Studio,所以如果真的要實用,應該是要參考《Visual C++ 2017 的 Docker 建置環境》,先安裝好 Build Tools for Visual Studio 2017(或者 2019)、再進行 Qt 的安裝了。
而如果還要搭配 Qt VS Tools(連結)使用的話,則也還需要另外處理 build rule 的部分。而這部分,就等之後有空再來整理了。