@@ -95,18 +95,36 @@ class MockArchive:
95
95
version : str = ""
96
96
arch_dir : str = ""
97
97
should_install : bool = True
98
+ extract_target : Optional [str ] = None
98
99
date : datetime = datetime .now ()
99
100
100
101
def xml_package_update (self ) -> str :
101
- return textwrap .dedent (
102
- f"""\
103
- <PackageUpdate>
104
- <Name>{ self .update_xml_name } </Name>
105
- <Version>{ self .version } -0-{ self .date .strftime ("%Y%m%d%H%M" )} </Version>
106
- <Description>none</Description>
107
- <DownloadableArchives>{ self .filename_7z } </DownloadableArchives>
108
- </PackageUpdate>"""
109
- )
102
+ if self .extract_target :
103
+ return textwrap .dedent (
104
+ f"""\
105
+ <PackageUpdate>
106
+ <Name>{ self .update_xml_name } </Name>
107
+ <Version>{ self .version } -0-{ self .date .strftime ("%Y%m%d%H%M" )} </Version>
108
+ <Description>none</Description>
109
+ <DownloadableArchives>{ self .filename_7z } </DownloadableArchives>
110
+ <Operations>
111
+ <Operation name="Extract">
112
+ <Argument>{ self .extract_target } </Argument>
113
+ <Argument>{ self .filename_7z } </Argument>
114
+ </Operation>
115
+ </Operations>
116
+ </PackageUpdate>"""
117
+ )
118
+ else :
119
+ return textwrap .dedent (
120
+ f"""\
121
+ <PackageUpdate>
122
+ <Name>{ self .update_xml_name } </Name>
123
+ <Version>{ self .version } -0-{ self .date .strftime ("%Y%m%d%H%M" )} </Version>
124
+ <Description>none</Description>
125
+ <DownloadableArchives>{ self .filename_7z } </DownloadableArchives>
126
+ </PackageUpdate>"""
127
+ )
110
128
111
129
def write_compressed_archive (self , dest : Path ) -> None :
112
130
def open_writable_archive ():
@@ -128,35 +146,50 @@ def write_to_archive(arc, src, arcname):
128
146
# shutil.make_archive(str(dest / self.filename_7z), "zip", src)
129
147
130
148
with TemporaryDirectory () as temp_dir , open_writable_archive () as archive :
149
+ # if the Updates.xml file uses Operations/Operation[@name='Extract'] elements then the names
150
+ # of archive members do not include the version and arch_dir path segments, instead they are
151
+ # supplied by an Argument element which is used when the archive is extracted.
131
152
temp_path = Path (temp_dir )
153
+ arch_dir = self .arch_dir if not self .extract_target else ""
132
154
133
155
for folder in ("bin" , "lib" , "mkspecs" ):
134
- (temp_path / self . arch_dir / folder ).mkdir (parents = True , exist_ok = True )
156
+ (temp_path / arch_dir / folder ).mkdir (parents = True , exist_ok = True )
135
157
136
158
# Use `self.contents` to write qmake binary, qmake script, QtCore binaries, etc
137
159
for patched_file in self .contents :
138
- full_path = temp_path / self . arch_dir / patched_file .filename
160
+ full_path = temp_path / arch_dir / patched_file .filename
139
161
if not full_path .parent .exists ():
140
162
full_path .parent .mkdir (parents = True )
141
163
full_path .write_text (patched_file .unpatched_content , "utf_8" )
142
164
143
- archive_name = "5.9" if self .version == "5.9.0" else self .version
165
+ if self .extract_target :
166
+ archive_name = "."
167
+ else :
168
+ archive_name = "5.9" if self .version == "5.9.0" else self .version
144
169
write_to_archive (archive , temp_path , arcname = archive_name )
145
170
146
171
147
172
def make_mock_geturl_download_archive (
148
173
* ,
149
174
standard_archives : List [MockArchive ],
150
175
desktop_archives : Optional [List [MockArchive ]] = None ,
176
+ extpdf_archives : Optional [List [MockArchive ]] = None ,
177
+ extweb_archives : Optional [List [MockArchive ]] = None ,
151
178
standard_updates_url : str ,
152
179
desktop_updates_url : str = "" ,
180
+ extpdf_updates_url : str = "" ,
181
+ extweb_updates_url : str = "" ,
153
182
) -> Tuple [GET_URL_TYPE , DOWNLOAD_ARCHIVE_TYPE ]:
154
183
"""
155
184
Returns a mock 'getUrl' and a mock 'downloadArchive' function.
156
185
"""
157
186
if desktop_archives is None :
158
187
desktop_archives = []
159
- for _archive in [* standard_archives , * desktop_archives ]:
188
+ if extpdf_archives is None :
189
+ extpdf_archives = []
190
+ if extweb_archives is None :
191
+ extweb_archives = []
192
+ for _archive in [* standard_archives , * desktop_archives , * extpdf_archives , * extweb_archives ]:
160
193
assert re .match (r".*\.(7z|tar\.xz)$" , _archive .filename_7z ), "Unsupported file type"
161
194
162
195
standard_xml = "<Updates>\n {}\n </Updates>" .format (
@@ -165,6 +198,8 @@ def make_mock_geturl_download_archive(
165
198
desktop_xml = "<Updates>\n {}\n </Updates>" .format (
166
199
"\n " .join ([archive .xml_package_update () for archive in desktop_archives ])
167
200
)
201
+ extpdf_xml = "<Updates>\n {}\n </Updates>" .format ("\n " .join ([archive .xml_package_update () for archive in extpdf_archives ]))
202
+ extweb_xml = "<Updates>\n {}\n </Updates>" .format ("\n " .join ([archive .xml_package_update () for archive in extweb_archives ]))
168
203
merged_xml = "<Updates>\n {}{}\n </Updates>" .format (
169
204
"\n " .join ([archive .xml_package_update () for archive in standard_archives ]),
170
205
"\n " .join ([archive .xml_package_update () for archive in desktop_archives ]),
@@ -177,6 +212,8 @@ def mock_getUrl(url: str, *args, **kwargs) -> str:
177
212
for xml , updates_url in (
178
213
(standard_xml , standard_updates_url ),
179
214
(desktop_xml , desktop_updates_url ),
215
+ (extpdf_xml , extpdf_updates_url ),
216
+ (extweb_xml , extweb_updates_url ),
180
217
):
181
218
basename = posixpath .dirname (updates_url )
182
219
if not updates_url :
@@ -186,13 +223,18 @@ def mock_getUrl(url: str, *args, **kwargs) -> str:
186
223
elif basename in url and url .endswith (".sha256" ):
187
224
filename = url .split ("/" )[- 1 ][: - len (".sha256" )]
188
225
return f"{ hashlib .sha256 (bytes (xml , 'utf-8' )).hexdigest ()} { filename } "
226
+ """
227
+ extensions urls may or may not exist.
228
+ """
229
+ if "/extensions/" in url :
230
+ raise ArchiveDownloadError (f"Failed to retrieve file at { url } \n Server response code: 404, reason: Not Found" )
189
231
assert False , f"No mocked url available for '{ url } '"
190
232
191
233
def mock_download_archive (url : str , out : str , * args ):
192
234
"""Make a mocked 7z archive at out_filename"""
193
235
194
236
def locate_archive () -> MockArchive :
195
- for archive in [* standard_archives , * desktop_archives ]:
237
+ for archive in [* standard_archives , * desktop_archives , * extpdf_archives , * extweb_archives ]:
196
238
if Path (out ).name == archive .filename_7z :
197
239
return archive
198
240
assert False , "Requested an archive that was not mocked"
@@ -1121,6 +1163,102 @@ def tool_archive(host: str, tool_name: str, variant: str, date: datetime = datet
1121
1163
r"INFO : Time elapsed: .* second"
1122
1164
),
1123
1165
),
1166
+ ( # extensions availability: qtpdf and qtwebengine
1167
+ "install-qt windows desktop 6.8.1 win64_msvc2022_64 -m qtwebengine" .split (),
1168
+ "windows" ,
1169
+ "desktop" ,
1170
+ "6.8.1" ,
1171
+ {"std" : "win64_msvc2022_64" , "extpdf" : "win64_msvc2022_64" , "extweb" : "win64_msvc2022_64" },
1172
+ {"std" : "msvc2022_64" , "extpdf" : "msvc2022_64" , "extweb" : "msvc2022_64" },
1173
+ {
1174
+ "std" : "windows_x86/desktop/qt6_681/qt6_681/Updates.xml" ,
1175
+ "extpdf" : "windows_x86/extensions/qtpdf/681/msvc2022_64/Updates.xml" ,
1176
+ "extweb" : "windows_x86/extensions/qtwebengine/681/msvc2022_64/Updates.xml" ,
1177
+ },
1178
+ {
1179
+ "std" : [
1180
+ plain_qtbase_archive (
1181
+ "qt.qt6.681.win64_msvc2022_64" ,
1182
+ "Windows-Windows_11_23H2-X86_64" ,
1183
+ host = "Windows-Windows_11_23H2-MSVC2022" ,
1184
+ )
1185
+ ],
1186
+ "extpdf" : [
1187
+ MockArchive (
1188
+ filename_7z = "qtpdf-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64.7z" ,
1189
+ update_xml_name = "extensions.qtpdf.681.win64_msvc2022_64" ,
1190
+ contents = (),
1191
+ should_install = False ,
1192
+ extract_target = "@TargetDir@/6.8.1/msvc2022_64" ,
1193
+ ),
1194
+ ],
1195
+ "extweb" : [
1196
+ MockArchive (
1197
+ filename_7z = "qtwebengine-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64.7z" ,
1198
+ update_xml_name = "extensions.qtwebengine.681.win64_msvc2022_64" ,
1199
+ contents = (
1200
+ PatchedFile (filename = "lib/Qt6WebEngineCore.prl" , unpatched_content = "... qtwebengine ...\n " ),
1201
+ ),
1202
+ should_install = False ,
1203
+ extract_target = "@TargetDir@/6.8.1/msvc2022_64" ,
1204
+ ),
1205
+ ],
1206
+ },
1207
+ re .compile (
1208
+ r"^INFO : aqtinstall\(aqt\) v.* on Python 3.*\n"
1209
+ r"INFO : Found extension qtwebengine\n"
1210
+ r"INFO : Found extension qtpdf\n"
1211
+ r"INFO : Downloading qtbase...\n"
1212
+ r"Finished installation of "
1213
+ r"qtbase-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64.7z in .*\n"
1214
+ r"INFO : Downloading qtwebengine...\n"
1215
+ r"Finished installation of "
1216
+ r"qtwebengine-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64.7z in .*\n"
1217
+ r"INFO : Patching .*/6.8.1/msvc2022_64/lib/Qt6WebEngineCore.prl\n"
1218
+ r"INFO : Finished installation\n"
1219
+ r"INFO : Time elapsed: .* second"
1220
+ ),
1221
+ ),
1222
+ ( # extension availability: qtpdf only
1223
+ "install-qt windows desktop 6.8.1 win64_mingw -m qtpdf" .split (),
1224
+ "windows" ,
1225
+ "desktop" ,
1226
+ "6.8.1" ,
1227
+ {"std" : "win64_mingw" , "extpdf" : "win64_mingw" },
1228
+ {"std" : "mingw_64" , "extpdf" : "mingw_64" },
1229
+ {
1230
+ "std" : "windows_x86/desktop/qt6_681/qt6_681/Updates.xml" ,
1231
+ "extpdf" : "windows_x86/extensions/qtpdf/681/mingw/Updates.xml" ,
1232
+ },
1233
+ {
1234
+ "std" : [
1235
+ plain_qtbase_archive (
1236
+ "qt.qt6.681.win64_mingw" ,
1237
+ "Windows-Windows_10_22H2-X86_64" ,
1238
+ host = "Windows-Windows_10_22H2-Mingw" ,
1239
+ )
1240
+ ],
1241
+ "extpdf" : [
1242
+ MockArchive (
1243
+ filename_7z = "qtpdf-Windows-Windows_10_22H2-Mingw-Windows-Windows_10_22H2-X86_64.7z" ,
1244
+ update_xml_name = "extensions.qtpdf.681.win64_mingw" ,
1245
+ contents = (),
1246
+ should_install = False ,
1247
+ extract_target = "@TargetDir@/6.8.1/mingw_64" ,
1248
+ ),
1249
+ ],
1250
+ },
1251
+ re .compile (
1252
+ r"^INFO : aqtinstall\(aqt\) v.* on Python 3.*\n"
1253
+ r"INFO : Found extension qtpdf\n"
1254
+ r"INFO : Downloading qtbase...\n"
1255
+ r"Finished installation of qtbase-Windows-Windows_10_22H2-Mingw-Windows-Windows_10_22H2-X86_64.7z in .*\n"
1256
+ r"INFO : Downloading qtpdf...\n"
1257
+ r"Finished installation of qtpdf-Windows-Windows_10_22H2-Mingw-Windows-Windows_10_22H2-X86_64.7z in .*\n"
1258
+ r"INFO : Finished installation\n"
1259
+ r"INFO : Time elapsed: .* second"
1260
+ ),
1261
+ ),
1124
1262
),
1125
1263
)
1126
1264
def test_install (
@@ -1145,12 +1283,24 @@ def test_install(
1145
1283
for i in range (len (desktop_archives )):
1146
1284
desktop_archives [i ].version = version
1147
1285
desktop_archives [i ].arch_dir = arch_dir ["desk" ]
1286
+ extpdf_archives = archives .get ("extpdf" , [])
1287
+ for i in range (len (extpdf_archives )):
1288
+ extpdf_archives [i ].version = version
1289
+ extpdf_archives [i ].arch_dir = arch_dir ["extpdf" ]
1290
+ extweb_archives = archives .get ("extweb" , [])
1291
+ for i in range (len (extweb_archives )):
1292
+ extweb_archives [i ].version = version
1293
+ extweb_archives [i ].arch_dir = arch_dir ["extweb" ]
1148
1294
1149
1295
mock_get_url , mock_download_archive = make_mock_geturl_download_archive (
1150
1296
standard_archives = std_archives ,
1151
1297
desktop_archives = desktop_archives ,
1298
+ extpdf_archives = extpdf_archives ,
1299
+ extweb_archives = extweb_archives ,
1152
1300
standard_updates_url = updates_url .get ("std" , "" ),
1153
1301
desktop_updates_url = updates_url .get ("desk" , "" ),
1302
+ extpdf_updates_url = updates_url .get ("extpdf" , "" ),
1303
+ extweb_updates_url = updates_url .get ("extweb" , "" ),
1154
1304
)
1155
1305
monkeypatch .setattr ("aqt.archives.getUrl" , mock_get_url )
1156
1306
monkeypatch .setattr ("aqt.helper.getUrl" , mock_get_url )
0 commit comments