mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
521 lines
18 KiB
C++
521 lines
18 KiB
C++
/* KDevelop CMake Support
|
|
*
|
|
* Copyright 2009 Andreas Pakulat <apaku@gmx.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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#include "cmakeutils.h"
|
|
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtCore/QDir>
|
|
|
|
#include <kconfig.h>
|
|
#include <klocale.h>
|
|
#include <kconfiggroup.h>
|
|
#include <kurl.h>
|
|
#include <kparts/mainwindow.h>
|
|
#include <kdebug.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <project/projectmodel.h>
|
|
#include <interfaces/iproject.h>
|
|
#include <interfaces/icore.h>
|
|
#include <interfaces/iuicontroller.h>
|
|
|
|
#include "icmakedocumentation.h"
|
|
#include "cmakebuilddirchooser.h"
|
|
#include "settings/cmakecachemodel.h"
|
|
#include <interfaces/idocumentationcontroller.h>
|
|
#include <interfaces/iplugincontroller.h>
|
|
|
|
namespace Config
|
|
{
|
|
namespace Old
|
|
{
|
|
static const QString currentBuildDirKey = "CurrentBuildDir";
|
|
static const QString currentCMakeBinaryKey = "Current CMake Binary";
|
|
static const QString currentBuildTypeKey = "CurrentBuildType";
|
|
static const QString currentInstallDirKey = "CurrentInstallDir";
|
|
static const QString currentEnvironmentKey = "CurrentEnvironment";
|
|
static const QString currentExtraArgumentsKey = "Extra Arguments";
|
|
static const QString projectRootRelativeKey = "ProjectRootRelative";
|
|
static const QString projectBuildDirs = "BuildDirs";
|
|
}
|
|
|
|
static const QString buildDirIndexKey = "Current Build Directory Index";
|
|
static const QString buildDirOverrideIndexKey = "Temporary Build Directory Index";
|
|
static const QString buildDirCountKey = "Build Directory Count";
|
|
|
|
namespace Specific
|
|
{
|
|
static const QString buildDirPathKey = "Build Directory Path";
|
|
static const QString cmakeBinKey = "CMake Binary";
|
|
static const QString cmakeBuildTypeKey = "Build Type";
|
|
static const QString cmakeInstallDirKey = "Install Directory";
|
|
static const QString cmakeEnvironmentKey = "Environment Profile";
|
|
static const QString cmakeArgumentsKey = "Extra Arguments";
|
|
}
|
|
|
|
static const QString groupNameBuildDir = "CMake Build Directory %1";
|
|
static const QString groupName = "CMake";
|
|
|
|
} // namespace Config
|
|
|
|
namespace
|
|
{
|
|
|
|
KConfigGroup baseGroup( KDevelop::IProject* project )
|
|
{
|
|
if (!project)
|
|
return KConfigGroup();
|
|
|
|
return project->projectConfiguration()->group( Config::groupName );
|
|
}
|
|
|
|
KConfigGroup buildDirGroup( KDevelop::IProject* project, int buildDirIndex )
|
|
{
|
|
return baseGroup(project).group( Config::groupNameBuildDir.arg(buildDirIndex) );
|
|
}
|
|
|
|
bool buildDirGroupExists( KDevelop::IProject* project, int buildDirIndex )
|
|
{
|
|
return baseGroup(project).hasGroup( Config::groupNameBuildDir.arg(buildDirIndex) );
|
|
}
|
|
|
|
int currentBuildDirIndex( KDevelop::IProject* project )
|
|
{
|
|
KConfigGroup baseGrp = baseGroup(project);
|
|
|
|
if ( baseGrp.hasKey( Config::buildDirOverrideIndexKey ) )
|
|
return baseGrp.readEntry<int>( Config::buildDirOverrideIndexKey, 0 );
|
|
|
|
else
|
|
return baseGrp.readEntry<int>( Config::buildDirIndexKey, 0 ); // default is 0 because QString::number(0) apparently returns an empty string
|
|
}
|
|
|
|
QString readProjectParameter( KDevelop::IProject* project, const QString& key, const QString& aDefault )
|
|
{
|
|
int buildDirIndex = currentBuildDirIndex(project);
|
|
if (buildDirIndex >= 0)
|
|
return buildDirGroup( project, buildDirIndex ).readEntry( key, aDefault );
|
|
|
|
else
|
|
return aDefault;
|
|
}
|
|
|
|
void writeProjectParameter( KDevelop::IProject* project, const QString& key, const QString& value )
|
|
{
|
|
int buildDirIndex = currentBuildDirIndex(project);
|
|
if (buildDirIndex >= 0)
|
|
{
|
|
KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex );
|
|
buildDirGrp.writeEntry( key, value );
|
|
}
|
|
|
|
else
|
|
{
|
|
kWarning() << "cannot write key" << key << "(" << value << ")" << "when no builddir is set!";
|
|
}
|
|
}
|
|
|
|
void writeProjectBaseParameter( KDevelop::IProject* project, const QString& key, const QString& value )
|
|
{
|
|
KConfigGroup baseGrp = baseGroup(project);
|
|
baseGrp.writeEntry( key, value );
|
|
}
|
|
|
|
inline QString replaceBuildDir(QString in, QString buildDir)
|
|
{
|
|
return in.replace("#[bin_dir]", buildDir);
|
|
}
|
|
|
|
inline QString replaceInstallDir(QString in, QString installDir)
|
|
{
|
|
return in.replace("#[install_dir]", installDir);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace CMake
|
|
{
|
|
|
|
KDevelop::Path::List resolveSystemDirs(KDevelop::IProject* project, const QStringList& dirs)
|
|
{
|
|
const KDevelop::Path buildDir(CMake::currentBuildDir(project));
|
|
const KDevelop::Path installDir(CMake::currentInstallDir(project));
|
|
|
|
KDevelop::Path::List newList;
|
|
newList.reserve(dirs.size());
|
|
foreach(const QString& s, dirs)
|
|
{
|
|
KDevelop::Path dir;
|
|
if(s.startsWith(QString::fromUtf8("#[bin_dir]")))
|
|
{
|
|
dir = KDevelop::Path(buildDir, s);
|
|
}
|
|
else if(s.startsWith(QString::fromUtf8("#[install_dir]")))
|
|
{
|
|
dir = KDevelop::Path(installDir, s);
|
|
}
|
|
else
|
|
{
|
|
dir = KDevelop::Path(s);
|
|
}
|
|
|
|
// kDebug(9042) << "resolved" << s << "to" << d;
|
|
|
|
if (!newList.contains(dir))
|
|
{
|
|
newList.append(dir);
|
|
}
|
|
}
|
|
return newList;
|
|
}
|
|
|
|
///NOTE: when you change this, update @c defaultConfigure in cmakemanagertest.cpp
|
|
bool checkForNeedingConfigure( KDevelop::IProject* project )
|
|
{
|
|
KUrl builddir = currentBuildDir(project);
|
|
if( !builddir.isValid() )
|
|
{
|
|
CMakeBuildDirChooser bd;
|
|
|
|
KUrl folderUrl=project->folder();
|
|
QString relative=CMake::projectRootRelative(project);
|
|
folderUrl.cd(relative);
|
|
|
|
bd.setSourceFolder( folderUrl );
|
|
bd.setAlreadyUsed( CMake::allBuildDirs(project) );
|
|
bd.setCMakeBinary(KStandardDirs::findExe("cmake"));
|
|
|
|
if( !bd.exec() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QString newbuilddir = bd.buildFolder().toLocalFile( KUrl::RemoveTrailingSlash );
|
|
int addedBuildDirIndex = buildDirCount( project ); // old count is the new index
|
|
|
|
// Initialize the kconfig items with the values from the dialog, this ensures the settings
|
|
// end up in the config file once the changes are saved
|
|
kDebug( 9042 ) << "adding to cmake config: new builddir index" << addedBuildDirIndex;
|
|
kDebug( 9042 ) << "adding to cmake config: builddir path " << bd.buildFolder().url();
|
|
kDebug( 9042 ) << "adding to cmake config: installdir " << bd.installPrefix().url();
|
|
kDebug( 9042 ) << "adding to cmake config: extra args" << bd.extraArguments();
|
|
kDebug( 9042 ) << "adding to cmake config: build type " << bd.buildType();
|
|
kDebug( 9042 ) << "adding to cmake config: cmake binary " << bd.cmakeBinary().url();
|
|
kDebug( 9042 ) << "adding to cmake config: environment <null>";
|
|
CMake::setBuildDirCount( project, addedBuildDirIndex + 1 );
|
|
CMake::setCurrentBuildDirIndex( project, addedBuildDirIndex );
|
|
CMake::setCurrentBuildDir( project, bd.buildFolder() );
|
|
CMake::setCurrentInstallDir( project, bd.installPrefix() );
|
|
CMake::setCurrentExtraArguments( project, bd.extraArguments() );
|
|
CMake::setCurrentBuildType( project, bd.buildType() );
|
|
CMake::setCurrentCMakeBinary( project, bd.cmakeBinary() );
|
|
CMake::setCurrentEnvironment( project, QString() );
|
|
|
|
return true;
|
|
} else if( !QFileInfo( builddir.toLocalFile() + "/CMakeCache.txt" ).exists() ||
|
|
!(QFileInfo( builddir.toLocalFile() + "/Makefile" ).exists() || QFileInfo( builddir.toLocalFile() + "/build.ninja" ).exists())
|
|
) //TODO: maybe we could use the builder for that?
|
|
{
|
|
// User entered information already, but cmake hasn't actually been run yet.
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
KUrl projectRoot(KDevelop::IProject* project)
|
|
{
|
|
if (!project) {
|
|
return KUrl();
|
|
}
|
|
|
|
KUrl projectPath = project->folder();
|
|
|
|
// We need this as its own variable as .cd mutates the object and would be
|
|
// turned into a no-op by assert
|
|
const bool correct = projectPath.cd(CMake::projectRootRelative(project));
|
|
Q_ASSERT(correct);
|
|
Q_UNUSED(correct);
|
|
|
|
return projectPath;
|
|
}
|
|
|
|
KUrl currentBuildDir( KDevelop::IProject* project )
|
|
{
|
|
return readProjectParameter( project, Config::Specific::buildDirPathKey, QString() );
|
|
}
|
|
|
|
QString currentBuildType( KDevelop::IProject* project )
|
|
{
|
|
return readProjectParameter( project, Config::Specific::cmakeBuildTypeKey, "Release" );
|
|
}
|
|
|
|
KUrl currentCMakeBinary( KDevelop::IProject* project )
|
|
{
|
|
return readProjectParameter( project, Config::Specific::cmakeBinKey, KStandardDirs::findExe( "cmake" ) );
|
|
}
|
|
|
|
KUrl currentInstallDir( KDevelop::IProject* project )
|
|
{
|
|
return readProjectParameter( project, Config::Specific::cmakeInstallDirKey, "/usr/local" );
|
|
}
|
|
|
|
QString projectRootRelative( KDevelop::IProject* project )
|
|
{
|
|
return baseGroup(project).readEntry( Config::Old::projectRootRelativeKey, "." );
|
|
}
|
|
|
|
bool hasProjectRootRelative(KDevelop::IProject* project)
|
|
{
|
|
return baseGroup(project).hasKey( Config::Old::projectRootRelativeKey );
|
|
}
|
|
|
|
QString currentExtraArguments( KDevelop::IProject* project )
|
|
{
|
|
return readProjectParameter( project, Config::Specific::cmakeArgumentsKey, QString() );
|
|
}
|
|
|
|
void setCurrentInstallDir( KDevelop::IProject* project, const KUrl& url )
|
|
{
|
|
writeProjectParameter( project, Config::Specific::cmakeInstallDirKey, url.url() );
|
|
}
|
|
|
|
void setCurrentBuildType( KDevelop::IProject* project, const QString& type )
|
|
{
|
|
writeProjectParameter( project, Config::Specific::cmakeBuildTypeKey, type );
|
|
}
|
|
|
|
void setCurrentCMakeBinary( KDevelop::IProject* project, const KUrl& url )
|
|
{
|
|
writeProjectParameter( project, Config::Specific::cmakeBinKey, url.url() );
|
|
}
|
|
|
|
void setCurrentBuildDir( KDevelop::IProject* project, const KUrl& url )
|
|
{
|
|
writeProjectParameter( project, Config::Specific::buildDirPathKey, url.url() );
|
|
}
|
|
|
|
void setProjectRootRelative( KDevelop::IProject* project, const QString& relative)
|
|
{
|
|
writeProjectBaseParameter( project, Config::Old::projectRootRelativeKey, relative );
|
|
}
|
|
|
|
void setCurrentExtraArguments( KDevelop::IProject* project, const QString& string)
|
|
{
|
|
writeProjectParameter( project, Config::Specific::cmakeArgumentsKey, string );
|
|
}
|
|
|
|
QString currentEnvironment(KDevelop::IProject* project)
|
|
{
|
|
return readProjectParameter( project, Config::Specific::cmakeEnvironmentKey, QString() );
|
|
}
|
|
|
|
|
|
int currentBuildDirIndex( KDevelop::IProject* project )
|
|
{
|
|
KConfigGroup baseGrp = baseGroup(project);
|
|
|
|
if ( baseGrp.hasKey( Config::buildDirOverrideIndexKey ) )
|
|
return baseGrp.readEntry<int>( Config::buildDirOverrideIndexKey, 0 );
|
|
|
|
else
|
|
return baseGrp.readEntry<int>( Config::buildDirIndexKey, 0 ); // default is 0 because QString::number(0) apparently returns an empty string
|
|
}
|
|
|
|
void setCurrentBuildDirIndex( KDevelop::IProject* project, int buildDirIndex )
|
|
{
|
|
writeProjectBaseParameter( project, Config::buildDirIndexKey, QString::number (buildDirIndex) );
|
|
}
|
|
|
|
void setCurrentEnvironment( KDevelop::IProject* project, const QString& environment )
|
|
{
|
|
writeProjectParameter( project, Config::Specific::cmakeEnvironmentKey, environment );
|
|
}
|
|
|
|
void initBuildDirConfig( KDevelop::IProject* project )
|
|
{
|
|
int buildDirIndex = currentBuildDirIndex( project );
|
|
if (buildDirCount(project) <= buildDirIndex )
|
|
setBuildDirCount( project, buildDirIndex + 1 );
|
|
}
|
|
|
|
int buildDirCount( KDevelop::IProject* project )
|
|
{
|
|
return baseGroup(project).readEntry<int>( Config::buildDirCountKey, 0 );
|
|
}
|
|
|
|
void setBuildDirCount( KDevelop::IProject* project, int count )
|
|
{
|
|
writeProjectBaseParameter( project, Config::buildDirCountKey, QString::number(count) );
|
|
}
|
|
|
|
void removeBuildDirConfig( KDevelop::IProject* project )
|
|
{
|
|
int buildDirIndex = currentBuildDirIndex( project );
|
|
if ( !buildDirGroupExists( project, buildDirIndex ) )
|
|
{
|
|
kWarning() << "build directory config" << buildDirIndex << "to be removed but does not exist";
|
|
return;
|
|
}
|
|
|
|
int bdCount = buildDirCount(project);
|
|
setBuildDirCount( project, bdCount - 1 );
|
|
removeOverrideBuildDirIndex( project );
|
|
setCurrentBuildDirIndex( project, -1 );
|
|
|
|
// move (rename) the upper config groups to keep the numbering
|
|
// if there's nothing to move, just delete the group physically
|
|
if (buildDirIndex + 1 == bdCount)
|
|
buildDirGroup( project, buildDirIndex ).deleteGroup();
|
|
|
|
else for (int i = buildDirIndex + 1; i < bdCount; ++i)
|
|
{
|
|
KConfigGroup src = buildDirGroup( project, i );
|
|
KConfigGroup dest = buildDirGroup( project, i - 1 );
|
|
dest.deleteGroup();
|
|
src.copyTo(&dest);
|
|
src.deleteGroup();
|
|
}
|
|
}
|
|
|
|
void updateConfig( KDevelop::IProject* project, int buildDirIndex, CMakeCacheModel* model )
|
|
{
|
|
if (buildDirIndex < 0)
|
|
return;
|
|
|
|
KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex );
|
|
bool deleteModel = false;
|
|
if (!model)
|
|
{
|
|
KUrl cacheFilePath( buildDirGrp.readEntry( Config::Specific::buildDirPathKey, QString() ) );
|
|
cacheFilePath.addPath("CMakeCache.txt");
|
|
|
|
if( QFile::exists( cacheFilePath.toLocalFile() ) )
|
|
{
|
|
model = new CMakeCacheModel( 0, cacheFilePath );
|
|
deleteModel = true;
|
|
}
|
|
}
|
|
if (!model)
|
|
return;
|
|
|
|
buildDirGrp.writeEntry( Config::Specific::cmakeBinKey, KUrl( model->value("CMAKE_COMMAND") ).url() );
|
|
buildDirGrp.writeEntry( Config::Specific::cmakeInstallDirKey, KUrl( model->value("CMAKE_INSTALL_PREFIX") ).url() );
|
|
buildDirGrp.writeEntry( Config::Specific::cmakeBuildTypeKey, model->value("CMAKE_BUILD_TYPE") );
|
|
if (deleteModel)
|
|
delete model;
|
|
}
|
|
|
|
void attemptMigrate( KDevelop::IProject* project )
|
|
{
|
|
if ( !baseGroup(project).hasKey( Config::Old::projectBuildDirs ) )
|
|
{
|
|
kDebug() << "CMake settings migration: already done, exiting";
|
|
return;
|
|
}
|
|
|
|
KConfigGroup baseGrp = baseGroup(project);
|
|
|
|
KUrl buildDir( baseGrp.readEntry( Config::Old::currentBuildDirKey, QString() ) );
|
|
int buildDirIndex = -1;
|
|
QStringList existingBuildDirs;
|
|
{
|
|
// the directories are originally stored in a path list, so need to convert them to URLs
|
|
QStringList existingBuildDirPathes = baseGrp.readEntry( Config::Old::projectBuildDirs, QStringList() );
|
|
|
|
// also, find current build directory in this list (we need an index, not path)
|
|
QString currentBuildDirCanonicalPath = QDir( buildDir.toLocalFile() ).canonicalPath();
|
|
|
|
for( int i = 0; i < existingBuildDirPathes.count(); ++i )
|
|
{
|
|
const QString& nextBuildDir = existingBuildDirPathes.at(i);
|
|
existingBuildDirs += KUrl(nextBuildDir).url();
|
|
if( QDir(nextBuildDir).canonicalPath() == currentBuildDirCanonicalPath )
|
|
{
|
|
buildDirIndex = i;
|
|
}
|
|
}
|
|
}
|
|
int buildDirsCount = existingBuildDirs.count();
|
|
|
|
kDebug() << "CMake settings migration: existing build directories" << existingBuildDirs;
|
|
kDebug() << "CMake settings migration: build directory count" << buildDirsCount;
|
|
kDebug() << "CMake settings migration: current build directory" << buildDir << "(index" << buildDirIndex << ")";
|
|
|
|
baseGrp.writeEntry( Config::buildDirCountKey, buildDirsCount );
|
|
baseGrp.writeEntry( Config::buildDirIndexKey, buildDirIndex );
|
|
|
|
for (int i = 0; i < buildDirsCount; ++i)
|
|
{
|
|
kDebug() << "CMake settings migration: writing group" << i << ": path" << existingBuildDirs.at(i);
|
|
|
|
KConfigGroup buildDirGrp = buildDirGroup( project, i );
|
|
buildDirGrp.writeEntry( Config::Specific::buildDirPathKey, KUrl( existingBuildDirs.at(i) ).url() );
|
|
}
|
|
|
|
baseGrp.deleteEntry( Config::Old::currentBuildDirKey );
|
|
baseGrp.deleteEntry( Config::Old::currentCMakeBinaryKey );
|
|
baseGrp.deleteEntry( Config::Old::currentBuildTypeKey );
|
|
baseGrp.deleteEntry( Config::Old::currentInstallDirKey );
|
|
baseGrp.deleteEntry( Config::Old::currentEnvironmentKey );
|
|
baseGrp.deleteEntry( Config::Old::currentExtraArgumentsKey );
|
|
baseGrp.deleteEntry( Config::Old::projectBuildDirs );
|
|
}
|
|
|
|
void setOverrideBuildDirIndex( KDevelop::IProject* project, int overrideBuildDirIndex )
|
|
{
|
|
writeProjectBaseParameter( project, Config::buildDirOverrideIndexKey, QString::number(overrideBuildDirIndex) );
|
|
}
|
|
|
|
void removeOverrideBuildDirIndex( KDevelop::IProject* project, bool writeToMainIndex )
|
|
{
|
|
KConfigGroup baseGrp = baseGroup(project);
|
|
|
|
if( !baseGrp.hasKey(Config::buildDirOverrideIndexKey) )
|
|
return;
|
|
if( writeToMainIndex )
|
|
baseGrp.writeEntry( Config::buildDirIndexKey, baseGrp.readEntry(Config::buildDirOverrideIndexKey) );
|
|
|
|
baseGrp.deleteEntry(Config::buildDirOverrideIndexKey);
|
|
}
|
|
|
|
ICMakeDocumentation* cmakeDocumentation()
|
|
{
|
|
ICMakeDocumentation* p=KDevelop::ICore::self()->pluginController()->extensionForPlugin<ICMakeDocumentation>("org.kdevelop.ICMakeDocumentation");
|
|
|
|
if( !p )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Q_ASSERT(p);
|
|
return p;
|
|
}
|
|
|
|
QStringList allBuildDirs(KDevelop::IProject* project)
|
|
{
|
|
QStringList result;
|
|
int bdCount = buildDirCount(project);
|
|
for (int i = 0; i < bdCount; ++i)
|
|
result += KUrl( buildDirGroup( project, i ).readEntry( Config::Specific::buildDirPathKey ) ).toLocalFile(KUrl::RemoveTrailingSlash);
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|