2010-09-27 15:51:49 +02:00
/**************************************************************************
* *
* * This file is part of Qt Creator
* *
2011-01-11 16:28:15 +01:00
* * Copyright ( c ) 2011 Nokia Corporation and / or its subsidiary ( - ies ) .
2010-09-27 15:51:49 +02:00
* *
2011-04-13 08:42:33 +02:00
* * Contact : Nokia Corporation ( info @ qt . nokia . com )
2010-09-27 15:51:49 +02:00
* *
* *
* * GNU Lesser General Public License Usage
* *
2011-04-13 08:42:33 +02:00
* * This file may be used under the terms of the GNU Lesser General Public
* * License version 2.1 as published by the Free Software Foundation and
* * appearing in the file LICENSE . LGPL included in the packaging of this file .
* * Please review the following information to ensure the GNU Lesser General
* * Public License version 2.1 requirements will be met :
* * http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
2010-09-27 15:51:49 +02:00
* *
2010-12-17 16:01:08 +01:00
* * In addition , as a special exception , Nokia gives you certain additional
2011-04-13 08:42:33 +02:00
* * rights . These rights are described in the Nokia Qt LGPL Exception
2010-12-17 16:01:08 +01:00
* * version 1.1 , included in the file LGPL_EXCEPTION . txt in this package .
* *
2011-04-13 08:42:33 +02:00
* * Other Usage
* *
* * Alternatively , this file may be used in accordance with the terms and
* * conditions contained in a signed written agreement between you and Nokia .
* *
2010-12-17 16:01:08 +01:00
* * If you have questions regarding the use of this file , please contact
* * Nokia at qt - info @ nokia . com .
2010-09-27 15:51:49 +02:00
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "buildablehelperlibrary.h"
# include <QtCore/QFileInfo>
# include <QtCore/QCoreApplication>
# include <QtCore/QHash>
# include <QtCore/QProcess>
# include <QtCore/QDir>
# include <QtCore/QDateTime>
# include <utils/environment.h>
# include <utils/synchronousprocess.h>
# include <QtGui/QDesktopServices>
2011-01-24 12:29:48 +01:00
# include <QtCore/QDebug>
2010-09-27 15:51:49 +02:00
namespace Utils {
QString BuildableHelperLibrary : : findSystemQt ( const Utils : : Environment & env )
{
QStringList paths = env . path ( ) ;
foreach ( const QString & path , paths ) {
2010-11-08 11:28:54 +01:00
QString prefix = path ;
if ( ! prefix . endsWith ( QLatin1Char ( ' / ' ) ) )
prefix . append ( QLatin1Char ( ' / ' ) ) ;
2010-09-27 15:51:49 +02:00
foreach ( const QString & possibleCommand , possibleQMakeCommands ( ) ) {
2010-11-08 11:28:54 +01:00
const QFileInfo qmake ( prefix + possibleCommand ) ;
2010-09-27 15:51:49 +02:00
if ( qmake . exists ( ) ) {
if ( ! qtVersionForQMake ( qmake . absoluteFilePath ( ) ) . isNull ( ) ) {
return qmake . absoluteFilePath ( ) ;
}
}
}
}
return QString ( ) ;
}
QString BuildableHelperLibrary : : qtInstallDataDir ( const QString & qmakePath )
{
QProcess proc ;
proc . start ( qmakePath , QStringList ( ) < < QLatin1String ( " -query " ) < < QLatin1String ( " QT_INSTALL_DATA " ) ) ;
if ( proc . waitForFinished ( ) )
return QString ( proc . readAll ( ) . trimmed ( ) ) ;
return QString ( ) ;
}
QString BuildableHelperLibrary : : qtVersionForQMake ( const QString & qmakePath )
{
if ( qmakePath . isEmpty ( ) )
return QString ( ) ;
QProcess qmake ;
qmake . start ( qmakePath , QStringList ( QLatin1String ( " --version " ) ) ) ;
if ( ! qmake . waitForStarted ( ) ) {
qWarning ( " Cannot start '%s': %s " , qPrintable ( qmakePath ) , qPrintable ( qmake . errorString ( ) ) ) ;
return QString ( ) ;
}
if ( ! qmake . waitForFinished ( ) ) {
Utils : : SynchronousProcess : : stopProcess ( qmake ) ;
qWarning ( " Timeout running '%s'. " , qPrintable ( qmakePath ) ) ;
return QString ( ) ;
}
if ( qmake . exitStatus ( ) ! = QProcess : : NormalExit ) {
qWarning ( " '%s' crashed. " , qPrintable ( qmakePath ) ) ;
return QString ( ) ;
}
const QString output = QString : : fromLocal8Bit ( qmake . readAllStandardOutput ( ) ) ;
2010-11-08 09:41:40 +01:00
static QRegExp regexp ( QLatin1String ( " (QMake version|QMake version:)[ \\ s]*([ \\ d.]*) " ) ,
Qt : : CaseInsensitive ) ;
2010-09-27 15:51:49 +02:00
regexp . indexIn ( output ) ;
if ( regexp . cap ( 2 ) . startsWith ( QLatin1String ( " 2. " ) ) ) {
2010-11-08 09:41:40 +01:00
static QRegExp regexp2 ( QLatin1String ( " Using Qt version[ \\ s]*([ \\ d \\ .]*) " ) ,
Qt : : CaseInsensitive ) ;
2010-09-27 15:51:49 +02:00
regexp2 . indexIn ( output ) ;
const QString version = regexp2 . cap ( 1 ) ;
return version ;
}
return QString ( ) ;
}
QStringList BuildableHelperLibrary : : possibleQMakeCommands ( )
{
// On windows no one has renamed qmake, right?
# ifdef Q_OS_WIN
return QStringList ( QLatin1String ( " qmake.exe " ) ) ;
# else
// On unix some distributions renamed qmake to avoid clashes
QStringList result ;
result < < QLatin1String ( " qmake-qt4 " ) < < QLatin1String ( " qmake4 " ) < < QLatin1String ( " qmake " ) ;
return result ;
# endif
}
// Copy helper source files to a target directory, replacing older files.
bool BuildableHelperLibrary : : copyFiles ( const QString & sourcePath ,
const QStringList & files ,
const QString & targetDirectory ,
QString * errorMessage )
{
if ( ! QDir ( ) . mkpath ( targetDirectory ) ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::DebuggingHelperLibrary " , " The target directory %1 could not be created. " ) . arg ( targetDirectory ) ;
return false ;
}
foreach ( const QString & file , files ) {
const QString source = sourcePath + file ;
const QString dest = targetDirectory + file ;
const QFileInfo destInfo ( dest ) ;
if ( destInfo . exists ( ) ) {
if ( destInfo . lastModified ( ) > = QFileInfo ( source ) . lastModified ( ) )
continue ;
if ( ! QFile : : remove ( dest ) ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::DebuggingHelperLibrary " , " The existing file %1 could not be removed. " ) . arg ( destInfo . absoluteFilePath ( ) ) ;
return false ;
}
}
2010-12-10 13:09:28 +01:00
if ( ! destInfo . dir ( ) . exists ( ) ) {
QDir ( ) . mkpath ( destInfo . dir ( ) . absolutePath ( ) ) ;
}
2010-09-27 15:51:49 +02:00
if ( ! QFile : : copy ( source , dest ) ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::DebuggingHelperLibrary " , " The file %1 could not be copied to %2. " ) . arg ( source , dest ) ;
return false ;
}
}
return true ;
}
2010-10-08 15:13:02 +02:00
// Helper: Run a build process with merged stdout/stderr
static inline bool runBuildProcessI ( QProcess & proc ,
const QString & binary ,
const QStringList & args ,
int timeoutMS ,
bool ignoreNonNullExitCode ,
QString * output , QString * errorMessage )
{
proc . start ( binary , args ) ;
if ( ! proc . waitForStarted ( ) ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" Cannot start process: %1 " ) .
arg ( proc . errorString ( ) ) ;
return false ;
}
// Read stdout/err and check for timeouts
QByteArray stdOut ;
QByteArray stdErr ;
if ( ! SynchronousProcess : : readDataFromProcess ( proc , timeoutMS , & stdOut , & stdErr , false ) ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" Timeout after %1s. " ) .
arg ( timeoutMS / 1000 ) ;
SynchronousProcess : : stopProcess ( proc ) ;
return false ;
}
if ( proc . exitStatus ( ) ! = QProcess : : NormalExit ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" The process crashed. " ) ;
return false ;
}
const QString stdOutS = QString : : fromLocal8Bit ( stdOut ) ;
if ( ! ignoreNonNullExitCode & & proc . exitCode ( ) ! = 0 ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" The process returned exit code %1: \n %2 " ) .
arg ( proc . exitCode ( ) ) . arg ( stdOutS ) ;
return false ;
}
output - > append ( stdOutS ) ;
return true ;
}
// Run a build process with merged stdout/stderr and qWarn about errors.
static bool runBuildProcess ( QProcess & proc ,
const QString & binary ,
const QStringList & args ,
int timeoutMS ,
bool ignoreNonNullExitCode ,
QString * output , QString * errorMessage )
{
const bool rc = runBuildProcessI ( proc , binary , args , timeoutMS , ignoreNonNullExitCode , output , errorMessage ) ;
if ( ! rc ) {
// Fail - reformat error.
QString cmd = binary ;
if ( ! args . isEmpty ( ) ) {
cmd + = QLatin1Char ( ' ' ) ;
cmd + = args . join ( QString ( QLatin1Char ( ' ' ) ) ) ;
}
* errorMessage =
QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" Error running '%1' in %2: %3 " ) .
arg ( cmd , proc . workingDirectory ( ) , * errorMessage ) ;
qWarning ( " %s " , qPrintable ( * errorMessage ) ) ;
}
return rc ;
}
2011-04-29 13:35:19 +02:00
bool BuildableHelperLibrary : : buildHelper ( const BuildHelperArguments & arguments ,
QString * log , QString * errorMessage )
2010-09-27 15:51:49 +02:00
{
const QChar newline = QLatin1Char ( ' \n ' ) ;
// Setup process
QProcess proc ;
2011-04-29 13:35:19 +02:00
proc . setEnvironment ( arguments . environment . toStringList ( ) ) ;
proc . setWorkingDirectory ( arguments . directory ) ;
2010-09-27 15:51:49 +02:00
proc . setProcessChannelMode ( QProcess : : MergedChannels ) ;
2011-04-29 13:35:19 +02:00
log - > append ( QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" Building helper '%1' in %2 \n " ) . arg ( arguments . helperName ,
arguments . directory ) ) ;
log - > append ( newline ) ;
2010-09-27 15:51:49 +02:00
2011-04-29 13:35:19 +02:00
const QString makeFullPath = arguments . environment . searchInPath ( arguments . makeCommand ) ;
if ( QFileInfo ( arguments . directory + QLatin1String ( " /Makefile " ) ) . exists ( ) ) {
2010-10-08 15:13:02 +02:00
if ( makeFullPath . isEmpty ( ) ) {
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::DebuggingHelperLibrary " ,
2011-04-29 13:35:19 +02:00
" %1 not found in PATH \n " ) . arg ( arguments . makeCommand ) ;
2010-10-08 15:13:02 +02:00
return false ;
2010-09-27 15:51:49 +02:00
}
2010-10-08 15:13:02 +02:00
const QString cleanTarget = QLatin1String ( " distclean " ) ;
2011-04-29 13:35:19 +02:00
log - > append ( QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
2010-10-08 15:13:02 +02:00
" Running %1 %2... \n " ) . arg ( makeFullPath , cleanTarget ) ) ;
2011-04-29 13:35:19 +02:00
if ( ! runBuildProcess ( proc , makeFullPath , QStringList ( cleanTarget ) , 30000 , true , log , errorMessage ) )
2010-10-08 15:13:02 +02:00
return false ;
2010-09-27 15:51:49 +02:00
}
2011-01-12 16:24:00 +01:00
QStringList qmakeArgs ;
2011-04-29 13:35:19 +02:00
if ( ! arguments . targetMode . isEmpty ( ) )
qmakeArgs < < arguments . targetMode ;
if ( ! arguments . mkspec . isEmpty ( ) )
qmakeArgs < < QLatin1String ( " -spec " ) < < arguments . mkspec ;
qmakeArgs < < arguments . proFilename ;
qmakeArgs < < arguments . qmakeArguments ;
2011-02-10 15:24:49 +01:00
2011-04-29 13:35:19 +02:00
log - > append ( newline ) ;
log - > append ( QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" Running %1 %2 ... \n " ) . arg ( arguments . qmakeCommand ,
arguments . qmakeArguments . join ( " " ) ) ) ;
2011-02-10 15:24:49 +01:00
2011-04-29 13:35:19 +02:00
if ( ! runBuildProcess ( proc , arguments . qmakeCommand , arguments . qmakeArguments , 30000 , false , log , errorMessage ) )
2010-10-08 15:13:02 +02:00
return false ;
2011-04-29 13:35:19 +02:00
log - > append ( newline ) ;
2010-10-08 15:13:02 +02:00
if ( makeFullPath . isEmpty ( ) ) {
2011-04-29 13:35:19 +02:00
* errorMessage = QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" %1 not found in PATH \n " ) . arg ( arguments . makeCommand ) ;
2010-10-08 15:13:02 +02:00
return false ;
2010-09-27 15:51:49 +02:00
}
2011-04-29 13:35:19 +02:00
log - > append ( QCoreApplication : : translate ( " ProjectExplorer::BuildableHelperLibrary " ,
" Running %1 ... \n " ) . arg ( makeFullPath ) ) ;
if ( ! runBuildProcess ( proc , makeFullPath , QStringList ( ) , 120000 , false , log , errorMessage ) )
2010-10-08 15:13:02 +02:00
return false ;
return true ;
2010-09-27 15:51:49 +02:00
}
bool BuildableHelperLibrary : : getHelperFileInfoFor ( const QStringList & validBinaryFilenames ,
const QString & directory , QFileInfo * info )
{
if ( ! info )
return false ;
foreach ( const QString & binaryFilename , validBinaryFilenames ) {
info - > setFile ( directory + binaryFilename ) ;
if ( info - > exists ( ) )
return true ;
}
return false ;
}
2011-02-18 14:43:33 +01:00
QString BuildableHelperLibrary : : byInstallDataHelper ( const QString & sourcePath ,
const QStringList & sourceFileNames ,
2010-09-27 15:51:49 +02:00
const QStringList & installDirectories ,
2011-04-18 14:13:18 +02:00
const QStringList & validBinaryFilenames ,
bool acceptOutdatedHelper )
2010-09-27 15:51:49 +02:00
{
2011-02-18 14:43:33 +01:00
// find the latest change to the sources
QDateTime sourcesModified ;
2011-04-18 14:13:18 +02:00
if ( ! acceptOutdatedHelper ) {
foreach ( const QString & sourceFileName , sourceFileNames ) {
const QDateTime fileModified = QFileInfo ( sourcePath + sourceFileName ) . lastModified ( ) ;
if ( fileModified . isValid ( ) & & ( ! sourcesModified . isValid ( ) | | fileModified > sourcesModified ) )
sourcesModified = fileModified ;
}
2011-02-18 14:43:33 +01:00
}
2010-09-27 15:51:49 +02:00
// We pretend that the lastmodified of gdbmacros.cpp is 5 minutes before what the file system says
// Because afer a installation from the package the modified dates of gdbmacros.cpp
// and the actual library are close to each other, but not deterministic in one direction
2011-04-18 14:13:18 +02:00
if ( sourcesModified . isValid ( ) )
sourcesModified = sourcesModified . addSecs ( - 300 ) ;
2010-09-27 15:51:49 +02:00
// look for the newest helper library in the different locations
QString newestHelper ;
QDateTime newestHelperModified = sourcesModified ; // prevent using one that's older than the sources
QFileInfo fileInfo ;
foreach ( const QString & installDirectory , installDirectories ) {
if ( getHelperFileInfoFor ( validBinaryFilenames , installDirectory , & fileInfo ) ) {
2011-04-18 14:13:18 +02:00
if ( ! newestHelperModified . isValid ( )
| | ( fileInfo . lastModified ( ) > newestHelperModified ) ) {
2010-09-27 15:51:49 +02:00
newestHelper = fileInfo . filePath ( ) ;
newestHelperModified = fileInfo . lastModified ( ) ;
}
}
}
return newestHelper ;
}
} // namespace Utils