blob: 8ab8d5d7214b356dc05b52f647960a890ca61281 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "testcompiler.h"
#include <QProcess>
#include <QDir>
QString TestCompiler::targetName(BuildType buildMode, const QString& target, const QString& version)
{
Q_UNUSED(version);
QString targetName = target;
#if defined (Q_OS_WIN32)
switch (buildMode)
{
case Exe: // app
targetName.append(".exe");
break;
case Dll: // dll
if (!version.isEmpty())
targetName.append(version.section(".", 0, 0));
targetName.append(".dll");
break;
case Lib: // lib
#ifdef Q_CC_GNU
targetName.prepend("lib");
targetName.append(".a");
#else
targetName.append(".lib");
#endif
break;
case Plain:
// no conversion
break;
}
#elif defined( Q_OS_MAC )
switch (buildMode)
{
case Exe: // app
targetName += ".app/Contents/MacOS/" + target.section('/', -1);
break;
case Dll: // dll
targetName.prepend("lib");
if (!version.isEmpty())
targetName.append('.' + version);
targetName.append(".dylib");
break;
case Lib: // lib
targetName.prepend("lib");
targetName.append(".a");
break;
case Plain:
// no conversion
break;
}
#else
switch (buildMode)
{
case Exe: // app
break;
case Dll: // dll
targetName.prepend("lib");
#if defined (Q_OS_HPUX) && !defined (__ia64)
targetName.append(".sl");
#elif defined (Q_OS_AIX)
targetName.append(".a");
#else
targetName.append(".so");
#endif
break;
case Lib: // lib
targetName.prepend("lib");
targetName.append(".a");
break;
case Plain:
// no conversion
break;
}
#endif
return targetName;
}
TestCompiler::TestCompiler()
{
setBaseCommands( "", "" );
}
TestCompiler::~TestCompiler()
{
}
bool TestCompiler::errorOut()
{
qDebug("%s", qPrintable(testOutput_.join(QStringLiteral("\n"))));
return false;
}
// Return the system environment, remove MAKEFLAGS variable in
// case the CI uses jom passing flags incompatible to nmake
// or vice versa.
static inline QStringList systemEnvironment()
{
#ifdef Q_OS_WIN
static QStringList result;
if (result.isEmpty()) {
foreach (const QString &variable, QProcess::systemEnvironment()) {
if (variable.startsWith(QStringLiteral("MAKEFLAGS="), Qt::CaseInsensitive)) {
qWarning("Removing environment setting '%s'", qPrintable(variable));
} else {
result.push_back(variable);
}
}
}
#else
static const QStringList result = QProcess::systemEnvironment();
#endif // ifdef Q_OS_WIN
return result;
}
bool TestCompiler::runCommand(const QString &cmd, const QStringList &args, bool expectFail)
{
QString dbg = cmd;
if (dbg.contains(' '))
dbg.prepend('"').append('"');
foreach (QString arg, args) {
if (arg.contains(' '))
arg.prepend('"').append('"');
dbg.append(' ').append(arg);
}
testOutput_.append("Running command: " + dbg);
QProcess child;
child.setEnvironment(systemEnvironment() + environment_);
child.start(cmd, args);
if (!child.waitForStarted(-1)) {
testOutput_.append( "Unable to start child process." );
return errorOut();
}
child.waitForFinished(-1);
bool ok = child.exitStatus() == QProcess::NormalExit && (expectFail ^ (child.exitCode() == 0));
for (auto channel : { QProcess::StandardOutput, QProcess::StandardError }) {
child.setReadChannel(channel);
while (!child.atEnd()) {
const QString output = QString::fromLocal8Bit(child.readLine());
if (output.startsWith("Project MESSAGE: FAILED"))
ok = false;
testOutput_.append(output);
}
}
return ok ? true : errorOut();
}
void TestCompiler::setBaseCommands( QString makeCmd, QString qmakeCmd )
{
makeCmd_ = makeCmd;
qmakeCmd_ = qmakeCmd;
}
void TestCompiler::resetArguments()
{
makeArgs_.clear();
qmakeArgs_.clear();
}
void TestCompiler::setArguments(const QStringList &makeArgs, const QStringList &qmakeArgs)
{
makeArgs_ = makeArgs;
qmakeArgs_ = qmakeArgs;
}
void TestCompiler::resetEnvironment()
{
environment_.clear();
}
void TestCompiler::addToEnvironment( QString varAssignment )
{
environment_.push_back(varAssignment);
}
bool TestCompiler::makeClean( const QString &workPath )
{
QDir D;
if (!D.exists(workPath)) {
testOutput_.append( "Directory '" + workPath + "' doesn't exist" );
return errorOut();
}
D.setCurrent(workPath);
QFileInfo Fi( workPath + "/Makefile");
if (Fi.exists())
// Run make clean
return runCommand(makeCmd_, QStringList() << "clean");
return true;
}
bool TestCompiler::makeDistClean( const QString &workPath )
{
QDir D;
if (!D.exists(workPath)) {
testOutput_.append( "Directory '" + workPath + "' doesn't exist" );
return errorOut();
}
D.setCurrent(workPath);
QFileInfo Fi( workPath + "/Makefile");
if (Fi.exists())
// Run make distclean
return runCommand(makeCmd_, QStringList() << "distclean");
return true;
}
bool TestCompiler::qmakeProject( const QString &workDir, const QString &proName )
{
QDir D;
if (!D.exists(workDir)) {
testOutput_.append( "Directory '" + workDir + "' doesn't exist" );
return errorOut();
}
D.setCurrent(workDir);
QString projectFile = proName;
if (!projectFile.endsWith(".pro"))
projectFile += ".pro";
return runCommand(qmakeCmd_, QStringList() << "-project" << "-o" << projectFile << "DESTDIR=./");
}
bool TestCompiler::qmake(const QString &workDir, const QString &proName, const QString &buildDir,
const QStringList &additionalArguments)
{
QDir D;
D.setCurrent( workDir );
if (D.exists("Makefile"))
D.remove("Makefile");
QString projectFile = proName;
QString makeFile = buildDir;
if (!projectFile.endsWith(".pro"))
projectFile += ".pro";
if (!makeFile.isEmpty() && !makeFile.endsWith('/'))
makeFile += '/';
makeFile += "Makefile";
// Now start qmake and generate the makefile
return runCommand(qmakeCmd_, QStringList(qmakeArgs_) << projectFile << "-o" << makeFile
<< additionalArguments);
}
bool TestCompiler::qmake(const QString &workDir, const QStringList &arguments)
{
QDir d;
d.setCurrent(workDir); // ### runCommand should take a workingDir argument instead
return runCommand(qmakeCmd_, arguments);
}
bool TestCompiler::make( const QString &workPath, const QString &target, bool expectFail )
{
QDir D;
D.setCurrent( workPath );
QStringList args = makeArgs_;
if (makeCmd_.contains("nmake", Qt::CaseInsensitive) ||
makeCmd_.contains("jom", Qt::CaseInsensitive)) {
args << "/NOLOGO";
}
if (!target.isEmpty())
args << target;
return runCommand(makeCmd_, args, expectFail);
}
bool TestCompiler::exists( const QString &destDir, const QString &exeName, BuildType buildType, const QString &version )
{
QFileInfo f(destDir + QLatin1Char('/') + targetName(buildType, exeName, version));
return f.exists();
}
bool TestCompiler::removeMakefile( const QString &workPath )
{
return removeFile( workPath, "Makefile" );
}
bool TestCompiler::removeProject( const QString &workPath, const QString &project )
{
QString projectFile = project;
if (!projectFile.endsWith(".pro"))
projectFile += ".pro";
return removeFile( workPath, projectFile );
}
bool TestCompiler::removeFile( const QString &workPath, const QString &fileName )
{
QDir D;
D.setCurrent( workPath );
return ( D.exists( fileName ) ) ? D.remove( fileName ) : true;
}
QString TestCompiler::commandOutput() const
{
#ifndef Q_OS_WIN
return testOutput_.join("\n");
#else
return testOutput_.join("\r\n");
#endif
}
void TestCompiler::clearCommandOutput()
{
testOutput_.clear();
}