1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | |
31 | #include "testcompiler.h" |
32 | |
33 | #include <QDir> |
34 | #include <QDirIterator> |
35 | #include <QObject> |
36 | #include <QRegularExpression> |
37 | #include <QStandardPaths> |
38 | #include <QTemporaryDir> |
39 | |
40 | #if defined(DEBUG_BUILD) |
41 | # define DIR_INFIX "debug/" |
42 | #elif defined(RELEASE_BUILD) |
43 | # define DIR_INFIX "release/" |
44 | #else |
45 | # define DIR_INFIX "" |
46 | #endif |
47 | |
48 | class tst_qmake : public QObject |
49 | { |
50 | Q_OBJECT |
51 | |
52 | public: |
53 | tst_qmake(); |
54 | |
55 | private slots: |
56 | void initTestCase(); |
57 | void cleanupTestCase(); |
58 | void cleanup(); |
59 | void simple_app(); |
60 | void simple_app_shadowbuild(); |
61 | void simple_app_shadowbuild2(); |
62 | void simple_app_versioned(); |
63 | void simple_lib(); |
64 | void simple_dll(); |
65 | void subdirs(); |
66 | void subdir_via_pro_file_extra_target(); |
67 | void duplicateLibraryEntries(); |
68 | void export_across_file_boundaries(); |
69 | void include_dir(); |
70 | void include_pwd(); |
71 | void install_files(); |
72 | void install_depends(); |
73 | void quotedfilenames(); |
74 | void prompt(); |
75 | void one_space(); |
76 | void findMocs(); |
77 | void findDeps(); |
78 | void rawString(); |
79 | #if defined(Q_OS_DARWIN) |
80 | void bundle_spaces(); |
81 | #elif defined(Q_OS_WIN) |
82 | void windowsResources(); |
83 | #endif |
84 | void substitutes(); |
85 | void project(); |
86 | void proFileCache(); |
87 | void qinstall(); |
88 | void resources(); |
89 | void conflictingTargets(); |
90 | |
91 | private: |
92 | TestCompiler test_compiler; |
93 | QTemporaryDir tempWorkDir; |
94 | QString base_path; |
95 | const QString origCurrentDirPath; |
96 | }; |
97 | |
98 | tst_qmake::tst_qmake() |
99 | : tempWorkDir(QDir::tempPath() + "/tst_qmake" ), |
100 | origCurrentDirPath(QDir::currentPath()) |
101 | { |
102 | } |
103 | |
104 | static void copyDir(const QString &sourceDirPath, const QString &targetDirPath) |
105 | { |
106 | QDir currentDir; |
107 | QDirIterator dit(sourceDirPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden); |
108 | while (dit.hasNext()) { |
109 | dit.next(); |
110 | const QString targetPath = targetDirPath + QLatin1Char('/') + dit.fileName(); |
111 | currentDir.mkpath(dirPath: targetPath); |
112 | copyDir(sourceDirPath: dit.filePath(), targetDirPath: targetPath); |
113 | } |
114 | |
115 | QDirIterator fit(sourceDirPath, QDir::Files | QDir::Hidden); |
116 | while (fit.hasNext()) { |
117 | fit.next(); |
118 | const QString targetPath = targetDirPath + QLatin1Char('/') + fit.fileName(); |
119 | QFile::remove(fileName: targetPath); // allowed to fail |
120 | QFile src(fit.filePath()); |
121 | QVERIFY2(src.copy(targetPath), qPrintable(src.errorString())); |
122 | } |
123 | } |
124 | |
125 | void tst_qmake::initTestCase() |
126 | { |
127 | QVERIFY2(tempWorkDir.isValid(), qPrintable(tempWorkDir.errorString())); |
128 | QString binpath = QLibraryInfo::location(QLibraryInfo::BinariesPath); |
129 | QString cmd = QString("%1/qmake" ).arg(a: binpath); |
130 | #ifdef Q_CC_MSVC |
131 | const QString jom = QStandardPaths::findExecutable(QLatin1String("jom.exe" )); |
132 | if (jom.isEmpty()) { |
133 | test_compiler.setBaseCommands( QLatin1String("nmake" ), cmd ); |
134 | } else { |
135 | test_compiler.setBaseCommands( jom, cmd ); |
136 | } |
137 | #elif defined(Q_CC_MINGW) |
138 | test_compiler.setBaseCommands( "mingw32-make" , cmd ); |
139 | #elif defined(Q_OS_WIN) && defined(Q_CC_GNU) |
140 | test_compiler.setBaseCommands( "mmmake" , cmd ); |
141 | #else |
142 | test_compiler.setBaseCommands( makeCmd: "make" , qmakeCmd: cmd ); |
143 | #endif |
144 | const QString testDataSubDir = QStringLiteral("testdata" ); |
145 | const QString subProgram = testDataSubDir + QLatin1String("/simple_app/main.cpp" ); |
146 | QString testDataPath = QFINDTESTDATA(subProgram); |
147 | if (!testDataPath.endsWith(s: subProgram)) |
148 | QFAIL("Cannot find test data directory." ); |
149 | testDataPath.chop(n: subProgram.length() - testDataSubDir.length()); |
150 | |
151 | QString userWorkDir = qgetenv(varName: "TST_QMAKE_BUILD_DIR" ); |
152 | if (userWorkDir.isEmpty()) { |
153 | base_path = tempWorkDir.path(); |
154 | } else { |
155 | if (!QFile::exists(fileName: userWorkDir)) { |
156 | QFAIL(qUtf8Printable(QStringLiteral("TST_QMAKE_BUILD_DIR %1 does not exist." ) |
157 | .arg(userWorkDir))); |
158 | } |
159 | base_path = userWorkDir; |
160 | } |
161 | |
162 | copyDir(sourceDirPath: testDataPath, targetDirPath: base_path + QLatin1Char('/') + testDataSubDir); |
163 | } |
164 | |
165 | void tst_qmake::cleanupTestCase() |
166 | { |
167 | // On Windows, ~QTemporaryDir fails to remove the directory if we're still in there. |
168 | QDir::setCurrent(origCurrentDirPath); |
169 | } |
170 | |
171 | void tst_qmake::cleanup() |
172 | { |
173 | test_compiler.resetArguments(); |
174 | test_compiler.resetEnvironment(); |
175 | test_compiler.clearCommandOutput(); |
176 | } |
177 | |
178 | void tst_qmake::simple_app() |
179 | { |
180 | QString workDir = base_path + "/testdata/simple_app" ; |
181 | QString destDir = workDir + "/dest dir" ; |
182 | QString installDir = workDir + "/dist" ; |
183 | |
184 | QVERIFY( test_compiler.qmake( workDir, "simple_app" , QString() )); |
185 | QVERIFY( test_compiler.make( workDir )); |
186 | QVERIFY( test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); |
187 | |
188 | QVERIFY(test_compiler.make(workDir, "install" )); |
189 | QVERIFY(test_compiler.exists(installDir, "simple app" , Exe, "1.0.0" )); |
190 | |
191 | QVERIFY( test_compiler.makeClean( workDir )); |
192 | QVERIFY( test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); // Should still exist after a make clean |
193 | QVERIFY( test_compiler.makeDistClean( workDir )); |
194 | QVERIFY( !test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); // Should not exist after a make distclean |
195 | QVERIFY( test_compiler.removeMakefile( workDir ) ); |
196 | } |
197 | |
198 | void tst_qmake::simple_app_shadowbuild() |
199 | { |
200 | QString workDir = base_path + "/testdata/simple_app" ; |
201 | QString buildDir = base_path + "/testdata/simple_app_build" ; |
202 | QString destDir = buildDir + "/dest dir" ; |
203 | |
204 | QVERIFY( test_compiler.qmake( workDir, "simple_app" , buildDir )); |
205 | QVERIFY( test_compiler.make( buildDir )); |
206 | QVERIFY( test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); |
207 | QVERIFY( test_compiler.makeClean( buildDir )); |
208 | QVERIFY( test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); // Should still exist after a make clean |
209 | QVERIFY( test_compiler.makeDistClean( buildDir )); |
210 | QVERIFY( !test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); // Should not exist after a make distclean |
211 | QVERIFY( test_compiler.removeMakefile( buildDir ) ); |
212 | } |
213 | |
214 | void tst_qmake::simple_app_shadowbuild2() |
215 | { |
216 | QString workDir = base_path + "/testdata/simple_app" ; |
217 | QString buildDir = base_path + "/testdata/simple_app/build" ; |
218 | QString destDir = buildDir + "/dest dir" ; |
219 | |
220 | QVERIFY( test_compiler.qmake( workDir, "simple_app" , buildDir )); |
221 | QVERIFY( test_compiler.make( buildDir )); |
222 | QVERIFY( test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); |
223 | QVERIFY( test_compiler.makeClean( buildDir )); |
224 | QVERIFY( test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); // Should still exist after a make clean |
225 | QVERIFY( test_compiler.makeDistClean( buildDir )); |
226 | QVERIFY( !test_compiler.exists( destDir, "simple app" , Exe, "1.0.0" )); // Should not exist after a make distclean |
227 | QVERIFY( test_compiler.removeMakefile( buildDir ) ); |
228 | } |
229 | |
230 | void tst_qmake::simple_app_versioned() |
231 | { |
232 | QString workDir = base_path + "/testdata/simple_app" ; |
233 | QString buildDir = base_path + "/testdata/simple_app_versioned_build" ; |
234 | QString destDir = buildDir + "/dest dir" ; |
235 | QString installDir = buildDir + "/dist" ; |
236 | |
237 | QString version = "4.5.6" ; |
238 | QVERIFY(test_compiler.qmake(workDir, "simple_app" , buildDir, QStringList{ "VERSION=" + version })); |
239 | QString qmakeOutput = test_compiler.commandOutput(); |
240 | QVERIFY(test_compiler.make(buildDir)); |
241 | QVERIFY(test_compiler.exists(destDir, "simple app" , Exe, version)); |
242 | |
243 | QString pdbFilePath; |
244 | bool checkPdb = qmakeOutput.contains(s: "Project MESSAGE: check for pdb, please" ); |
245 | if (checkPdb) { |
246 | QString targetBase = QFileInfo(TestCompiler::targetName(buildMode: Exe, target: "simple app" , version)) |
247 | .completeBaseName(); |
248 | pdbFilePath = destDir + '/' + targetBase + ".pdb" ; |
249 | QVERIFY2(QFile::exists(pdbFilePath), qPrintable(pdbFilePath)); |
250 | QVERIFY(test_compiler.make(buildDir, "install" )); |
251 | QString installedPdbFilePath = installDir + '/' + targetBase + ".pdb" ; |
252 | QVERIFY2(QFile::exists(installedPdbFilePath), qPrintable(installedPdbFilePath)); |
253 | } |
254 | |
255 | QVERIFY(test_compiler.makeClean(buildDir)); |
256 | QVERIFY(test_compiler.exists(destDir, "simple app" , Exe, version)); |
257 | QVERIFY(test_compiler.makeDistClean(buildDir)); |
258 | QVERIFY(!test_compiler.exists(destDir, "simple app" , Exe, version)); |
259 | if (checkPdb) |
260 | QVERIFY(!QFile::exists(pdbFilePath)); |
261 | QVERIFY(test_compiler.removeMakefile(buildDir)); |
262 | } |
263 | |
264 | void tst_qmake::simple_dll() |
265 | { |
266 | QString workDir = base_path + "/testdata/simple_dll" ; |
267 | QString destDir = workDir + "/dest dir" ; |
268 | |
269 | QDir D; |
270 | D.remove( fileName: workDir + "/Makefile" ); |
271 | QVERIFY( test_compiler.qmake( workDir, "simple_dll" )); |
272 | QVERIFY( test_compiler.make( workDir )); |
273 | QVERIFY( test_compiler.exists( destDir, "simple dll" , Dll, "1.0.0" )); |
274 | QVERIFY( test_compiler.makeClean( workDir )); |
275 | QVERIFY( test_compiler.exists( destDir, "simple dll" , Dll, "1.0.0" )); // Should still exist after a make clean |
276 | QVERIFY( test_compiler.makeDistClean( workDir )); |
277 | QVERIFY( !test_compiler.exists( destDir, "simple dll" , Dll, "1.0.0" )); // Should not exist after a make distclean |
278 | QVERIFY( test_compiler.removeMakefile( workDir ) ); |
279 | } |
280 | |
281 | void tst_qmake::simple_lib() |
282 | { |
283 | QString workDir = base_path + "/testdata/simple_lib" ; |
284 | QString destDir = workDir + "/dest dir" ; |
285 | |
286 | QDir D; |
287 | D.remove( fileName: workDir + "/Makefile" ); |
288 | QVERIFY( test_compiler.qmake( workDir, "simple_lib" )); |
289 | QVERIFY( test_compiler.make( workDir )); |
290 | QVERIFY( test_compiler.exists( destDir, "simple lib" , Lib, "1.0.0" )); |
291 | QVERIFY( test_compiler.makeClean( workDir )); |
292 | QVERIFY( test_compiler.exists( destDir, "simple lib" , Lib, "1.0.0" )); // Should still exist after a make clean |
293 | QVERIFY( test_compiler.makeDistClean( workDir )); |
294 | QVERIFY( !test_compiler.exists( destDir, "simple lib" , Lib, "1.0.0" )); // Should not exist after a make distclean |
295 | QVERIFY( test_compiler.removeMakefile( workDir ) ); |
296 | } |
297 | |
298 | void tst_qmake::subdirs() |
299 | { |
300 | QString workDir = base_path + "/testdata/subdirs" ; |
301 | |
302 | QDir D; |
303 | D.remove( fileName: workDir + "/simple_app/Makefile" ); |
304 | D.remove( fileName: workDir + "/simple_dll/Makefile" ); |
305 | QVERIFY( test_compiler.qmake( workDir, "subdirs" )); |
306 | QVERIFY( test_compiler.make( workDir )); |
307 | QVERIFY( test_compiler.exists(workDir + "/simple_app/dest dir" , "simple app" , Exe)); |
308 | QVERIFY( test_compiler.exists(workDir + "/simple_dll/dest dir" , "simple dll" , Dll)); |
309 | QVERIFY( test_compiler.makeClean( workDir )); |
310 | // Should still exist after a make clean |
311 | QVERIFY( test_compiler.exists(workDir + "/simple_app/dest dir" , "simple app" , Exe)); |
312 | QVERIFY( test_compiler.exists(workDir + "/simple_dll/dest dir" , "simple dll" , Dll)); |
313 | // Since subdirs templates do not have a make dist clean, we should clean up ourselves |
314 | // properly |
315 | QVERIFY( test_compiler.makeDistClean( workDir )); |
316 | QVERIFY( test_compiler.removeMakefile( workDir ) ); |
317 | } |
318 | |
319 | void tst_qmake::() |
320 | { |
321 | QString workDir = base_path + "/testdata/subdir_via_pro_file_extra_target" ; |
322 | |
323 | QDir D; |
324 | D.remove( fileName: workDir + "/Makefile" ); |
325 | D.remove( fileName: workDir + "/Makefile.subdir" ); |
326 | D.remove( fileName: workDir + "/simple/Makefile" ); |
327 | D.remove( fileName: workDir + "/simple/Makefile.subdir" ); |
328 | QVERIFY( test_compiler.qmake( workDir, "subdir_via_pro_file_extra_target" )); |
329 | QVERIFY( test_compiler.make( workDir, "extratarget" )); |
330 | } |
331 | |
332 | void tst_qmake::duplicateLibraryEntries() |
333 | { |
334 | QVERIFY(true); |
335 | /* TODO: this test does not work as the problem it tests doesn't happen |
336 | until after the parsing of the pro-file and thus has to be tested |
337 | by parsing the Makefile. This is not doable with the current |
338 | testcompiler framework and has as such been put on hold. |
339 | |
340 | QString workDir = base_path + "/testdata/duplicateLibraryEntries"; |
341 | QVERIFY(test_compiler.qmake(workDir, "duplicateLibraryEntries")); */ |
342 | } |
343 | |
344 | void tst_qmake::export_across_file_boundaries() |
345 | { |
346 | // This relies on features so we need to set the QMAKEFEATURES environment variable |
347 | test_compiler.addToEnvironment(varAssignment: "QMAKEFEATURES=." ); |
348 | QString workDir = base_path + "/testdata/export_across_file_boundaries" ; |
349 | QVERIFY( test_compiler.qmake( workDir, "foo" )); |
350 | } |
351 | |
352 | void tst_qmake::include_dir() |
353 | { |
354 | #ifdef QT_NO_WIDGETS |
355 | QSKIP("This test depends on QtWidgets" ); |
356 | #else |
357 | QString workDir = base_path + "/testdata/include_dir" ; |
358 | QVERIFY( test_compiler.qmake( workDir, "foo" )); |
359 | QVERIFY( test_compiler.make( workDir )); |
360 | QVERIFY( test_compiler.exists( workDir, "foo" , Exe, "1.0.0" )); |
361 | QVERIFY( test_compiler.makeDistClean( workDir )); |
362 | |
363 | QString buildDir = base_path + "/testdata/include_dir_build" ; |
364 | QVERIFY( test_compiler.qmake( workDir, "foo" , buildDir )); |
365 | QVERIFY( test_compiler.make( buildDir )); |
366 | QVERIFY( test_compiler.exists( buildDir, "foo" , Exe, "1.0.0" )); |
367 | QVERIFY( test_compiler.makeDistClean( buildDir )); |
368 | #endif |
369 | } |
370 | |
371 | void tst_qmake::include_pwd() |
372 | { |
373 | QString workDir = base_path + "/testdata/include_pwd" ; |
374 | QVERIFY( test_compiler.qmake( workDir, "include_pwd" )); |
375 | QVERIFY( test_compiler.make( workDir )); |
376 | QVERIFY( test_compiler.makeDistClean( workDir )); |
377 | } |
378 | |
379 | void tst_qmake::install_files() |
380 | { |
381 | QString workDir = base_path + "/testdata/shadow_files" ; |
382 | QVERIFY( test_compiler.qmake( workDir, "foo" )); |
383 | QVERIFY( test_compiler.make( workDir )); |
384 | QVERIFY( test_compiler.exists( workDir, "foo" , Exe, "1.0.0" )); |
385 | QVERIFY( test_compiler.make( workDir, "install" )); |
386 | QVERIFY( test_compiler.exists( workDir + "/dist" , "foo" , Exe, "1.0.0" )); |
387 | QVERIFY( test_compiler.exists( workDir + "/dist" , "test.txt" , Plain, "1.0.0" )); |
388 | QCOMPARE(QFileInfo(workDir + "/test.txt" ).lastModified(), QFileInfo(workDir + "/dist/test.txt" ).lastModified()); |
389 | QVERIFY( test_compiler.make( workDir, "uninstall" )); |
390 | QVERIFY( test_compiler.makeDistClean( workDir )); |
391 | |
392 | QString buildDir = base_path + "/testdata/shadow_files_build" ; |
393 | QVERIFY( test_compiler.qmake( workDir, "foo" , buildDir )); |
394 | QVERIFY( test_compiler.make( buildDir )); |
395 | QVERIFY( test_compiler.exists( buildDir, "foo" , Exe, "1.0.0" )); |
396 | QVERIFY( test_compiler.make( buildDir, "install" )); |
397 | QVERIFY( test_compiler.exists( workDir + "/dist" , "foo" , Exe, "1.0.0" )); |
398 | QVERIFY( test_compiler.exists( workDir + "/dist" , "test.txt" , Plain, "1.0.0" )); |
399 | QVERIFY( test_compiler.exists( workDir + "/dist" , "foo.bar" , Plain, "1.0.0" )); |
400 | QVERIFY( test_compiler.make( buildDir, "uninstall" )); |
401 | QVERIFY( test_compiler.makeDistClean( buildDir )); |
402 | } |
403 | |
404 | void tst_qmake::install_depends() |
405 | { |
406 | QString workDir = base_path + "/testdata/install_depends" ; |
407 | QVERIFY( test_compiler.qmake( workDir, "foo" )); |
408 | QVERIFY( test_compiler.make( workDir )); |
409 | QVERIFY( test_compiler.exists( workDir, "foo" , Exe, "1.0.0" )); |
410 | QVERIFY( test_compiler.make( workDir, "install" )); |
411 | QVERIFY( test_compiler.exists( workDir + "/dist" , "foo" , Exe, "1.0.0" )); |
412 | QVERIFY( test_compiler.exists( workDir + "/dist" , "test1" , Plain, "1.0.0" )); |
413 | QVERIFY( test_compiler.exists( workDir + "/dist" , "test2" , Plain, "1.0.0" )); |
414 | QVERIFY( test_compiler.make( workDir, "uninstall" )); |
415 | QVERIFY( test_compiler.makeDistClean( workDir )); |
416 | } |
417 | void tst_qmake::quotedfilenames() |
418 | { |
419 | QString workDir = base_path + "/testdata/quotedfilenames" ; |
420 | QVERIFY( test_compiler.qmake( workDir, "quotedfilenames" )); |
421 | QVERIFY( test_compiler.makeClean( workDir )); |
422 | QVERIFY( test_compiler.make( workDir )); |
423 | QVERIFY( test_compiler.exists( workDir, "quotedfilenames" , Exe, "1.0.0" )); |
424 | } |
425 | |
426 | void tst_qmake::prompt() |
427 | { |
428 | #if 0 |
429 | QProcess qmake; |
430 | qmake.setProcessChannelMode(QProcess::MergedChannels); |
431 | qmake.setWorkingDirectory(QLatin1String("testdata/prompt" )); |
432 | qmake.start(QLatin1String("qmake CONFIG-=debug_and_release CONFIG-=debug CONFIG+=release" ), |
433 | QIODevice::Text | QIODevice::ReadWrite); |
434 | QVERIFY(qmake.waitForStarted(20000)); |
435 | QByteArray read = qmake.readAll(); |
436 | qDebug() << read; |
437 | QCOMPARE(read, QByteArray("Project PROMPT: Prompteroo? " )); |
438 | qmake.write("promptetiprompt\n" ); |
439 | QVERIFY(qmake.waitForFinished(20000)); |
440 | #endif |
441 | } |
442 | |
443 | void tst_qmake::one_space() |
444 | { |
445 | QString workDir = base_path + "/testdata/one_space" ; |
446 | |
447 | QVERIFY( test_compiler.qmake( workDir, "one_space" )); |
448 | QVERIFY( test_compiler.make( workDir )); |
449 | QVERIFY( test_compiler.exists( workDir, "one space" , Exe, "1.0.0" )); |
450 | QVERIFY( test_compiler.makeClean( workDir )); |
451 | QVERIFY( test_compiler.exists( workDir, "one space" , Exe, "1.0.0" )); // Should still exist after a make clean |
452 | QVERIFY( test_compiler.makeDistClean( workDir )); |
453 | QVERIFY( !test_compiler.exists( workDir, "one space" , Exe, "1.0.0" )); // Should not exist after a make distclean |
454 | QVERIFY( test_compiler.removeMakefile( workDir ) ); |
455 | } |
456 | |
457 | void tst_qmake::findMocs() |
458 | { |
459 | QString workDir = base_path + "/testdata/findMocs" ; |
460 | |
461 | QVERIFY( test_compiler.qmake(workDir, "findMocs" ) ); |
462 | QVERIFY( test_compiler.make(workDir) ); |
463 | QVERIFY( test_compiler.exists(workDir, "findMocs" , Exe, "1.0.0" ) ); |
464 | QVERIFY( test_compiler.makeClean(workDir) ); |
465 | QVERIFY( test_compiler.exists(workDir, "findMocs" , Exe, "1.0.0" ) ); |
466 | QVERIFY( test_compiler.makeDistClean(workDir ) ); |
467 | QVERIFY( !test_compiler.exists(workDir, "findMocs" , Exe, "1.0.0" ) ); |
468 | QVERIFY( test_compiler.removeMakefile(workDir) ); |
469 | } |
470 | |
471 | void tst_qmake::findDeps() |
472 | { |
473 | QString workDir = base_path + "/testdata/findDeps" ; |
474 | |
475 | QVERIFY( test_compiler.qmake(workDir, "findDeps" ) ); |
476 | QVERIFY( test_compiler.make(workDir) ); |
477 | QVERIFY( test_compiler.exists(workDir, "findDeps" , Exe, "1.0.0" ) ); |
478 | QVERIFY( test_compiler.makeClean(workDir) ); |
479 | QVERIFY( test_compiler.exists(workDir, "findDeps" , Exe, "1.0.0" ) ); |
480 | QVERIFY( test_compiler.makeDistClean(workDir ) ); |
481 | QVERIFY( !test_compiler.exists(workDir, "findDeps" , Exe, "1.0.0" ) ); |
482 | QVERIFY( test_compiler.removeMakefile(workDir) ); |
483 | } |
484 | |
485 | void tst_qmake::rawString() |
486 | { |
487 | #ifdef Q_COMPILER_RAW_STRINGS |
488 | QString workDir = base_path + "/testdata/rawString" ; |
489 | |
490 | QVERIFY( test_compiler.qmake(workDir, "rawString" ) ); |
491 | QVERIFY( test_compiler.make(workDir) ); |
492 | QVERIFY( test_compiler.exists(workDir, "rawString" , Exe, "1.0.0" ) ); |
493 | QVERIFY( test_compiler.makeClean(workDir) ); |
494 | QVERIFY( test_compiler.exists(workDir, "rawString" , Exe, "1.0.0" ) ); |
495 | QVERIFY( test_compiler.makeDistClean(workDir ) ); |
496 | QVERIFY( !test_compiler.exists(workDir, "rawString" , Exe, "1.0.0" ) ); |
497 | QVERIFY( test_compiler.removeMakefile(workDir) ); |
498 | #else |
499 | QSKIP("Test for C++11 raw strings depends on compiler support for them" ); |
500 | #endif |
501 | } |
502 | |
503 | struct TempFile |
504 | : QFile |
505 | { |
506 | TempFile(QString filename) |
507 | : QFile(filename) |
508 | { |
509 | } |
510 | |
511 | ~TempFile() |
512 | { |
513 | if (this->exists()) |
514 | this->remove(); |
515 | } |
516 | }; |
517 | |
518 | #if defined(Q_OS_DARWIN) |
519 | |
520 | void tst_qmake::bundle_spaces() |
521 | { |
522 | QString workDir = base_path + "/testdata/bundle-spaces" ; |
523 | |
524 | // We set up alternate arguments here, to make sure we're testing Mac |
525 | // Bundles and since this might be the wrong output we rely on dry-running |
526 | // make (-n). |
527 | |
528 | test_compiler.setArguments(QStringList() << "-n" , |
529 | QStringList() << "-spec" << "macx-clang" ); |
530 | |
531 | QVERIFY( test_compiler.qmake(workDir, "bundle-spaces" ) ); |
532 | |
533 | TempFile non_existing_file(workDir + "/non-existing file" ); |
534 | QVERIFY( !non_existing_file.exists() ); |
535 | |
536 | // Make fails: no rule to make "non-existing file" |
537 | QVERIFY( test_compiler.make(workDir, QString(), true) ); |
538 | |
539 | QVERIFY( non_existing_file.open(QIODevice::WriteOnly) ); |
540 | QVERIFY( non_existing_file.exists() ); |
541 | |
542 | // Aha! |
543 | QVERIFY( test_compiler.make(workDir) ); |
544 | |
545 | // Cleanup |
546 | QVERIFY( non_existing_file.remove() ); |
547 | QVERIFY( !non_existing_file.exists() ); |
548 | QVERIFY( test_compiler.removeMakefile(workDir) ); |
549 | } |
550 | |
551 | #elif defined(Q_OS_WIN) // defined(Q_OS_DARWIN) |
552 | |
553 | void tst_qmake::windowsResources() |
554 | { |
555 | QString workDir = base_path + "/testdata/windows_resources" ; |
556 | QVERIFY(test_compiler.qmake(workDir, "windows_resources" )); |
557 | QVERIFY(test_compiler.make(workDir)); |
558 | |
559 | // Another "make" must not rebuild the .res file |
560 | test_compiler.clearCommandOutput(); |
561 | QVERIFY(test_compiler.make(workDir)); |
562 | QVERIFY(!test_compiler.commandOutput().contains("windows_resources.rc" )); |
563 | test_compiler.clearCommandOutput(); |
564 | |
565 | // Wait a second to make sure we get a new timestamp in the touch below |
566 | QTest::qWait(1000); |
567 | |
568 | // Touch the deepest include of the .rc file |
569 | QVERIFY(test_compiler.runCommand("cmd" , QStringList{"/c" , |
570 | "echo.>>" + QDir::toNativeSeparators(workDir + "/version.inc" )})); |
571 | |
572 | // The next "make" must rebuild the .res file |
573 | QVERIFY(test_compiler.make(workDir)); |
574 | QVERIFY(test_compiler.commandOutput().contains("windows_resources.rc" )); |
575 | } |
576 | |
577 | #endif // defined(Q_OS_WIN) |
578 | |
579 | void tst_qmake::substitutes() |
580 | { |
581 | QString workDir = base_path + "/testdata/substitutes" ; |
582 | QVERIFY( test_compiler.qmake( workDir, "test" )); |
583 | QVERIFY( test_compiler.exists( workDir, "test" , Plain, "" )); |
584 | QVERIFY( test_compiler.exists( workDir, "sub/test2" , Plain, "" )); |
585 | QVERIFY( test_compiler.exists( workDir, "sub/indirect_test.txt" , Plain, "" )); |
586 | QVERIFY( test_compiler.makeDistClean( workDir )); |
587 | |
588 | QString buildDir = base_path + "/testdata/substitutes_build" ; |
589 | QVERIFY( test_compiler.qmake( workDir, "test" , buildDir )); |
590 | QVERIFY( test_compiler.exists( buildDir, "test" , Plain, "" )); |
591 | QVERIFY( test_compiler.exists( buildDir, "sub/test2" , Plain, "" )); |
592 | QVERIFY( test_compiler.exists( buildDir, "sub/indirect_test.txt" , Plain, "" )); |
593 | |
594 | QFile copySource(workDir + "/copy.txt" ); |
595 | QFile copyDestination(buildDir + "/copy_test.txt" ); |
596 | |
597 | QVERIFY(copySource.open(QFile::ReadOnly)); |
598 | QVERIFY(copyDestination.open(QFile::ReadOnly)); |
599 | QCOMPARE(copySource.readAll(), copyDestination.readAll()); |
600 | |
601 | QVERIFY( test_compiler.makeDistClean( buildDir )); |
602 | } |
603 | |
604 | void tst_qmake::project() |
605 | { |
606 | QString workDir = base_path + "/testdata/project" ; |
607 | |
608 | QVERIFY( test_compiler.qmakeProject( workDir, "project" )); |
609 | QVERIFY( test_compiler.exists( workDir, "project.pro" , Plain, "" )); |
610 | QVERIFY( test_compiler.qmake( workDir, "project" )); |
611 | QVERIFY( test_compiler.exists( workDir, "Makefile" , Plain, "" )); |
612 | QVERIFY( test_compiler.make( workDir )); |
613 | QVERIFY( test_compiler.exists( workDir, "project" , Exe, "" )); |
614 | QVERIFY( test_compiler.makeDistClean( workDir )); |
615 | QVERIFY( test_compiler.removeProject( workDir, "project" )); |
616 | } |
617 | |
618 | void tst_qmake::proFileCache() |
619 | { |
620 | QString workDir = base_path + "/testdata/pro_file_cache" ; |
621 | QVERIFY( test_compiler.qmake( workDir, "pro_file_cache" )); |
622 | } |
623 | |
624 | void tst_qmake::qinstall() |
625 | { |
626 | const QString testName = "qinstall" ; |
627 | QDir testDataDir = base_path + "/testdata" ; |
628 | if (testDataDir.exists(name: testName)) |
629 | testDataDir.rmdir(dirName: testName); |
630 | QVERIFY(testDataDir.mkdir(testName)); |
631 | const QString workDir = testDataDir.filePath(fileName: testName); |
632 | auto qinstall = [&](const QString &src, const QString &dst, bool executable = false) { |
633 | QStringList args = {"-install" , "qinstall" }; |
634 | if (executable) |
635 | args << "-exe" ; |
636 | args << src << dst; |
637 | return test_compiler.qmake(workDir, arguments: args); |
638 | }; |
639 | const QFileDevice::Permissions readFlags |
640 | = QFileDevice::ReadOwner | QFileDevice::ReadUser |
641 | | QFileDevice::ReadGroup | QFileDevice::ReadOther; |
642 | const QFileDevice::Permissions writeFlags |
643 | = QFileDevice::WriteOwner | QFileDevice::WriteUser |
644 | | QFileDevice::WriteGroup | QFileDevice::WriteOther; |
645 | const QFileDevice::Permissions exeFlags |
646 | = QFileDevice::ExeOwner | QFileDevice::ExeUser |
647 | | QFileDevice::ExeGroup | QFileDevice::ExeOther; |
648 | |
649 | // install a regular file |
650 | { |
651 | QFileInfo src(testDataDir.filePath(fileName: "project/main.cpp" )); |
652 | QFileInfo dst("foo.cpp" ); |
653 | QVERIFY(qinstall(src.filePath(), dst.filePath())); |
654 | QVERIFY(dst.exists()); |
655 | QCOMPARE(src.size(), dst.size()); |
656 | QVERIFY(dst.permissions() & readFlags); |
657 | QVERIFY(dst.permissions() & writeFlags); |
658 | QVERIFY(!(dst.permissions() & exeFlags)); |
659 | test_compiler.clearCommandOutput(); |
660 | } |
661 | |
662 | // install an executable file |
663 | { |
664 | const QString mocFilePath = QLibraryInfo::location(QLibraryInfo::BinariesPath) |
665 | + "/moc" |
666 | #ifdef Q_OS_WIN |
667 | + ".exe" |
668 | #endif |
669 | ; |
670 | QFileInfo src(mocFilePath); |
671 | QVERIFY(src.exists()); |
672 | QVERIFY(src.permissions() & exeFlags); |
673 | QFileInfo dst("copied_" + src.fileName()); |
674 | QVERIFY(qinstall(src.filePath(), dst.filePath(), true)); |
675 | QVERIFY(dst.exists()); |
676 | QCOMPARE(src.size(), dst.size()); |
677 | QVERIFY(dst.permissions() & readFlags); |
678 | QVERIFY(dst.permissions() & writeFlags); |
679 | QVERIFY(dst.permissions() & exeFlags); |
680 | test_compiler.clearCommandOutput(); |
681 | } |
682 | |
683 | // install a read-only file |
684 | { |
685 | QFile srcfile("foo.cpp" ); |
686 | QVERIFY(srcfile.setPermissions(srcfile.permissions() & ~writeFlags)); |
687 | QFileInfo src(srcfile); |
688 | QFileInfo dst("bar.cpp" ); |
689 | QVERIFY(qinstall(src.filePath(), dst.filePath())); |
690 | QVERIFY(dst.exists()); |
691 | QCOMPARE(src.size(), dst.size()); |
692 | QVERIFY(dst.permissions() & readFlags); |
693 | QVERIFY(dst.permissions() & writeFlags); |
694 | QVERIFY(!(dst.permissions() & exeFlags)); |
695 | test_compiler.clearCommandOutput(); |
696 | } |
697 | |
698 | // install a directory |
699 | { |
700 | QDir src = testDataDir; |
701 | src.cd(dirName: "project" ); |
702 | QDir dst("narf" ); |
703 | QVERIFY(qinstall(src.absolutePath(), dst.absolutePath())); |
704 | QCOMPARE(src.entryList(QDir::Files, QDir::Name), dst.entryList(QDir::Files, QDir::Name)); |
705 | test_compiler.clearCommandOutput(); |
706 | } |
707 | |
708 | // install a directory with a read-only file |
709 | { |
710 | QDir src("narf" ); |
711 | QFile srcfile(src.filePath(fileName: "main.cpp" )); |
712 | QVERIFY(srcfile.setPermissions(srcfile.permissions() & ~writeFlags)); |
713 | QDir dst("zort" ); |
714 | QVERIFY(qinstall(src.absolutePath(), dst.absolutePath())); |
715 | QCOMPARE(src.entryList(QDir::Files, QDir::Name), dst.entryList(QDir::Files, QDir::Name)); |
716 | } |
717 | } |
718 | |
719 | void tst_qmake::resources() |
720 | { |
721 | QString workDir = base_path + "/testdata/resources" ; |
722 | QVERIFY(test_compiler.qmake(workDir, "resources" )); |
723 | |
724 | { |
725 | QFile qrcFile(workDir + '/' + DIR_INFIX "qmake_pro_file.qrc" ); |
726 | QVERIFY2(qrcFile.exists(), qPrintable(qrcFile.fileName())); |
727 | QVERIFY(qrcFile.open(QFile::ReadOnly)); |
728 | QByteArray qrcXml = qrcFile.readAll(); |
729 | QVERIFY(qrcXml.contains("alias=\"resources.pro\"" )); |
730 | QVERIFY(qrcXml.contains("prefix=\"/prefix\"" )); |
731 | } |
732 | |
733 | { |
734 | QFile qrcFile(workDir + '/' + DIR_INFIX "qmake_subdir.qrc" ); |
735 | QVERIFY(qrcFile.exists()); |
736 | QVERIFY(qrcFile.open(QFile::ReadOnly)); |
737 | QByteArray qrcXml = qrcFile.readAll(); |
738 | QVERIFY(qrcXml.contains("alias=\"file.txt\"" )); |
739 | } |
740 | |
741 | { |
742 | QFile qrcFile(workDir + '/' + DIR_INFIX "qmake_qmake_immediate.qrc" ); |
743 | QVERIFY(qrcFile.exists()); |
744 | QVERIFY(qrcFile.open(QFile::ReadOnly)); |
745 | QByteArray qrcXml = qrcFile.readAll(); |
746 | QVERIFY(qrcXml.contains("alias=\"main.cpp\"" )); |
747 | } |
748 | |
749 | QVERIFY(test_compiler.make(workDir)); |
750 | } |
751 | |
752 | void tst_qmake::conflictingTargets() |
753 | { |
754 | QString workDir = base_path + "/testdata/conflicting_targets" ; |
755 | QVERIFY(test_compiler.qmake(workDir, "conflicting_targets" )); |
756 | const QRegularExpression rex("Targets of builds '([^']+)' and '([^']+)' conflict" ); |
757 | auto match = rex.match(subject: test_compiler.commandOutput()); |
758 | QVERIFY(match.hasMatch()); |
759 | QStringList builds = { match.captured(nth: 1), match.captured(nth: 2) }; |
760 | std::sort(first: builds.begin(), last: builds.end()); |
761 | const QStringList expectedBuilds{"Debug" , "Release" }; |
762 | QCOMPARE(builds, expectedBuilds); |
763 | } |
764 | |
765 | QTEST_MAIN(tst_qmake) |
766 | #include "tst_qmake.moc" |
767 | |