Windows 的 Qt SDK Dockerfile [202002]

| | 0 Comments| 14:20
Categories:

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() {
     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 的部分。而這部分,就等之後有空再來整理了。

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *