You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
578 lines
21 KiB
578 lines
21 KiB
/***************************************************************************
|
|
copyright : (C) 2007 by David Nolden
|
|
email : david.nolden.kdevelop@art-master.de
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
/** Compatibility:
|
|
* make/automake: Should work perfectly
|
|
* cmake: Thanks to the path-recursion, this works with cmake(tested with version "2.4-patch 6" tested with kdelibs out-of-source and with kdevelop4 in-source)
|
|
*
|
|
*
|
|
* unsermake:
|
|
* unsermake is detected by reading the first line of the makefile. If it contains "generated by unsermake" the following things are respected:
|
|
* 1. Since unsermake does not have the -W command(which should tell it to recompile the given file no matter whether it has been changed or not), the file-modification-time of the file is changed temporarily and the --no-real-compare option is used to force recompilation.
|
|
* 2. The targets seem to be called *.lo instead of *.o when using unsermake, so *.lo names are used.
|
|
* example-(test)command: unsermake --no-real-compare -n myfile.lo
|
|
**/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <memory>
|
|
#include "kurl.h" /* defines KURL */
|
|
#include "qdir.h" /* defines QDir */
|
|
#include "qregexp.h" /* defines QRegExp */
|
|
#include "klocale.h" /* defines [function] i18n */
|
|
#include "blockingkprocess.h" /* defines BlockingKProcess */
|
|
#include "includepathresolver.h"
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef TEST
|
|
#include "blockingkprocess.cpp"
|
|
|
|
#include <iostream>
|
|
using namespace std;
|
|
#endif
|
|
|
|
#ifndef TEST
|
|
#define ifTest(x) {}
|
|
#else
|
|
#define ifTest(x) x
|
|
#endif
|
|
|
|
///After how many seconds should we retry?
|
|
#define CACHE_FAIL_FOR_SECONDS 200
|
|
|
|
using namespace CppTools;
|
|
|
|
|
|
namespace CppTools {
|
|
///Helper-class used to fake file-modification times
|
|
class FileModificationTimeWrapper {
|
|
public:
|
|
///@param files list of files that should be fake-modified(modtime will be set to current time)
|
|
FileModificationTimeWrapper( const QStringList& files = QStringList() ) : m_newTime( time(0) ) {
|
|
for( QStringList::const_iterator it = files.begin(); it != files.end(); ++it ) {
|
|
ifTest( cout << "touching " << (*it).ascii() << endl );
|
|
struct stat s;
|
|
if( stat( (*it).local8Bit().data(), &s ) == 0 ) {
|
|
///Success
|
|
m_stat[*it] = s;
|
|
///change the modification-time to m_newTime
|
|
struct timeval times[2];
|
|
times[0].tv_sec = m_newTime;
|
|
times[0].tv_usec = 0;
|
|
times[1].tv_sec = m_newTime;
|
|
times[1].tv_usec = 0;
|
|
|
|
if( utimes( (*it).local8Bit().data(), times ) != 0 )
|
|
{
|
|
ifTest( cout << "failed to touch " << (*it).ascii() << endl );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Not used yet, might be used to return LD_PRELOAD=.. FAKE_MODIFIED=.. etc. later
|
|
QString commandPrefix() const {
|
|
return QString();
|
|
}
|
|
|
|
///Undo changed modification-times
|
|
void unModify() {
|
|
for( StatMap::const_iterator it = m_stat.begin(); it != m_stat.end(); ++it ) {
|
|
|
|
ifTest( cout << "untouching " << it.key().ascii() << endl );
|
|
|
|
struct stat s;
|
|
if( stat( it.key().local8Bit().data(), &s ) == 0 ) {
|
|
if( s.st_mtime == m_newTime ) {
|
|
///Still the modtime that we've set, change it back
|
|
struct timeval times[2];
|
|
times[0].tv_usec = 0;
|
|
times[0].tv_sec = s.st_atime;
|
|
times[1].tv_usec = 0;
|
|
times[1].tv_sec = (*it).st_mtime;
|
|
if( utimes( it.key().local8Bit().data(), times ) != 0 ) {
|
|
ifTest( cout << "failed to untouch " << it.key().ascii() << endl );
|
|
}
|
|
} else {
|
|
///The file was modified since we changed the modtime
|
|
ifTest( cout << " will not untouch " << it.key().ascii() << " because the modification-time has changed" << endl );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
~FileModificationTimeWrapper() {
|
|
unModify();
|
|
}
|
|
|
|
private:
|
|
typedef QMap<QString, struct stat> StatMap;
|
|
StatMap m_stat;
|
|
time_t m_newTime;
|
|
};
|
|
|
|
class SourcePathInformation {
|
|
public:
|
|
SourcePathInformation( const QString& path ) : m_path( path ), m_isUnsermake(false), m_shouldTouchFiles(false) {
|
|
m_isUnsermake = isUnsermakePrivate( path );
|
|
|
|
ifTest( if( m_isUnsermake ) cout << "unsermake detected" << endl );
|
|
}
|
|
|
|
bool isUnsermake() const {
|
|
return m_isUnsermake;
|
|
}
|
|
|
|
///When this is set, the file-modification times are changed no matter whether it is unsermake or make
|
|
void setShouldTouchFiles(bool b) {
|
|
m_shouldTouchFiles = b;
|
|
}
|
|
|
|
QString getCommand( const QString& sourceFile, const QString& makeParameters ) const {
|
|
if( isUnsermake() )
|
|
return "unsermake -k --no-real-compare -n " + makeParameters;
|
|
else
|
|
return "make -k --no-print-directory -W \'" + sourceFile + "\' -n " + makeParameters;
|
|
}
|
|
|
|
bool hasMakefile() const {
|
|
QFileInfo makeFile( m_path, "Makefile" );
|
|
return makeFile.exists();
|
|
}
|
|
|
|
bool shouldTouchFiles() const {
|
|
return isUnsermake() || m_shouldTouchFiles;
|
|
}
|
|
|
|
QStringList possibleTargets( const QString& targetBaseName ) const {
|
|
QStringList ret;
|
|
if( isUnsermake() ) {
|
|
//unsermake breaks if the first given target does not exist, so in worst-case 2 calls are necessary
|
|
ret << targetBaseName + ".lo";
|
|
ret << targetBaseName + ".o";
|
|
} else {
|
|
//It would be nice if both targets could be processed in one call, the problem is the exit-status of make, so for now make has to be called twice.
|
|
ret << targetBaseName + ".o";
|
|
ret << targetBaseName + ".lo";
|
|
//ret << targetBaseName + ".lo " + targetBaseName + ".o";
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
private:
|
|
bool isUnsermakePrivate( const QString& path ) {
|
|
bool ret = false;
|
|
QFileInfo makeFile( path, "Makefile" );
|
|
QFile f( makeFile.absFilePath() );
|
|
if( f.open( IO_ReadOnly ) ) {
|
|
QString firstLine;
|
|
f.readLine( firstLine, 1000 );
|
|
if( firstLine.find( "generated by unsermake" ) != -1 ) {
|
|
ret = true;
|
|
}
|
|
f.close();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QString m_path;
|
|
bool m_isUnsermake;
|
|
bool m_shouldTouchFiles;
|
|
};
|
|
|
|
};
|
|
|
|
bool IncludePathResolver::executeCommandPopen ( const QString& command, const QString& workingDirectory, QString& result ) const
|
|
{
|
|
ifTest( cout << "executing " << command.ascii() << endl );
|
|
|
|
char* oldWd = getcwd(0,0);
|
|
chdir( workingDirectory.local8Bit() );
|
|
|
|
FILE* fp;
|
|
const int BUFSIZE = 2048;
|
|
char buf [BUFSIZE];
|
|
|
|
result = QString();
|
|
|
|
int status = 1;
|
|
if ((fp = popen(command.local8Bit(), "r")) != NULL) {
|
|
while (fgets(buf, sizeof (buf), fp))
|
|
result += QString(buf);
|
|
|
|
status = pclose(fp);
|
|
}
|
|
|
|
if( oldWd ) {
|
|
chdir( oldWd );
|
|
free( oldWd );
|
|
}
|
|
return status == 0;
|
|
}
|
|
|
|
IncludePathResolver::IncludePathResolver( bool continueEventLoop ) : m_isResolving(false), m_outOfSource(false), m_continueEventLoop(continueEventLoop) {
|
|
/* m_continueEventLoop = false;
|
|
#warning DEBUGGING TEST, REMOVE THIS*/
|
|
}
|
|
|
|
///More efficient solution: Only do exactly one call for each directory. During that call, mark all source-files as changed, and make all targets for those files.
|
|
PathResolutionResult IncludePathResolver::resolveIncludePath( const QString& file ) {
|
|
QFileInfo fi( file );
|
|
return resolveIncludePath( fi.fileName(), fi.dirPath(true) );
|
|
}
|
|
|
|
PathResolutionResult IncludePathResolver::resolveIncludePath( const QString& file, const QString& workingDirectory ) {
|
|
|
|
struct Enabler {
|
|
bool& b;
|
|
Enabler( bool& bb ) : b(bb) {
|
|
b = true;
|
|
}
|
|
~Enabler() {
|
|
b = false;
|
|
}
|
|
};
|
|
|
|
if( m_isResolving )
|
|
return PathResolutionResult(false, i18n("tried include-path-resolution while another resolution-process was still running") );
|
|
|
|
Enabler e( m_isResolving );
|
|
|
|
///STEP 1: CACHING
|
|
QDir dir( workingDirectory );
|
|
dir = QDir( dir.absPath() );
|
|
QFileInfo makeFile( dir, "Makefile" );
|
|
if( !makeFile.exists() )
|
|
return PathResolutionResult(false, i18n("Makefile is missing in folder \"%1\"").arg(dir.absPath()), i18n("problem while trying to resolve include-paths for %1").arg(file) );
|
|
|
|
QStringList cachedPath; //If the call doesn't succeed, use the cached not up-to-date version
|
|
QDateTime makeFileModification = makeFile.lastModified();
|
|
Cache::iterator it = m_cache.find( dir.path() );
|
|
if( it != m_cache.end() ) {
|
|
cachedPath = (*it).path;
|
|
if( makeFileModification == (*it).modificationTime ) {
|
|
if( !(*it).failed ) {
|
|
//We have a valid cached result
|
|
PathResolutionResult ret(true);
|
|
ret.path = (*it).path;
|
|
return ret;
|
|
} else {
|
|
//We have a cached failed result. We should use that for some time but then try again. Return the failed result if: ( there were too many tries within this folder OR this file was already tried ) AND The last tries have not expired yet
|
|
if( /*((*it).failedFiles.size() > 3 || (*it).failedFiles.find( file ) != (*it).failedFiles.end()) &&*/ (*it).failTime.secsTo( QDateTime::currentDateTime() ) < CACHE_FAIL_FOR_SECONDS ) {
|
|
PathResolutionResult ret(false); //Fake that the result is ok
|
|
ret.errorMessage = i18n("Cached: ") + (*it).errorMessage;
|
|
ret.longErrorMessage = (*it).longErrorMessage;
|
|
ret.path = (*it).path;
|
|
return ret;
|
|
} else {
|
|
//Try getting a correct result again
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///STEP 1: Prepare paths
|
|
QString targetName;
|
|
QFileInfo fi( file );
|
|
|
|
QString absoluteFile = file;
|
|
if( !file.startsWith("/") )
|
|
absoluteFile = dir.path() + "/" + file;
|
|
KURL u( absoluteFile );
|
|
u.cleanPath();
|
|
absoluteFile = u.path();
|
|
|
|
int dot;
|
|
if( (dot = file.findRev( '.' )) == -1 )
|
|
return PathResolutionResult( false, i18n( "Filename %1 seems to be malformed" ).arg(file) );
|
|
|
|
targetName = file.left( dot );
|
|
|
|
QString wd = dir.path();
|
|
if( !wd.startsWith("/") ) {
|
|
wd = QDir::currentDirPath() + "/" + wd;
|
|
KURL u( wd );
|
|
u.cleanPath();
|
|
wd = u.path();
|
|
}
|
|
if( m_outOfSource ) {
|
|
if( wd.startsWith( m_source ) ) {
|
|
//Move the current working-directory out of source, into the build-system
|
|
wd = m_build + "/" + wd.mid( m_source.length() );
|
|
KURL u( wd );
|
|
u.cleanPath();
|
|
wd = u.path();
|
|
}
|
|
}
|
|
|
|
SourcePathInformation source( wd );
|
|
QStringList possibleTargets = source.possibleTargets( targetName );
|
|
|
|
source.setShouldTouchFiles(true); //Think about whether this should be always enabled. I've enabled it for now so there's an even bigger chance that everything works.
|
|
|
|
///STEP 3: Try resolving the paths, by using once the absolute and once the relative file-path. Which kind is required differs from setup to setup.
|
|
|
|
///STEP 3.1: Try resolution using the absolute path
|
|
PathResolutionResult res;
|
|
//Try for each possible target
|
|
for( QStringList::const_iterator it = possibleTargets.begin(); it != possibleTargets.end(); ++it ) {
|
|
res = resolveIncludePathInternal( absoluteFile, wd, *it, source );
|
|
if( res ) break;
|
|
}
|
|
if( res ) {
|
|
CacheEntry ce;
|
|
ce.errorMessage = res.errorMessage;
|
|
ce.longErrorMessage = res.longErrorMessage;
|
|
ce.modificationTime = makeFileModification;
|
|
ce.path = res.path;
|
|
m_cache[dir.path()] = ce;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
///STEP 3.2: Try resolution using the relative path
|
|
QString relativeFile = KURL::relativePath(wd, absoluteFile);
|
|
for( QStringList::const_iterator it = possibleTargets.begin(); it != possibleTargets.end(); ++it ) {
|
|
res = resolveIncludePathInternal( relativeFile, wd, *it, source );
|
|
if( res ) break;
|
|
}
|
|
|
|
if( res.path.isEmpty() )
|
|
res.path = cachedPath; //We failed, maybe there is an old cached result, use that.
|
|
|
|
if( it == m_cache.end() )
|
|
it = m_cache.insert( dir.path(), CacheEntry() );
|
|
|
|
CacheEntry& ce(*it);
|
|
ce.modificationTime = makeFileModification;
|
|
ce.path = res.path;
|
|
if( !res ) {
|
|
ce.failed = true;
|
|
ce.errorMessage = res.errorMessage;
|
|
ce.longErrorMessage = res.longErrorMessage;
|
|
ce.failTime = QDateTime::currentDateTime();
|
|
ce.failedFiles[file] = true;
|
|
} else {
|
|
ce.failed = false;
|
|
ce.failedFiles.clear();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
PathResolutionResult IncludePathResolver::getFullOutput( const QString& command, const QString& workingDirectory, QString& output ) const{
|
|
if( m_continueEventLoop ) {
|
|
BlockingKProcess proc;
|
|
proc.setWorkingDirectory( workingDirectory );
|
|
proc.setUseShell( true );
|
|
proc << command;
|
|
if ( !proc.start(KProcess::NotifyOnExit, KProcess::Stdout) ) {
|
|
return PathResolutionResult( false, i18n("Could not start the make-process") );
|
|
}
|
|
|
|
output = proc.stdOut();
|
|
if( proc.exitStatus() != 0 )
|
|
return PathResolutionResult( false, i18n("make-process finished with nonzero exit-status"), i18n("output: %1").arg( output ) );
|
|
} else {
|
|
bool ret = executeCommandPopen(command, workingDirectory, output);
|
|
|
|
if( !ret )
|
|
return PathResolutionResult( false, i18n("make-process failed"), i18n("output: %1").arg( output ) );
|
|
}
|
|
return PathResolutionResult(true);
|
|
}
|
|
|
|
PathResolutionResult IncludePathResolver::resolveIncludePathInternal( const QString& file, const QString& workingDirectory, const QString& makeParameters, const SourcePathInformation& source ) {
|
|
|
|
QString processStdout;
|
|
|
|
QStringList touchFiles;
|
|
if( source.shouldTouchFiles() )
|
|
touchFiles << file;
|
|
|
|
FileModificationTimeWrapper touch( touchFiles );
|
|
|
|
QString fullOutput;
|
|
PathResolutionResult res = getFullOutput( source.getCommand( file, makeParameters ), workingDirectory, fullOutput );
|
|
if( !res )
|
|
return res;
|
|
|
|
QRegExp newLineRx("\\\\\\n");
|
|
fullOutput.replace( newLineRx, "" );
|
|
///@todo collect multiple outputs at the same time for performance-reasons
|
|
QString firstLine = fullOutput;
|
|
int lineEnd;
|
|
if( (lineEnd = fullOutput.find('\n')) != -1 )
|
|
firstLine.truncate( lineEnd ); //Only look at the first line of output
|
|
|
|
/**
|
|
* There's two possible cases this can currently handle.
|
|
* 1.: gcc is called, with the parameters we are searching for(so we parse the parameters)
|
|
* 2.: A recursive make is called, within another directory(so we follow the recursion and try again) "cd /foo/bar && make -f pi/pa/build.make pi/pa/po.o
|
|
* */
|
|
|
|
|
|
///STEP 1: Test if it is a recursive make-call
|
|
QRegExp makeRx( "\\bmake\\s" );
|
|
int offset = 0;
|
|
while( (offset = makeRx.search( firstLine, offset )) != -1 )
|
|
{
|
|
QString prefix = firstLine.left( offset ).stripWhiteSpace();
|
|
if( prefix.endsWith( "&&") || prefix.endsWith( ";" ) || prefix.isEmpty() )
|
|
{
|
|
QString newWorkingDirectory = workingDirectory;
|
|
///Extract the new working-directory
|
|
if( !prefix.isEmpty() ) {
|
|
if( prefix.endsWith( "&&" ) )
|
|
prefix.truncate( prefix.length() - 2 );
|
|
else if( prefix.endsWith( ";" ) )
|
|
prefix.truncate( prefix.length() - 1 );
|
|
///Now test if what we have as prefix is a simple "cd /foo/bar" call.
|
|
if( prefix.startsWith( "cd ") && !prefix.contains( ";") && !prefix.contains("&&") ) {
|
|
newWorkingDirectory = prefix.right( prefix.length() - 3 ).stripWhiteSpace();
|
|
if( !newWorkingDirectory.startsWith("/") )
|
|
newWorkingDirectory = workingDirectory + "/" + newWorkingDirectory;
|
|
KURL u( newWorkingDirectory );
|
|
u.cleanPath();
|
|
newWorkingDirectory = u.path();
|
|
}
|
|
}
|
|
QFileInfo d( newWorkingDirectory );
|
|
if( d.exists() ) {
|
|
///The recursive working-directory exists.
|
|
QString makeParams = firstLine.mid( offset+5 );
|
|
if( !makeParams.contains( ";" ) && !makeParams.contains( "&&" ) ) {
|
|
///Looks like valid parameters
|
|
///Make the file-name absolute, so it can be referenced from any directory
|
|
QString absoluteFile = file;
|
|
if( !absoluteFile.startsWith("/") )
|
|
absoluteFile = workingDirectory + "/" + file;
|
|
KURL u( absoluteFile );
|
|
u.cleanPath();
|
|
///Try once with absolute, and if that fails with relative path of the file
|
|
SourcePathInformation newSource( newWorkingDirectory );
|
|
PathResolutionResult res = resolveIncludePathInternal( u.path(), newWorkingDirectory, makeParams, newSource );
|
|
if( res )
|
|
return res;
|
|
return resolveIncludePathInternal( KURL::relativePath(newWorkingDirectory,u.path()), newWorkingDirectory, makeParams , newSource );
|
|
}else{
|
|
return PathResolutionResult( false, i18n("Recursive make-call failed"), i18n("The parameter-string \"%1\" does not seem to be valid. Output was: %2").arg(makeParams).arg(fullOutput) );
|
|
}
|
|
} else {
|
|
return PathResolutionResult( false, i18n("Recursive make-call failed"), i18n("The directory \"%1\" does not exist. Output was: %2").arg(newWorkingDirectory).arg(fullOutput) );
|
|
}
|
|
|
|
} else {
|
|
return PathResolutionResult( false, i18n("Recursive make-call malformed"), i18n("Output was: %2").arg(fullOutput) );
|
|
}
|
|
|
|
++offset;
|
|
if( offset >= firstLine.length() ) break;
|
|
}
|
|
|
|
///STEP 2: Search the output for include-paths
|
|
QRegExp validRx( "\\b([cg]\\+\\+|gcc)" );
|
|
if( validRx.search( fullOutput ) == -1 )
|
|
return PathResolutionResult( false, i18n("Output seems not to be a valid gcc or g++ call"), i18n("Folder: \"%1\" Command: \"%2\" Output: \"%3\"").arg(workingDirectory).arg( source.getCommand(file, makeParameters) ).arg(fullOutput) );
|
|
|
|
PathResolutionResult ret( true );
|
|
ret.longErrorMessage = fullOutput;
|
|
|
|
QString includeParameterRx( "\\s(-I|--include-dir=|-I\\s)" );
|
|
QString quotedRx( "(\\').*(\\')|(\\\").*(\\\")" ); //Matches "hello", 'hello', 'hello"hallo"', etc.
|
|
QString escapedPathRx( "(([^)(\"'\\s]*)(\\\\\\s)?)*" ); //Matches /usr/I\ am \ a\ strange\ path/include
|
|
|
|
QRegExp includeRx( QString( "%1(%2|%3)(?=\\s)" ).arg( includeParameterRx ).arg( quotedRx ).arg( escapedPathRx ) );
|
|
includeRx.setMinimal( true );
|
|
includeRx.setCaseSensitive( true );
|
|
offset = 0;
|
|
while( (offset = includeRx.search( fullOutput, offset )) != -1 ) {
|
|
offset += 1; ///The previous white space
|
|
int pathOffset = 2;
|
|
if( fullOutput[offset+1] == '-' ) {
|
|
///Must be --include-dir=, with a length of 14 characters
|
|
pathOffset = 14;
|
|
}
|
|
if( fullOutput.length() <= offset + pathOffset )
|
|
break;
|
|
|
|
if( fullOutput[offset+pathOffset].isSpace() )
|
|
pathOffset++;
|
|
|
|
|
|
|
|
int start = offset + pathOffset;
|
|
int end = offset + includeRx.matchedLength();
|
|
|
|
QString path = fullOutput.mid( start, end-start ).stripWhiteSpace();
|
|
if( path.startsWith( "\"") || path.startsWith( "\'") && path.length() > 2 ) {
|
|
//probable a quoted path
|
|
if( path.endsWith(path.left(1)) ) {
|
|
//Quotation is ok, remove it
|
|
path = path.mid( 1, path.length() - 2 );
|
|
}
|
|
}
|
|
if( !path.startsWith("/") )
|
|
path = workingDirectory + (workingDirectory.endsWith("/") ? "" : "/") + path;
|
|
|
|
KURL u( path );
|
|
u.cleanPath();
|
|
|
|
ret.path << u.path();
|
|
|
|
offset = end-1;
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
void IncludePathResolver::setOutOfSourceBuildSystem( const QString& source, const QString& build ) {
|
|
m_outOfSource = true;
|
|
m_source = source;
|
|
m_build = build;
|
|
}
|
|
|
|
#ifdef TEST
|
|
/** This can be used for testing and debugging the system. To compile it use
|
|
* gcc includepathresolver.cpp -I /usr/share/qt3/include -I /usr/include/kde -I ../../lib/util -DTEST -lkdecore -g -o includepathresolver
|
|
* */
|
|
|
|
int main(int argc, char **argv) {
|
|
QApplication app(argc,argv);
|
|
IncludePathResolver resolver;
|
|
if( argc < 3 ) {
|
|
cout << "params: 1. file-name, 2. working-directory [3. source-directory 4. build-directory]" << endl;
|
|
return 1;
|
|
}
|
|
if( argc >= 5 ) {
|
|
cout << "mapping " << argv[3] << " -> " << argv[4] << endl;
|
|
resolver.setOutOfSourceBuildSystem( argv[3], argv[4] );
|
|
}
|
|
PathResolutionResult res = resolver.resolveIncludePath( argv[1], argv[2] );
|
|
cout << "success: " << res.success << "\n";
|
|
if( !res.success ) {
|
|
cout << "error-message: \n" << res.errorMessage << "\n";
|
|
cout << "long error-message: \n" << res.longErrorMessage << "\n";
|
|
}
|
|
cout << "path: \n" << res.path.join("\n");
|
|
return res.success;
|
|
}
|
|
|
|
#endif
|