Windows 的 Qt SDK Dockerfile [202002]


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:\Users\Heresy\AppData\Roaming\Qt\qtaccount.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:\TEMP\qt.exe
ADD qt-install.qs C:\TEMP\qt-install.qs
ADD qtaccount.ini C:\Users\ContainerAdministrator\AppData\Roaming\Qt\qtaccount.ini
RUN C:\TEMP\qt.exe -v --script C:\TEMP\qt-install.qs
 
# env setting
ENV QTDIR C:\Qt\5.13.1\msvc2017_64
 
# clean download files
RUN del C:\TEMP\* /q
 
# Start developer command prompt with any other commands specified.
ENTRYPOINT c:\BuildTools\VC\Auxiliary\Build\vcvarsall.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() {
     var page = gui.pageWidgetByObjectName("ObligationsPage");
     page.obligationsAgreement.setChecked(true);
     page.completeChanged();
     gui.clickButton(buttons.NextButton); }
Controller.prototype.IntroductionPageCallback = function() {
   gui.clickButton(buttons.NextButton); } Controller.prototype.TargetDirectoryPageCallback = function() {
   gui.currentPageWidget().TargetDirectoryLineEdit.setText("C:/Qt/");
   gui.clickButton(buttons.NextButton); }
Controller.prototype.PerformInstallationPageCallback = function() {
   gui.clickButton(buttons.CommitButton); }
Controller.prototype.ComponentSelectionPageCallback = function() {
   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); }

和之前的版本相比,主要是加入了一些錯誤自動重試,以及 ObligationsPageCallbackPerformInstallationPageCallback 這兩個函式。

理論上,這樣就可以正確地在微軟提供的 .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 的部分。而這部分,就等之後有空再來整理了。

對「Windows 的 Qt SDK Dockerfile [202002]」的想法

  1. […] 開始玩這部分後,才發現 Qt 在這部份真的很討厭…本來以為弄好第一次後,以後就沒什麼問題了,但是後來 Qt 一直修改他的安裝程式,導致常常過一段時間要重建 Docker image 的時候,安裝 Qt SDK 的腳本就廢掉、要修改(2020/02)… […]

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料