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 "testcompiler.h"
30
31#include <QProcess>
32#include <QDir>
33
34QString TestCompiler::targetName(BuildType buildMode, const QString& target, const QString& version)
35{
36 Q_UNUSED(version);
37 QString targetName = target;
38
39#if defined (Q_OS_WIN32)
40 switch (buildMode)
41 {
42 case Exe: // app
43 targetName.append(".exe");
44 break;
45 case Dll: // dll
46 if (!version.isEmpty())
47 targetName.append(version.section(".", 0, 0));
48 targetName.append(".dll");
49 break;
50 case Lib: // lib
51#ifdef Q_CC_GNU
52 targetName.prepend("lib");
53 targetName.append(".a");
54#else
55 targetName.append(".lib");
56#endif
57 break;
58 case Plain:
59 // no conversion
60 break;
61 }
62#elif defined( Q_OS_MAC )
63 switch (buildMode)
64 {
65 case Exe: // app
66 targetName += ".app/Contents/MacOS/" + target.section('/', -1);
67 break;
68 case Dll: // dll
69 targetName.prepend("lib");
70 if (!version.isEmpty())
71 targetName.append('.' + version);
72 targetName.append(".dylib");
73 break;
74 case Lib: // lib
75 targetName.prepend("lib");
76 targetName.append(".a");
77 break;
78 case Plain:
79 // no conversion
80 break;
81 }
82#else
83 switch (buildMode)
84 {
85 case Exe: // app
86 break;
87 case Dll: // dll
88 targetName.prepend(s: "lib");
89#if defined (Q_OS_HPUX) && !defined (__ia64)
90 targetName.append(".sl");
91#elif defined (Q_OS_AIX)
92 targetName.append(".a");
93#else
94 targetName.append(s: ".so");
95#endif
96 break;
97 case Lib: // lib
98 targetName.prepend(s: "lib");
99 targetName.append(s: ".a");
100 break;
101 case Plain:
102 // no conversion
103 break;
104 }
105#endif
106 return targetName;
107}
108
109TestCompiler::TestCompiler()
110{
111 setBaseCommands( makeCmd: "", qmakeCmd: "" );
112}
113
114TestCompiler::~TestCompiler()
115{
116}
117
118bool TestCompiler::errorOut()
119{
120 qDebug(msg: "%s", qPrintable(testOutput_.join(QStringLiteral("\n"))));
121 return false;
122}
123
124// Return the system environment, remove MAKEFLAGS variable in
125// case the CI uses jom passing flags incompatible to nmake
126// or vice versa.
127static inline QStringList systemEnvironment()
128{
129#ifdef Q_OS_WIN
130 static QStringList result;
131 if (result.isEmpty()) {
132 foreach (const QString &variable, QProcess::systemEnvironment()) {
133 if (variable.startsWith(QStringLiteral("MAKEFLAGS="), Qt::CaseInsensitive)) {
134 qWarning("Removing environment setting '%s'", qPrintable(variable));
135 } else {
136 result.push_back(variable);
137 }
138 }
139 }
140#else
141 static const QStringList result = QProcess::systemEnvironment();
142#endif // ifdef Q_OS_WIN
143 return result;
144}
145
146bool TestCompiler::runCommand(const QString &cmd, const QStringList &args, bool expectFail)
147{
148 QString dbg = cmd;
149 if (dbg.contains(c: ' '))
150 dbg.prepend(c: '"').append(c: '"');
151 foreach (QString arg, args) {
152 if (arg.contains(c: ' '))
153 arg.prepend(c: '"').append(c: '"');
154 dbg.append(c: ' ').append(s: arg);
155 }
156 testOutput_.append(t: "Running command: " + dbg);
157
158 QProcess child;
159 child.setEnvironment(systemEnvironment() + environment_);
160
161 child.start(program: cmd, arguments: args);
162 if (!child.waitForStarted(msecs: -1)) {
163 testOutput_.append( t: "Unable to start child process." );
164 return errorOut();
165 }
166
167 child.waitForFinished(msecs: -1);
168 bool ok = child.exitStatus() == QProcess::NormalExit && (expectFail ^ (child.exitCode() == 0));
169
170 for (auto channel : { QProcess::StandardOutput, QProcess::StandardError }) {
171 child.setReadChannel(channel);
172 while (!child.atEnd()) {
173 const QString output = QString::fromLocal8Bit(str: child.readLine());
174 if (output.startsWith(s: "Project MESSAGE: FAILED"))
175 ok = false;
176 testOutput_.append(t: output);
177 }
178 }
179
180 return ok ? true : errorOut();
181}
182
183void TestCompiler::setBaseCommands( QString makeCmd, QString qmakeCmd )
184{
185 makeCmd_ = makeCmd;
186 qmakeCmd_ = qmakeCmd;
187}
188
189void TestCompiler::resetArguments()
190{
191 makeArgs_.clear();
192 qmakeArgs_.clear();
193}
194
195void TestCompiler::setArguments(const QStringList &makeArgs, const QStringList &qmakeArgs)
196{
197 makeArgs_ = makeArgs;
198 qmakeArgs_ = qmakeArgs;
199}
200
201void TestCompiler::resetEnvironment()
202{
203 environment_.clear();
204}
205
206void TestCompiler::addToEnvironment( QString varAssignment )
207{
208 environment_.push_back(t: varAssignment);
209}
210
211bool TestCompiler::makeClean( const QString &workPath )
212{
213 QDir D;
214 if (!D.exists(name: workPath)) {
215 testOutput_.append( t: "Directory '" + workPath + "' doesn't exist" );
216 return errorOut();
217 }
218
219 D.setCurrent(workPath);
220 QFileInfo Fi( workPath + "/Makefile");
221 if (Fi.exists())
222 // Run make clean
223 return runCommand(cmd: makeCmd_, args: QStringList() << "clean");
224
225 return true;
226}
227
228bool TestCompiler::makeDistClean( const QString &workPath )
229{
230 QDir D;
231 if (!D.exists(name: workPath)) {
232 testOutput_.append( t: "Directory '" + workPath + "' doesn't exist" );
233 return errorOut();
234 }
235
236 D.setCurrent(workPath);
237 QFileInfo Fi( workPath + "/Makefile");
238 if (Fi.exists())
239 // Run make distclean
240 return runCommand(cmd: makeCmd_, args: QStringList() << "distclean");
241
242 return true;
243
244}
245
246bool TestCompiler::qmakeProject( const QString &workDir, const QString &proName )
247{
248 QDir D;
249 if (!D.exists(name: workDir)) {
250 testOutput_.append( t: "Directory '" + workDir + "' doesn't exist" );
251 return errorOut();
252 }
253 D.setCurrent(workDir);
254
255 QString projectFile = proName;
256 if (!projectFile.endsWith(s: ".pro"))
257 projectFile += ".pro";
258
259 return runCommand(cmd: qmakeCmd_, args: QStringList() << "-project" << "-o" << projectFile << "DESTDIR=./");
260}
261
262bool TestCompiler::qmake(const QString &workDir, const QString &proName, const QString &buildDir,
263 const QStringList &additionalArguments)
264{
265 QDir D;
266 D.setCurrent( workDir );
267
268 if (D.exists(name: "Makefile"))
269 D.remove(fileName: "Makefile");
270
271 QString projectFile = proName;
272 QString makeFile = buildDir;
273 if (!projectFile.endsWith(s: ".pro"))
274 projectFile += ".pro";
275 if (!makeFile.isEmpty() && !makeFile.endsWith(c: '/'))
276 makeFile += '/';
277 makeFile += "Makefile";
278
279 // Now start qmake and generate the makefile
280 return runCommand(cmd: qmakeCmd_, args: QStringList(qmakeArgs_) << projectFile << "-o" << makeFile
281 << additionalArguments);
282}
283
284bool TestCompiler::qmake(const QString &workDir, const QStringList &arguments)
285{
286 QDir d;
287 d.setCurrent(workDir); // ### runCommand should take a workingDir argument instead
288 return runCommand(cmd: qmakeCmd_, args: arguments);
289}
290
291bool TestCompiler::make( const QString &workPath, const QString &target, bool expectFail )
292{
293 QDir D;
294 D.setCurrent( workPath );
295
296 QStringList args = makeArgs_;
297 if (makeCmd_.contains(s: "nmake", cs: Qt::CaseInsensitive) ||
298 makeCmd_.contains(s: "jom", cs: Qt::CaseInsensitive)) {
299 args << "/NOLOGO";
300 }
301 if (!target.isEmpty())
302 args << target;
303
304 return runCommand(cmd: makeCmd_, args, expectFail);
305}
306
307bool TestCompiler::exists( const QString &destDir, const QString &exeName, BuildType buildType, const QString &version )
308{
309 QFileInfo f(destDir + QLatin1Char('/') + targetName(buildMode: buildType, target: exeName, version));
310 return f.exists();
311}
312
313bool TestCompiler::removeMakefile( const QString &workPath )
314{
315 return removeFile( workPath, fileName: "Makefile" );
316}
317
318bool TestCompiler::removeProject( const QString &workPath, const QString &project )
319{
320 QString projectFile = project;
321 if (!projectFile.endsWith(s: ".pro"))
322 projectFile += ".pro";
323
324 return removeFile( workPath, fileName: projectFile );
325}
326
327bool TestCompiler::removeFile( const QString &workPath, const QString &fileName )
328{
329 QDir D;
330 D.setCurrent( workPath );
331
332 return ( D.exists( name: fileName ) ) ? D.remove( fileName ) : true;
333}
334
335QString TestCompiler::commandOutput() const
336{
337#ifndef Q_OS_WIN
338 return testOutput_.join(sep: "\n");
339#else
340 return testOutput_.join("\r\n");
341#endif
342}
343
344void TestCompiler::clearCommandOutput()
345{
346 testOutput_.clear();
347}
348

source code of qtbase/tests/auto/tools/qmake/testcompiler.cpp