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.
1670 lines
60 KiB
1670 lines
60 KiB
/***************************************************************************
|
|
* Copyright (C) 2001-2002 by Bernd Gehrmann *
|
|
* bernd@kdevelop.org *
|
|
* Copyright (C) 2007 by 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "customprojectpart.h"
|
|
|
|
#include <tqapplication.h>
|
|
#include <tdeapplication.h>
|
|
#include <tqdir.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqregexp.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtabwidget.h>
|
|
#include <tqvaluestack.h>
|
|
#include <tqvbox.h>
|
|
#include <tqwhatsthis.h>
|
|
#include <tqdom.h>
|
|
|
|
#include <tdeaction.h>
|
|
#include <tdeconfig.h>
|
|
#include <kdebug.h>
|
|
#include <kdialogbase.h>
|
|
#include <keditlistbox.h>
|
|
#include <kdevgenericfactory.h>
|
|
#include <kiconloader.h>
|
|
#include <tdelocale.h>
|
|
#include <tdemainwindow.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tdeparts/part.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <tdeversion.h>
|
|
#include <kprocess.h>
|
|
|
|
#include "domutil.h"
|
|
#include "kdevcore.h"
|
|
#include "kdevmainwindow.h"
|
|
#include "kdevmakefrontend.h"
|
|
#include "kdevappfrontend.h"
|
|
#include "kdevpartcontroller.h"
|
|
#include "runoptionswidget.h"
|
|
#include "makeoptionswidget.h"
|
|
#include "custombuildoptionswidget.h"
|
|
#include "custommakeconfigwidget.h"
|
|
#include "customotherconfigwidget.h"
|
|
#include "custommanagerwidget.h"
|
|
#include "config.h"
|
|
#include "envvartools.h"
|
|
#include "urlutil.h"
|
|
|
|
#include "selectnewfilesdialog.h"
|
|
|
|
#include <kdevplugininfo.h>
|
|
|
|
typedef KDevGenericFactory<CustomProjectPart> CustomProjectFactory;
|
|
static const KDevPluginInfo data( "kdevcustomproject" );
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevcustomproject, CustomProjectFactory( data ) )
|
|
|
|
CustomProjectPart::CustomProjectPart( TQObject *parent, const char *name, const TQStringList & )
|
|
: KDevBuildTool( &data, parent, name ? name : "CustomProjectPart" )
|
|
, m_lastCompilationFailed( false ), m_recursive( false ), m_first_recursive( false )
|
|
{
|
|
setInstance( CustomProjectFactory::instance() );
|
|
setXMLFile( "kdevcustomproject.rc" );
|
|
|
|
m_executeAfterBuild = false;
|
|
|
|
TDEAction *action;
|
|
|
|
action = new TDEAction( i18n( "Re-Populate Project" ), 0, this, TQT_SLOT( populateProject() ), actionCollection(), "repopulate_project" );
|
|
action->setToolTip( i18n( "Re-Populate Project" ) );
|
|
action->setWhatsThis( i18n( "<b>Re-Populate Project</b><p>Re-Populates the project, searching through the project directory and adding all files that match one of the wildcards set in the custom manager options of the project filelist." ) );
|
|
|
|
action = new TDEAction( i18n( "&Build Project" ), "make_tdevelop", Key_F8,
|
|
this, TQT_SLOT( slotBuild() ),
|
|
actionCollection(), "build_build" );
|
|
action->setToolTip( i18n( "Build project" ) );
|
|
action->setWhatsThis( i18n( "<b>Build project</b><p>Runs <b>make</b> from the project directory.<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Build Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "&Build Active Directory" ), "make_tdevelop", Key_F7,
|
|
this, TQT_SLOT( slotBuildActiveDir() ),
|
|
actionCollection(), "build_buildactivetarget" );
|
|
action->setToolTip( i18n( "Build active directory" ) );
|
|
action->setWhatsThis( i18n( "<b>Build active directory</b><p>Constructs a series of make commands to build the active directory. "
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Make Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "Compile &File" ), "make_tdevelop",
|
|
this, TQT_SLOT( slotCompileFile() ),
|
|
actionCollection(), "build_compilefile" );
|
|
action->setToolTip( i18n( "Compile file" ) );
|
|
action->setWhatsThis( i18n( "<b>Compile file</b><p>Runs <b>make filename.o</b> command from the directory where 'filename' is the name of currently opened file.<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Build Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "Install" ), 0,
|
|
this, TQT_SLOT( slotInstall() ),
|
|
actionCollection(), "build_install" );
|
|
action->setToolTip( i18n( "Install" ) );
|
|
action->setWhatsThis( i18n( "<b>Install</b><p>Runs <b>make install</b> command from the project directory.<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Make Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "Install Active Directory" ), 0,
|
|
this, TQT_SLOT( slotInstallActiveDir() ),
|
|
actionCollection(), "build_installactivetarget" );
|
|
action->setToolTip( i18n( "Install active directory" ) );
|
|
action->setWhatsThis( i18n( "<b>Install active directory</b><p>Runs <b>make install</b> command from the active directory.<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Make Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "Install (as root user)" ), 0,
|
|
this, TQT_SLOT( slotInstallWithKdesu() ),
|
|
actionCollection(), "build_install_tdesu" );
|
|
action->setToolTip( i18n( "Install as root user" ) );
|
|
action->setWhatsThis( i18n( "<b>Install</b><p>Runs <b>make install</b> command from the project directory with root privileges.<br>"
|
|
"It is executed via tdesu command.<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Make Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "&Clean Project" ), 0,
|
|
this, TQT_SLOT( slotClean() ),
|
|
actionCollection(), "build_clean" );
|
|
action->setToolTip( i18n( "Clean project" ) );
|
|
action->setWhatsThis( i18n( "<b>Clean project</b><p>Runs <b>make clean</b> command from the project directory.<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Build Options</b> tab." ) );
|
|
|
|
action = new TDEAction( i18n( "Execute Program" ), "application-x-executable", 0,
|
|
this, TQT_SLOT( slotExecute() ),
|
|
actionCollection(), "build_execute" );
|
|
action->setToolTip( i18n( "Execute program" ) );
|
|
action->setWhatsThis( i18n( "<b>Execute program</b><p>Executes the main program specified in project settings, <b>Run Options</b> tab. "
|
|
"If it is not specified then the active target is used to determine the application to run." ) );
|
|
|
|
TDEActionMenu *menu = new TDEActionMenu( i18n( "Build &Target" ),
|
|
actionCollection(), "build_target" );
|
|
m_targetMenu = menu->popupMenu();
|
|
menu->setToolTip( i18n( "Build target" ) );
|
|
menu->setWhatsThis( i18n( "<b>Build target</b><p>Runs <b>make targetname</b> from the project directory (targetname is the name of the target selected).<br>"
|
|
"Environment variables and make arguments can be specified "
|
|
"in the project settings dialog, <b>Build Options</b> tab." ) );
|
|
|
|
m_targetObjectFilesMenu = new TQPopupMenu();
|
|
m_targetOtherFilesMenu = new TQPopupMenu();
|
|
|
|
m_makeEnvironmentsSelector = new TDESelectAction( i18n( "Make &Environment" ), 0,
|
|
actionCollection(), "build_make_environment" );
|
|
m_makeEnvironmentsSelector->setToolTip( i18n( "Make environment" ) );
|
|
m_makeEnvironmentsSelector->setWhatsThis( i18n( "<b>Make Environment</b><p> Choose the set of environment variables to be passed on to make.<br>"
|
|
"Environment variables can be specified in the project "
|
|
"settings dialog, <b>Build Options</b> tab." ) );
|
|
|
|
connect( m_targetMenu, TQT_SIGNAL( aboutToShow() ),
|
|
this, TQT_SLOT( updateTargetMenu() ) );
|
|
connect( m_targetMenu, TQT_SIGNAL( activated( int ) ),
|
|
this, TQT_SLOT( targetMenuActivated( int ) ) );
|
|
connect( m_targetObjectFilesMenu, TQT_SIGNAL( activated( int ) ),
|
|
this, TQT_SLOT( targetObjectFilesMenuActivated( int ) ) );
|
|
connect( m_targetOtherFilesMenu, TQT_SIGNAL( activated( int ) ),
|
|
this, TQT_SLOT( targetOtherFilesMenuActivated( int ) ) );
|
|
connect( m_makeEnvironmentsSelector->popupMenu(), TQT_SIGNAL( aboutToShow() ),
|
|
this, TQT_SLOT( updateMakeEnvironmentsMenu() ) );
|
|
connect( m_makeEnvironmentsSelector->popupMenu(), TQT_SIGNAL( activated( int ) ),
|
|
this, TQT_SLOT( makeEnvironmentsMenuActivated( int ) ) );
|
|
connect( core(), TQT_SIGNAL( projectConfigWidget( KDialogBase* ) ),
|
|
this, TQT_SLOT( projectConfigWidget( KDialogBase* ) ) );
|
|
connect( core(), TQT_SIGNAL( contextMenu( TQPopupMenu *, const Context * ) ),
|
|
this, TQT_SLOT( contextMenu( TQPopupMenu *, const Context * ) ) );
|
|
|
|
connect( makeFrontend(), TQT_SIGNAL( commandFinished( const TQString& ) ),
|
|
this, TQT_SLOT( slotCommandFinished( const TQString& ) ) );
|
|
connect( makeFrontend(), TQT_SIGNAL( commandFailed( const TQString& ) ),
|
|
this, TQT_SLOT( slotCommandFailed( const TQString& ) ) );
|
|
}
|
|
|
|
|
|
CustomProjectPart::~CustomProjectPart()
|
|
{}
|
|
|
|
|
|
void CustomProjectPart::projectConfigWidget( KDialogBase *dlg )
|
|
{
|
|
TQVBox *vbox;
|
|
vbox = dlg->addVBoxPage( i18n( "Custom Manager" ), i18n( "Custom Manager" ), BarIcon( "text-x-makefile", TDEIcon::SizeMedium ) );
|
|
CustomManagerWidget *w0 = new CustomManagerWidget( this, vbox );
|
|
connect( dlg, TQT_SIGNAL( okClicked() ), w0, TQT_SLOT( accept() ) );
|
|
|
|
vbox = dlg->addVBoxPage( i18n( "Run Options" ), i18n( "Run Options" ), BarIcon( "text-x-makefile", TDEIcon::SizeMedium ) );
|
|
RunOptionsWidget *w1 = new RunOptionsWidget( *projectDom(), "/kdevcustomproject", buildDirectory(), vbox );
|
|
connect( dlg, TQT_SIGNAL( okClicked() ), w1, TQT_SLOT( accept() ) );
|
|
vbox = dlg->addVBoxPage( i18n( "Build Options" ), i18n( "Build Options" ), BarIcon( "text-x-makefile", TDEIcon::SizeMedium ) );
|
|
TQTabWidget *buildtab = new TQTabWidget( vbox );
|
|
|
|
CustomBuildOptionsWidget *w2 = new CustomBuildOptionsWidget( *projectDom(), buildtab );
|
|
connect( dlg, TQT_SIGNAL( okClicked() ), w2, TQT_SLOT( accept() ) );
|
|
buildtab->addTab( w2, i18n( "&Build" ) );
|
|
|
|
CustomOtherConfigWidget *w4 = new CustomOtherConfigWidget( this, "/kdevcustomproject", buildtab );
|
|
connect( dlg, TQT_SIGNAL( okClicked() ), w4, TQT_SLOT( accept() ) );
|
|
buildtab->addTab( w4, i18n( "&Other" ) );
|
|
|
|
CustomMakeConfigWidget *w3 = new CustomMakeConfigWidget( this, "/kdevcustomproject", buildtab );
|
|
buildtab->addTab( w3, i18n( "Ma&ke" ) );
|
|
w2->setMakeOptionsWidget( buildtab, w3, w4 );
|
|
connect( dlg, TQT_SIGNAL( okClicked() ), w3, TQT_SLOT( accept() ) );
|
|
|
|
}
|
|
|
|
|
|
void CustomProjectPart::contextMenu( TQPopupMenu *popup, const Context *context )
|
|
{
|
|
if ( !context->hasType( Context::FileContext ) )
|
|
return;
|
|
|
|
const FileContext *fcontext = static_cast<const FileContext*>( context );
|
|
|
|
m_contextAddFiles.clear();
|
|
m_contextRemoveFiles.clear();
|
|
|
|
TQString popupstr = fcontext->urls().first().fileName();
|
|
|
|
if ( popupstr == TQString() )
|
|
popupstr = ".";
|
|
|
|
if ( fcontext->urls().size() == 1 && URLUtil::isDirectory( fcontext->urls().first() ) && !isInBlacklist( fcontext->urls().first().path() ) )
|
|
{
|
|
popup->insertSeparator();
|
|
// remember the name of the directory
|
|
m_contextDirName = fcontext->urls().first().path();
|
|
m_contextDirName = m_contextDirName.mid( project()->projectDirectory().length() + 1 );
|
|
int id = popup->insertItem( i18n( "Make Active Directory" ),
|
|
this, TQT_SLOT( slotChooseActiveDirectory() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Make active directory</b><p>"
|
|
"Chooses this directory as the destination for new files created using wizards "
|
|
"like the <i>New Class</i> wizard." ) );
|
|
}
|
|
|
|
kdDebug( 9025 ) << "context urls: " << fcontext->urls() << endl;
|
|
if ( fcontext->urls().size() == 1 && ( isProjectFileType( fcontext->urls().first().path() ) || URLUtil::isDirectory( fcontext->urls().first() ) ) )
|
|
{
|
|
popup->insertSeparator();
|
|
m_contextDirName = fcontext->urls().first().path();
|
|
m_contextDirName = m_contextDirName.mid( project()->projectDirectory().length() + 1 );
|
|
int id;
|
|
if ( isInBlacklist( m_contextDirName ) )
|
|
{
|
|
id = popup->insertItem( i18n( "Remove from blacklist" ),
|
|
this, TQT_SLOT( slotChangeBlacklist() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Remove from blacklist</b><p>"
|
|
"Removes the given file or directory from the "
|
|
"blacklist if it is already in it.<br>The blacklist contains files and"
|
|
" directories that should be ignored even if they match a project filetype "
|
|
"pattern" ) );
|
|
}
|
|
else
|
|
{
|
|
id = popup->insertItem( i18n( "Add to blacklist" ),
|
|
this, TQT_SLOT( slotChangeBlacklist() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Add to blacklist</b><p>"
|
|
"Adds the given file or directory to the blacklist.<br>The blacklist contains files and"
|
|
" directories that should be ignored even if they match a project filetype "
|
|
"pattern" ) );
|
|
}
|
|
}
|
|
|
|
const KURL::List urls = fcontext->urls();
|
|
|
|
bool dirAddRecursive = false;
|
|
bool dirDelRecursive = false;
|
|
|
|
for ( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it )
|
|
{
|
|
kdDebug( 9025 ) << "Checking URL: " << *it << endl;
|
|
TQString canPath( URLUtil::canonicalPath(( *it ).path() ) );
|
|
TQString relPath = relativeToProject( canPath );
|
|
kdDebug( 9025 ) << "relpath: " << relPath << "|canpath: " << canPath << endl;
|
|
if ( isInBlacklist( relPath ) )
|
|
continue;
|
|
if ((( *it ).isLocalFile() && isProjectFileType(( *it ).fileName() ) ) )
|
|
{
|
|
if ( project()->isProjectFile( canPath ) )
|
|
m_contextRemoveFiles << relPath;
|
|
if ( !project()->isProjectFile( canPath ) )
|
|
m_contextAddFiles << relPath;
|
|
}
|
|
if ( TQFileInfo(( *it ).path() ).isDir() )
|
|
{
|
|
if ( containsProjectFiles( canPath ) || project()->isProjectFile( canPath ) )
|
|
{
|
|
if ( containsProjectFiles( canPath ) )
|
|
dirDelRecursive = true;
|
|
m_contextRemoveFiles << relPath;
|
|
}
|
|
if ( containsNonProjectFiles( canPath ) || !project()->isProjectFile( canPath ) )
|
|
{
|
|
if ( containsNonProjectFiles( canPath ) )
|
|
dirAddRecursive = true;
|
|
m_contextAddFiles << relPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_contextAddFiles.size() > 0 || m_contextRemoveFiles.size() > 0 )
|
|
popup->insertSeparator();
|
|
if ( m_contextAddFiles.size() > 0 )
|
|
{
|
|
int id = popup->insertItem( i18n( "Add Selected File/Dir(s) to Project" ),
|
|
this, TQT_SLOT( slotAddToProject() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Add to project</b><p>Adds selected file/dir(s) to the list of files in the project. "
|
|
"Note that the files should be manually added to the corresponding makefile or build.xml." ) );
|
|
if ( dirAddRecursive )
|
|
{
|
|
int id = popup->insertItem( i18n( "Add Selected Dir(s) to Project (recursive)" ),
|
|
this, TQT_SLOT( slotAddToProjectRecursive() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Add to project</b><p>Recursively adds selected dir(s) to the list of files in the project. "
|
|
"Note that the files should be manually added to the corresponding makefile or build.xml." ) );
|
|
}
|
|
}
|
|
|
|
if ( m_contextRemoveFiles.size() > 0 )
|
|
{
|
|
int id = popup->insertItem( i18n( "Remove Selected File/Dir(s) From Project" ),
|
|
this, TQT_SLOT( slotRemoveFromProject() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Remove from project</b><p>Removes selected file/dir(s) from the list of files in the project. "
|
|
"Note that the files should be manually excluded from the corresponding makefile or build.xml." ) );
|
|
|
|
if ( dirDelRecursive )
|
|
{
|
|
int id = popup->insertItem( i18n( "Remove Selected Dir(s) From Project (recursive)" ),
|
|
this, TQT_SLOT( slotRemoveFromProjectRecursive() ) );
|
|
popup->setWhatsThis( id, i18n( "<b>Remove from project</b><p>Recursively removes selected dir(s) from the list of files in the project. "
|
|
"Note that the files should be manually excluded from the corresponding makefile or build.xml." ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotAddToProject()
|
|
{
|
|
m_recursive = false;
|
|
m_first_recursive = true;
|
|
addFiles( m_contextAddFiles );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotRemoveFromProject()
|
|
{
|
|
m_recursive = false;
|
|
m_first_recursive = true;
|
|
removeFiles( m_contextRemoveFiles );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotAddToProjectRecursive()
|
|
{
|
|
m_recursive = true;
|
|
addFiles( m_contextAddFiles );
|
|
m_recursive = false;
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotRemoveFromProjectRecursive()
|
|
{
|
|
m_recursive = true;
|
|
removeFiles( m_contextRemoveFiles );
|
|
m_recursive = false;
|
|
}
|
|
|
|
void CustomProjectPart::slotChangeBlacklist()
|
|
{
|
|
switchBlacklistEntry( m_contextDirName );
|
|
}
|
|
|
|
void CustomProjectPart::slotChooseActiveDirectory()
|
|
{
|
|
TQString olddir = activeDirectory();
|
|
TQDomDocument &dom = *projectDom();
|
|
DomUtil::writeEntry( dom, "/kdevcustomproject/general/activedir", m_contextDirName );
|
|
emit activeDirectoryChanged( olddir, activeDirectory() );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::openProject( const TQString &dirName, const TQString &projectName )
|
|
{
|
|
m_projectDirectory = dirName;
|
|
m_projectName = projectName;
|
|
|
|
TQDomDocument &dom = *projectDom();
|
|
// Set the default directory radio to "executable"
|
|
if ( DomUtil::readEntry( dom, "/kdevcustomproject/run/directoryradio" ) == "" )
|
|
{
|
|
DomUtil::writeEntry( dom, "/kdevcustomproject/run/directoryradio", "executable" );
|
|
}
|
|
|
|
if ( filetypes().isEmpty() )
|
|
{
|
|
TQStringList types;
|
|
types << "*.java" << "*.h" << "*.H" << "*.hh" << "*.hxx" << "*.hpp" << "*.c" << "*.C"
|
|
<< "*.cc" << "*.cpp" << "*.c++" << "*.cxx" << "Makefile" << "CMakeLists.txt";
|
|
DomUtil::writeListEntry( dom, "/kdevcustomproject/filetypes", "filetype", types );
|
|
}
|
|
|
|
/*this entry is currently only created by the cmake tdevelop3 project generator
|
|
in order to support completely-out-of-source builds, where nothing, not
|
|
even the tdevelop project files are created in the source directory, Alex <neundorf@kde.org>
|
|
*/
|
|
m_filelistDir = DomUtil::readEntry( dom, "/kdevcustomproject/filelistdirectory" );
|
|
if ( m_filelistDir.isEmpty() )
|
|
m_filelistDir = dirName;
|
|
|
|
if ( TQFileInfo( m_filelistDir + "/" + projectName.lower() +
|
|
".tdevelop.filelist" ).exists() )
|
|
{
|
|
TQDir( m_filelistDir ).rename(
|
|
projectName.lower() + ".tdevelop.filelist",
|
|
projectName + ".tdevelop.filelist" );
|
|
}
|
|
|
|
TQFile f( m_filelistDir + "/" + projectName + ".tdevelop.filelist" );
|
|
if ( f.open( IO_ReadOnly ) )
|
|
{
|
|
TQTextStream stream( &f );
|
|
while ( !stream.atEnd() )
|
|
{
|
|
TQString s = stream.readLine();
|
|
// Skip comments.
|
|
if ( s.isEmpty() || s.startsWith( "#" ) )
|
|
continue;
|
|
// Skip non-existent files.
|
|
if ( ! TQFileInfo( projectDirectory() + "/" + s ).exists() )
|
|
continue;
|
|
// Do not bother with files already in project or on blacklist.
|
|
if ( isInProject( s ) || isInBlacklist( s ) )
|
|
continue;
|
|
addToProject( s );
|
|
}
|
|
TQStringList newfiles;
|
|
findNewFiles( dirName, newfiles );
|
|
|
|
if ( newfiles.count() > 0 )
|
|
{
|
|
addNewFilesToProject( newfiles );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
int r = KMessageBox::questionYesNo( mainWindow()->main(),
|
|
i18n( "This project does not contain any files yet.\n"
|
|
"Populate it with all C/C++/Java files below "
|
|
"the project directory?" ), TQString(), i18n( "Populate" ), i18n( "Do Not Populate" ) );
|
|
if ( r == KMessageBox::Yes )
|
|
populateProject();
|
|
}
|
|
|
|
|
|
// check if there is an old envvars entry (from old project file with single make environment)
|
|
TQString buildtool = DomUtil::readEntry( dom , "/kdevcustomproject/build/buildtool" );
|
|
TQDomElement el =
|
|
DomUtil::elementByPath( dom , "/kdevcustomproject/" + buildtool + "/envvars" );
|
|
if ( !el.isNull() )
|
|
{
|
|
TQDomElement envs = DomUtil::createElementByPath( dom , "/kdevcustomproject/" + buildtool + "/environments" );
|
|
DomUtil::makeEmpty( envs );
|
|
el.setTagName( "default" );
|
|
envs.appendChild( el );
|
|
}
|
|
KDevProject::openProject( dirName, projectName );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Recursively search given directory searching for files which may be part of this project.
|
|
*
|
|
* The files found not in a black list,
|
|
* and not being already part of our project are gathered.
|
|
*
|
|
* @param dir directory to scan (and recurse) for potential project files.
|
|
* @param[out] fileList the list of files found.
|
|
*/
|
|
void CustomProjectPart::findNewFiles( const TQString& dir, TQStringList& filelist ) const
|
|
{
|
|
if ( dir.isEmpty() )
|
|
return;
|
|
TQStringList fileentries = TQDir( dir ).entryList( filetypes().join( ";" ) );
|
|
TQStringList dirs = TQDir( dir ).entryList( TQDir::Dirs );
|
|
TQStringList entries = fileentries + dirs;
|
|
TQString relpath = relativeToProject( dir );
|
|
if ( !relpath.isEmpty() )
|
|
relpath += "/";
|
|
for ( TQStringList::const_iterator it = entries.begin(); it != entries.end(); ++it )
|
|
{
|
|
// Only process genuine entries - files and directories.
|
|
if (( *it == "." ) || ( *it == ".." ) )
|
|
continue;
|
|
// If the entry (be it a file or a directory) is already part of this project, proceed to next one.
|
|
const TQString relativeEntry( relpath + *it );
|
|
if ( isInProject( relativeEntry ) )
|
|
continue;
|
|
// If the entry is blacklisted, proceed to next one.
|
|
// Note that by using generic isInBlacklist(),
|
|
// we are actually wasting resources,
|
|
// because it also tests whether any parent directory of relativeEntry is blacklisted.
|
|
// But by the order we are traversing and processing the directories,
|
|
// we know it is not the case, ever.
|
|
if ( isInBlacklist( relativeEntry ) )
|
|
continue;
|
|
// We have a new, non-blacklisted entry.
|
|
// Recurse into it (a directory) or add it to the potential list of new project files.
|
|
const TQString absoluteEntry( dir + "/" + *it );
|
|
if ( TQFileInfo( absoluteEntry ).isFile() )
|
|
{
|
|
filelist << relativeEntry;
|
|
}
|
|
else if ( TQFileInfo( absoluteEntry ).isDir() )
|
|
{
|
|
bool searchRecursive = true;
|
|
TQFileInfo fi( absoluteEntry );
|
|
if( fi.isSymLink() )
|
|
{
|
|
TQString realDir = fi.readLink();
|
|
if( TQFileInfo( realDir ).exists() )
|
|
{
|
|
for( TQStringList::const_iterator it = filelist.constBegin(); it != filelist.constEnd(); ++it )
|
|
{
|
|
|
|
if( TQFileInfo(projectDirectory()+"/"+*it).absFilePath().startsWith( realDir ) )
|
|
{
|
|
searchRecursive = false;
|
|
}
|
|
}
|
|
} else {
|
|
searchRecursive = false;
|
|
}
|
|
}
|
|
if( searchRecursive )
|
|
{
|
|
findNewFiles( absoluteEntry, filelist );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CustomProjectPart::populateProject()
|
|
{
|
|
|
|
KDialogBase* dlg = new KDialogBase( mainWindow()->main(), "typeselector", true,
|
|
"Select filetypes of project", KDialogBase::Ok | KDialogBase::Cancel );
|
|
TQVBox* box = dlg->makeVBoxMainWidget();
|
|
KEditListBox* lb = new KEditListBox( "Filetypes in the project", box, "selecttypes",
|
|
false, KEditListBox::Add | KEditListBox::Remove );
|
|
lb->setItems( filetypes() );
|
|
if ( dlg->exec() == TQDialog::Accepted )
|
|
{
|
|
setFiletypes( lb->items() );
|
|
}
|
|
|
|
TQApplication::setOverrideCursor( TQt::waitCursor );
|
|
removeFiles( allFiles() );
|
|
updateBlacklist( TQStringList() );
|
|
|
|
TQStringList newlist;
|
|
|
|
findNewFiles( projectDirectory(), newlist );
|
|
|
|
TQApplication::restoreOverrideCursor();
|
|
addNewFilesToProject( newlist );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::closeProject()
|
|
{
|
|
saveProject();
|
|
}
|
|
|
|
void CustomProjectPart::saveProject()
|
|
{
|
|
TQFile f( m_filelistDir + "/" + m_projectName + ".tdevelop.filelist" );
|
|
if ( !f.open( IO_WriteOnly ) )
|
|
return;
|
|
|
|
TQTextStream stream( &f );
|
|
stream << "# KDevelop Custom Project File List" << endl;
|
|
|
|
ProjectFilesSet::ConstIterator it;
|
|
for ( it = m_sourceFilesSet.constBegin(); it != m_sourceFilesSet.constEnd(); ++it )
|
|
stream << it.key() << endl;
|
|
f.close();
|
|
}
|
|
|
|
|
|
TQString CustomProjectPart::projectDirectory() const
|
|
{
|
|
return m_projectDirectory;
|
|
}
|
|
|
|
|
|
TQString CustomProjectPart::projectName() const
|
|
{
|
|
return m_projectName;
|
|
}
|
|
|
|
|
|
/** Retuns a PairList with the run environment variables */
|
|
DomUtil::PairList CustomProjectPart::runEnvironmentVars() const
|
|
{
|
|
return DomUtil::readPairListEntry( *projectDom(), "/kdevcustomproject/run/envvars", "envvar", "name", "value" );
|
|
}
|
|
|
|
|
|
/** Retuns the currently selected run directory
|
|
* The returned string can be:
|
|
* if run/directoryradio == executable
|
|
* The directory where the executable is
|
|
* if run/directoryradio == build
|
|
* The directory where the executable is relative to build directory
|
|
* if run/directoryradio == custom
|
|
* The custom directory absolute path
|
|
*/
|
|
TQString CustomProjectPart::runDirectory() const
|
|
{
|
|
TQString cwd = defaultRunDirectory( "kdevcustomproject" );
|
|
if ( cwd.isEmpty() )
|
|
cwd = buildDirectory();
|
|
return cwd;
|
|
}
|
|
|
|
|
|
/** Retuns the currently selected main program
|
|
* The returned string can be:
|
|
* if run/directoryradio == executable
|
|
* The executable name
|
|
* if run/directoryradio == build
|
|
* The path to executable relative to build directory
|
|
* if run/directoryradio == custom or relative == false
|
|
* The absolute path to executable
|
|
*/
|
|
TQString CustomProjectPart::mainProgram() const
|
|
{
|
|
TQDomDocument * dom = projectDom();
|
|
|
|
if ( !dom ) return TQString();
|
|
|
|
TQString DomMainProgram = DomUtil::readEntry( *dom, "/kdevcustomproject/run/mainprogram" );
|
|
|
|
if ( DomMainProgram.isEmpty() ) return TQString();
|
|
|
|
if ( DomMainProgram.startsWith( "/" ) ) // assume absolute path
|
|
{
|
|
return DomMainProgram;
|
|
}
|
|
else // assume project relative path
|
|
{
|
|
return projectDirectory() + "/" + DomMainProgram;
|
|
}
|
|
|
|
return TQString();
|
|
}
|
|
|
|
/** Retuns a TQString with the debug command line arguments */
|
|
TQString CustomProjectPart::debugArguments() const
|
|
{
|
|
return DomUtil::readEntry( *projectDom(), "/kdevcustomproject/run/globaldebugarguments" );
|
|
}
|
|
|
|
|
|
/** Retuns a TQString with the run command line arguments */
|
|
TQString CustomProjectPart::runArguments() const
|
|
{
|
|
return DomUtil::readEntry( *projectDom(), "/kdevcustomproject/run/programargs" );
|
|
}
|
|
|
|
TQString CustomProjectPart::activeDirectory() const
|
|
{
|
|
TQDomDocument &dom = *projectDom();
|
|
return DomUtil::readEntry( dom, "/kdevcustomproject/general/activedir", "." );
|
|
}
|
|
|
|
|
|
TQStringList CustomProjectPart::allFiles() const
|
|
{
|
|
return m_sourceFilesSet.keys();
|
|
}
|
|
|
|
|
|
void CustomProjectPart::addFile( const TQString &fileName )
|
|
{
|
|
TQStringList fileList;
|
|
fileList.append( fileName );
|
|
|
|
this->addFiles( fileList );
|
|
}
|
|
|
|
void CustomProjectPart::addFiles( const TQStringList& fileList )
|
|
{
|
|
TQStringList::ConstIterator it;
|
|
TQStringList addedFiles;
|
|
TQStringList myfileList = fileList;
|
|
kdDebug( 9025 ) << "Adding files: " << myfileList << endl;
|
|
myfileList.remove( "." );
|
|
myfileList.remove( "" );
|
|
myfileList.remove( ".." );
|
|
for ( it = myfileList.begin(); it != myfileList.end(); ++it )
|
|
{
|
|
if ( isInBlacklist( *it ) )
|
|
continue;
|
|
TQString relpath;
|
|
kdDebug( 9025 ) << "Checking path: " << *it << endl;
|
|
if ( TQDir::isRelativePath( *it ) )
|
|
{
|
|
kdDebug( 9025 ) << *it << " is relative" << endl;
|
|
relpath = *it;
|
|
}
|
|
else
|
|
{
|
|
kdDebug( 9025 ) << *it << " is not relative" << endl;
|
|
relpath = relativeToProject( *it );
|
|
}
|
|
|
|
if ( !TQFileInfo( projectDirectory() + "/" + relpath ).exists() )
|
|
continue;
|
|
|
|
if ( TQFileInfo( projectDirectory() + "/" + relpath ).isDir() && ( m_recursive || m_first_recursive ) )
|
|
{
|
|
kdDebug( 9025 ) << "is a dir and " << m_recursive << "|" << m_first_recursive << endl;
|
|
m_first_recursive = false;
|
|
TQStringList fileentries = TQDir( projectDirectory() + "/" + relpath ).entryList( filetypes().join( ";" ) );
|
|
TQStringList dirs = TQDir( projectDirectory() + "/" + relpath ).entryList( TQDir::Dirs );
|
|
TQStringList subentries = fileentries + dirs;
|
|
for ( TQStringList::iterator subit = subentries.begin(); subit != subentries.end(); ++subit )
|
|
{
|
|
if ( *subit != "." && *subit != ".." )
|
|
*subit = relpath + "/" + ( *subit );
|
|
if (( *subit ).startsWith( "/" ) )
|
|
*subit = ( *subit ).mid( 1, ( *subit ).length() );
|
|
}
|
|
addFiles( subentries );
|
|
addedFiles << relpath;
|
|
addToProject( relpath );
|
|
m_first_recursive = true;
|
|
}
|
|
else if ( isProjectFileType( TQFileInfo( relpath ).fileName() ) && ( ! isInProject( relpath ) ) )
|
|
{
|
|
TQStringList paths = TQStringList::split( "/", relpath );
|
|
paths.pop_back();
|
|
TQString path;
|
|
for ( TQStringList::const_iterator it = paths.begin(); it != paths.end(); ++it )
|
|
{
|
|
path += *it;
|
|
if ( ! isInProject( path ) )
|
|
{
|
|
addedFiles << path;
|
|
addToProject( path );
|
|
}
|
|
path += "/";
|
|
}
|
|
addedFiles << relpath;
|
|
addToProject( relpath );
|
|
}
|
|
else
|
|
{
|
|
kdDebug( 9025 ) << "not adding " << relpath << endl;
|
|
}
|
|
}
|
|
m_first_recursive = false;
|
|
saveProject();
|
|
|
|
kdDebug( 9025 ) << "Emitting addedFilesToProject" << addedFiles << endl;
|
|
emit addedFilesToProject( addedFiles );
|
|
}
|
|
|
|
void CustomProjectPart::removeFile( const TQString &fileName )
|
|
{
|
|
TQStringList fileList;
|
|
fileList.append( fileName );
|
|
|
|
this->removeFiles( fileList );
|
|
}
|
|
|
|
void CustomProjectPart::removeFiles( const TQStringList& fileList )
|
|
{
|
|
kdDebug( 9025 ) << "Emitting removedFilesFromProject" << fileList << endl;
|
|
TQStringList removedFiles;
|
|
TQStringList myfileList = fileList;
|
|
TQStringList::ConstIterator it;
|
|
myfileList.remove( "." );
|
|
myfileList.remove( ".." );
|
|
myfileList.remove( "" );
|
|
|
|
for ( it = myfileList.begin(); it != myfileList.end(); ++it )
|
|
{
|
|
TQString relpath;
|
|
if ( TQDir::isRelativePath( *it ) )
|
|
relpath = *it;
|
|
else
|
|
relpath = relativeToProject( *it );
|
|
|
|
if ( TQFileInfo( projectDirectory() + "/" + relpath ).isDir() && ( m_recursive || m_first_recursive ) )
|
|
{
|
|
m_first_recursive = false;
|
|
TQStringList fileentries = TQDir( projectDirectory() + "/" + relpath ).entryList( filetypes().join( ";" ) );
|
|
TQStringList dirs = TQDir( projectDirectory() + "/" + relpath ).entryList( TQDir::Dirs );
|
|
TQStringList subentries = fileentries + dirs;
|
|
for ( TQStringList::iterator subit = subentries.begin(); subit != subentries.end(); ++subit )
|
|
if ( *subit != "." && *subit != ".." )
|
|
*subit = relpath + "/" + ( *subit );
|
|
removeFiles( subentries );
|
|
if ( !containsProjectFiles( relpath ) )
|
|
{
|
|
removedFiles << relpath;
|
|
removeFromProject( relpath );
|
|
}
|
|
m_first_recursive = true;
|
|
}
|
|
else if ( isInProject( relpath ) )
|
|
{
|
|
removedFiles << relpath;
|
|
removeFromProject( relpath );
|
|
TQStringList paths = TQStringList::split( "/", relpath );
|
|
TQString lastsubentry = paths[paths.size()-1];
|
|
paths.pop_back();
|
|
while ( paths.size() > 0 )
|
|
{
|
|
TQString dir = paths.join( "/" );
|
|
TQStringList projectentries = projectFilesInDir( dir );
|
|
if ( projectentries.size() == 0 )
|
|
{
|
|
removedFiles << dir;
|
|
removeFromProject( dir );
|
|
}
|
|
else
|
|
break;
|
|
lastsubentry = paths[paths.size()-1];
|
|
paths.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
saveProject();
|
|
emit removedFilesFromProject( removedFiles );
|
|
}
|
|
|
|
TQString CustomProjectPart::buildDirectory() const
|
|
{
|
|
TQString dir = DomUtil::readEntry( *projectDom(), "/kdevcustomproject/build/builddir" );
|
|
if ( dir.isEmpty() )
|
|
return projectDirectory();
|
|
if ( TQFileInfo( dir ).isRelative() )
|
|
return TQDir::cleanDirPath( projectDirectory() + "/" + dir );
|
|
return dir;
|
|
}
|
|
|
|
|
|
TQString CustomProjectPart::makeEnvironment() const
|
|
{
|
|
// Get the make environment variables pairs into the environstr string
|
|
// in the form of: "ENV_VARIABLE=ENV_VALUE"
|
|
// Note that we quote the variable value due to the possibility of
|
|
// embedded spaces
|
|
TQString buildtool = DomUtil::readEntry( *projectDom(), "/kdevcustomproject/build/buildtool" );
|
|
DomUtil::PairList envvars =
|
|
DomUtil::readPairListEntry( *projectDom(), "/kdevcustomproject/" + buildtool + "/environments/" + currentMakeEnvironment(), "envvar", "name", "value" );
|
|
|
|
TQString environstr;
|
|
DomUtil::PairList::ConstIterator it;
|
|
for ( it = envvars.begin(); it != envvars.end(); ++it )
|
|
{
|
|
environstr += ( *it ).first;
|
|
environstr += "=";
|
|
environstr += EnvVarTools::quote(( *it ).second );
|
|
environstr += " ";
|
|
}
|
|
|
|
TDEConfigGroup grp( kapp->config(), "MakeOutputView" );
|
|
if( grp.readBoolEntry( "ForceCLocale", true ) )
|
|
environstr += "LC_MESSAGES=" + EnvVarTools::quote( "C" )+" "+" "+"LC_CTYPE="+EnvVarTools::quote("C")+" ";
|
|
|
|
return environstr;
|
|
}
|
|
|
|
|
|
void CustomProjectPart::startMakeCommand( const TQString &dir, const TQString &target, bool withKdesu )
|
|
{
|
|
if ( partController()->saveAllFiles() == false )
|
|
return; //user cancelled
|
|
|
|
TQDomDocument &dom = *projectDom();
|
|
TQString buildtool = DomUtil::readEntry( dom, "/kdevcustomproject/build/buildtool" );
|
|
|
|
TQString cmdline;
|
|
if ( buildtool == "ant" )
|
|
{
|
|
cmdline = "ant";
|
|
}
|
|
else if ( buildtool == "other" )
|
|
{
|
|
cmdline = DomUtil::readEntry( dom, "/kdevcustomproject/other/otherbin" );
|
|
if ( cmdline.isEmpty() )
|
|
cmdline = "echo";
|
|
else if ( cmdline.find( "/" ) == -1 )
|
|
cmdline = "./" + cmdline;
|
|
cmdline += " " + DomUtil::readEntry( dom, "/kdevcustomproject/other/otheroptions" );
|
|
}
|
|
else
|
|
{
|
|
cmdline = DomUtil::readEntry( dom, "/kdevcustomproject/make/makebin" );
|
|
if ( cmdline.isEmpty() )
|
|
cmdline = MAKE_COMMAND;
|
|
if ( !DomUtil::readBoolEntry( dom, "/kdevcustomproject/make/abortonerror" ) )
|
|
cmdline += " -k";
|
|
int jobs = DomUtil::readIntEntry( dom, "/kdevcustomproject/make/numberofjobs" );
|
|
if ( jobs != 0 )
|
|
{
|
|
cmdline += " -j";
|
|
cmdline += TQString::number( jobs );
|
|
}
|
|
if ( DomUtil::readBoolEntry( dom, "/kdevcustomproject/make/dontact" ) )
|
|
cmdline += " -n";
|
|
cmdline += " " + DomUtil::readEntry( dom, "/kdevcustomproject/make/makeoptions" );
|
|
}
|
|
|
|
cmdline += " ";
|
|
if ( !target.isEmpty() )
|
|
cmdline += TDEProcess::quote( target );
|
|
|
|
TQString dircmd = "cd ";
|
|
dircmd += TDEProcess::quote( dir );
|
|
dircmd += " && ";
|
|
|
|
int prio = DomUtil::readIntEntry( dom, "/kdevcustomproject/" + buildtool + "/prio" );
|
|
TQString nice;
|
|
if ( prio != 0 )
|
|
{
|
|
nice = TQString( "nice -n%1 " ).arg( prio );
|
|
}
|
|
|
|
cmdline.prepend( nice );
|
|
cmdline.prepend( makeEnvironment() );
|
|
|
|
if ( withKdesu )
|
|
cmdline = "tdesu -t -c '" + cmdline + "'";
|
|
|
|
m_buildCommand = dircmd + cmdline;
|
|
|
|
|
|
makeFrontend()->queueCommand( dir, dircmd + cmdline );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotBuild()
|
|
{
|
|
m_lastCompilationFailed = false;
|
|
TQString buildtool = DomUtil::readEntry( *projectDom(), "/kdevcustomproject/build/buildtool" );
|
|
startMakeCommand( buildDirectory(), DomUtil::readEntry( *projectDom(),
|
|
"/kdevcustomproject/" + buildtool + "/defaulttarget" ) );
|
|
}
|
|
|
|
void CustomProjectPart::slotBuildActiveDir()
|
|
{
|
|
m_lastCompilationFailed = false;
|
|
TQString buildtool = DomUtil::readEntry( *projectDom(), "/kdevcustomproject/build/buildtool" );
|
|
startMakeCommand( buildDirectory() + "/" + activeDirectory(), DomUtil::readEntry( *projectDom(),
|
|
"/kdevcustomproject/" + buildtool + "/defaulttarget" ) );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotCompileFile()
|
|
{
|
|
KParts::ReadWritePart *part = dynamic_cast<KParts::ReadWritePart*>( partController()->activePart() );
|
|
if ( !part || !part->url().isLocalFile() )
|
|
return;
|
|
|
|
TQString fileName = part->url().path();
|
|
TQFileInfo fi( fileName );
|
|
TQString sourceDir = fi.dirPath();
|
|
TQString baseName = fi.baseName( true );
|
|
kdDebug( 9025 ) << "Compiling " << fileName
|
|
<< "in dir " << sourceDir
|
|
<< " with baseName " << baseName << endl;
|
|
|
|
// What would be nice: In case of non-recursive build system, climb up from
|
|
// the source dir until a Makefile is found
|
|
|
|
TQString buildDir = sourceDir;
|
|
TQString target = baseName + ".o";
|
|
|
|
TQString buildtool = DomUtil::readEntry( *projectDom(), "/kdevcustomproject/build/buildtool" );
|
|
|
|
//if there is no Makefile in the directory of the source file
|
|
//try to build it from the main build dir
|
|
//this works e.g. for non-recursive cmake Makefiles, Alex
|
|
if ( buildtool == "make" && ( TQFile::exists( sourceDir + "/Makefile" ) == false )
|
|
&& ( TQFile::exists( sourceDir + "/makefile" ) == false ) )
|
|
{
|
|
buildDir = buildDirectory();
|
|
}
|
|
|
|
startMakeCommand( buildDir, target );
|
|
}
|
|
|
|
void CustomProjectPart::slotInstallActiveDir()
|
|
{
|
|
startMakeCommand( buildDirectory() + "/" + activeDirectory(), TQString::fromLatin1( "install" ) );
|
|
}
|
|
|
|
void CustomProjectPart::slotInstall()
|
|
{
|
|
startMakeCommand( buildDirectory(), TQString::fromLatin1( "install" ) );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotInstallWithKdesu()
|
|
{
|
|
// First issue "make" to build the entire project with the current user
|
|
// This way we make sure all files are up to date before we do the "make install"
|
|
slotBuild();
|
|
|
|
// After that issue "make install" with the root user
|
|
startMakeCommand( buildDirectory(), TQString::fromLatin1( "install" ), true );
|
|
}
|
|
|
|
void CustomProjectPart::slotClean()
|
|
{
|
|
startMakeCommand( buildDirectory(), TQString::fromLatin1( "clean" ) );
|
|
}
|
|
|
|
|
|
void CustomProjectPart::slotExecute()
|
|
{
|
|
partController()->saveAllFiles();
|
|
|
|
bool _auto = false;
|
|
if ( DomUtil::readBoolEntry( *projectDom(), "/kdevcustomproject/run/autocompile", true ) && ( isDirty() || !TQFileInfo( mainProgram() ).exists() ) )
|
|
{
|
|
m_executeAfterBuild = true;
|
|
slotBuild();
|
|
_auto = true;
|
|
}
|
|
|
|
if ( DomUtil::readBoolEntry( *projectDom(), "/kdevcustomproject/run/autoinstall", false ) && ( isDirty() || !TQFileInfo( mainProgram() ).exists() ) )
|
|
{
|
|
m_executeAfterBuild = true;
|
|
// Use tdesu??
|
|
if ( DomUtil::readBoolEntry( *projectDom(), "/kdevcustomproject/run/autotdesu", false ) )
|
|
//slotInstallWithKdesu assumes that it hasn't just been build...
|
|
_auto ? slotInstallWithKdesu() : startMakeCommand( buildDirectory(), TQString::fromLatin1( "install" ), true );
|
|
else
|
|
slotInstall();
|
|
_auto = true;
|
|
}
|
|
|
|
if ( _auto )
|
|
return;
|
|
|
|
// Get the run environment variables pairs into the environstr string
|
|
// in the form of: "ENV_VARIABLE=ENV_VALUE"
|
|
// Note that we quote the variable value due to the possibility of
|
|
// embedded spaces
|
|
DomUtil::PairList envvars = runEnvironmentVars();
|
|
TQString environstr;
|
|
DomUtil::PairList::ConstIterator it;
|
|
for ( it = envvars.begin(); it != envvars.end(); ++it )
|
|
{
|
|
environstr += ( *it ).first;
|
|
environstr += "=";
|
|
environstr += EnvVarTools::quote(( *it ).second );
|
|
environstr += " ";
|
|
}
|
|
|
|
if ( mainProgram().isEmpty() )
|
|
// Do not execute non executable targets
|
|
return;
|
|
|
|
TQString program = environstr;
|
|
program += mainProgram();
|
|
program += " " + runArguments();
|
|
|
|
bool inTerminal = DomUtil::readBoolEntry( *projectDom(), "/kdevcustomproject/run/terminal" );
|
|
|
|
kdDebug( 9025 ) << "runDirectory: <" << runDirectory() << ">" << endl;
|
|
kdDebug( 9025 ) << "environstr : <" << environstr << ">" << endl;
|
|
kdDebug( 9025 ) << "mainProgram : <" << mainProgram() << ">" << endl;
|
|
kdDebug( 9025 ) << "runArguments: <" << runArguments() << ">" << endl;
|
|
|
|
appFrontend()->startAppCommand( runDirectory(), program, inTerminal );
|
|
}
|
|
|
|
void CustomProjectPart::updateTargetMenu()
|
|
{
|
|
m_targets.clear();
|
|
m_targetsObjectFiles.clear();
|
|
m_targetsOtherFiles.clear();
|
|
m_targetMenu->clear();
|
|
m_targetObjectFilesMenu->clear();
|
|
m_targetOtherFilesMenu->clear();
|
|
|
|
TQDomDocument &dom = *projectDom();
|
|
bool ant = DomUtil::readEntry( dom, "/kdevcustomproject/build/buildtool" ) == "ant";
|
|
|
|
if ( ant )
|
|
{
|
|
TQFile f( buildDirectory() + "/build.xml" );
|
|
if ( !f.open( IO_ReadOnly ) )
|
|
{
|
|
kdDebug( 9025 ) << "No build file" << endl;
|
|
return;
|
|
}
|
|
TQDomDocument dom;
|
|
if ( !dom.setContent( &f ) )
|
|
{
|
|
kdDebug( 9025 ) << "Build script not valid xml" << endl;
|
|
f.close();
|
|
return;
|
|
}
|
|
f.close();
|
|
|
|
TQDomNode node = dom.documentElement().firstChild();
|
|
while ( !node.isNull() )
|
|
{
|
|
if ( node.toElement().tagName() == "target" )
|
|
m_targets.append( node.toElement().attribute( "name" ) );
|
|
node = node.nextSibling();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdDebug( 9025 ) << "Trying to load a makefile... " << endl;
|
|
|
|
m_makefileVars.clear();
|
|
m_parsedMakefiles.clear();
|
|
m_makefilesToParse.clear();
|
|
m_makefilesToParse.push( "Makefile" );
|
|
m_makefilesToParse.push( "makefile" );
|
|
putEnvVarsInVarMap();
|
|
while ( !m_makefilesToParse.isEmpty() )
|
|
parseMakefile( m_makefilesToParse.pop() );
|
|
|
|
//free the memory again
|
|
m_makefileVars.clear();
|
|
m_parsedMakefiles.clear();
|
|
|
|
m_targets.sort();
|
|
m_targetsObjectFiles.sort();
|
|
m_targetsOtherFiles.sort();
|
|
|
|
}
|
|
|
|
m_targetMenu->insertItem( i18n( "Object Files" ), m_targetObjectFilesMenu );
|
|
m_targetMenu->insertItem( i18n( "Other Files" ), m_targetOtherFilesMenu );
|
|
|
|
int id = 0;
|
|
TQStringList::ConstIterator it;
|
|
for ( it = m_targets.begin(); it != m_targets.end(); ++it )
|
|
m_targetMenu->insertItem( *it, id++ );
|
|
|
|
id = 0;
|
|
for ( it = m_targetsObjectFiles.begin(); it != m_targetsObjectFiles.end(); ++it )
|
|
m_targetObjectFilesMenu->insertItem( *it, id++ );
|
|
|
|
id = 0;
|
|
for ( it = m_targetsOtherFiles.begin(); it != m_targetsOtherFiles.end(); ++it )
|
|
m_targetOtherFilesMenu->insertItem( *it, id++ );
|
|
}
|
|
|
|
void CustomProjectPart::putEnvVarsInVarMap()
|
|
{
|
|
DomUtil::PairList envvars =
|
|
DomUtil::readPairListEntry( *projectDom(), "/kdevcustomproject/make/environments/" + currentMakeEnvironment(), "envvar", "name", "value" );
|
|
|
|
for ( DomUtil::PairList::ConstIterator it = envvars.begin(); it != envvars.end(); ++it )
|
|
m_makefileVars[( *it ).first] = ( *it ).second; //is qouting here required as in makeEnvironment() ??
|
|
}
|
|
|
|
void CustomProjectPart::parseMakefile( const TQString& filename )
|
|
{
|
|
if ( m_parsedMakefiles.contains( filename ) )
|
|
return;
|
|
|
|
m_parsedMakefiles.insert( filename, 1 );
|
|
|
|
TQString absFilename = filename;
|
|
if ( !filename.startsWith( "/" ) )
|
|
absFilename = buildDirectory() + "/" + filename;
|
|
|
|
TQFile f( absFilename );
|
|
if ( !f.open( IO_ReadOnly ) )
|
|
{
|
|
kdDebug( 9025 ) << "could not open " << absFilename << endl;
|
|
return;
|
|
}
|
|
TQRegExp targetRe( "^ *([^\\t$.#]\\S+) *:.*$" );
|
|
targetRe.setMinimal( true );
|
|
|
|
TQRegExp variablesRe( "\\$\\(\\s*([^\\)\\s]+)\\s*\\)" );
|
|
TQRegExp assignmentRe( "^\\s*(\\S+)\\s*[:\\?]?=\\s*(\\S+)\\s*(#.*)?$" );
|
|
|
|
TQRegExp includedMakefilesRe( "^include\\s+(\\S+)" );
|
|
TQString str = "";
|
|
while ( !f.atEnd() )
|
|
{
|
|
f.readLine( str, 200 );
|
|
|
|
// Replace any variables in the current line
|
|
int offset = -1;
|
|
while (( offset = variablesRe.search( str, offset + 1 ) ) != -1 )
|
|
{
|
|
TQString variableName = variablesRe.cap( 1 ).simplifyWhiteSpace();
|
|
if ( m_makefileVars.contains( variableName ) )
|
|
{
|
|
str.replace( variablesRe.cap( 0 ), m_makefileVars[variableName] );
|
|
}
|
|
}
|
|
|
|
// Read all continuation lines
|
|
// kdDebug(9025) << "Trying: " << str.simplifyWhiteSpace() << endl;
|
|
//while (str.right(1) == "\\" && !stream.atEnd()) {
|
|
// str.remove(str.length()-1, 1);
|
|
// str += stream.readLine();
|
|
//}
|
|
// Find any variables
|
|
if ( assignmentRe.search( str ) != -1 )
|
|
{
|
|
m_makefileVars[assignmentRe.cap( 1 ).simplifyWhiteSpace()] = assignmentRe.cap( 2 ).simplifyWhiteSpace();
|
|
}
|
|
else if ( includedMakefilesRe.search( str ) != -1 )
|
|
{
|
|
TQString includedMakefile = includedMakefilesRe.cap( 1 ).simplifyWhiteSpace();
|
|
m_makefilesToParse.push( includedMakefile );
|
|
}
|
|
else if ( targetRe.search( str ) != -1 )
|
|
{
|
|
TQString tmpTarget = targetRe.cap( 1 ).simplifyWhiteSpace();
|
|
if ( tmpTarget.endsWith( ".o" ) )
|
|
{
|
|
if ( m_targetsObjectFiles.find( tmpTarget ) == m_targetsObjectFiles.end() )
|
|
m_targetsObjectFiles += tmpTarget;
|
|
}
|
|
else if ( tmpTarget.contains( '.' ) )
|
|
{
|
|
if ( m_targetsOtherFiles.find( tmpTarget ) == m_targetsOtherFiles.end() )
|
|
m_targetsOtherFiles += tmpTarget;
|
|
}
|
|
else
|
|
{
|
|
if ( m_targets.find( tmpTarget ) == m_targets.end() )
|
|
m_targets += tmpTarget;
|
|
}
|
|
}
|
|
}
|
|
f.close();
|
|
}
|
|
|
|
void CustomProjectPart::targetMenuActivated( int id )
|
|
{
|
|
TQString target = m_targets[id];
|
|
startMakeCommand( buildDirectory(), target );
|
|
}
|
|
|
|
void CustomProjectPart::targetObjectFilesMenuActivated( int id )
|
|
{
|
|
TQString target = m_targetsObjectFiles[id];
|
|
startMakeCommand( buildDirectory(), target );
|
|
}
|
|
|
|
void CustomProjectPart::targetOtherFilesMenuActivated( int id )
|
|
{
|
|
TQString target = m_targetsOtherFiles[id];
|
|
startMakeCommand( buildDirectory(), target );
|
|
}
|
|
|
|
void CustomProjectPart::updateMakeEnvironmentsMenu()
|
|
{
|
|
TQDomDocument &dom = *projectDom();
|
|
bool makeUsed = ( DomUtil::readEntry( dom, "/kdevcustomproject/build/buildtool" ) == "make" );
|
|
if ( makeUsed )
|
|
{
|
|
TQStringList l = allMakeEnvironments();
|
|
m_makeEnvironmentsSelector->setItems( l );
|
|
m_makeEnvironmentsSelector->setCurrentItem( l.findIndex( currentMakeEnvironment() ) );
|
|
}
|
|
else
|
|
{
|
|
m_makeEnvironmentsSelector->clear();
|
|
}
|
|
/*
|
|
m_makeEnvironmentsMenu->clear();
|
|
TQDomDocument &dom = *projectDom();
|
|
|
|
TQStringList environments = allMakeEnvironments();
|
|
TQStringList::ConstIterator it;
|
|
int id = 0;
|
|
for (it = environments.begin(); it != environments.end(); ++it)
|
|
m_makeEnvironmentsMenu->insertItem(*it, id++);
|
|
}
|
|
*/
|
|
}
|
|
|
|
void CustomProjectPart::makeEnvironmentsMenuActivated( int id )
|
|
{
|
|
TQDomDocument &dom = *projectDom();
|
|
TQString environment = allMakeEnvironments()[id];
|
|
DomUtil::writeEntry( dom, "/kdevcustomproject/make/selectedenvironment", environment );
|
|
}
|
|
|
|
void CustomProjectPart::slotCommandFinished( const TQString& command )
|
|
{
|
|
kdDebug( 9025 ) << "CustomProjectPart::slotProcessFinished()" << endl;
|
|
|
|
if ( m_buildCommand != command )
|
|
return;
|
|
|
|
m_buildCommand = TQString();
|
|
|
|
m_timestamp.clear();
|
|
TQStringList fileList = allFiles();
|
|
TQStringList::Iterator it = fileList.begin();
|
|
while ( it != fileList.end() )
|
|
{
|
|
TQString fileName = *it;
|
|
++it;
|
|
|
|
m_timestamp[ fileName ] = TQFileInfo( projectDirectory(), fileName ).lastModified();
|
|
}
|
|
|
|
emit projectCompiled();
|
|
|
|
if ( m_executeAfterBuild )
|
|
{
|
|
slotExecute();
|
|
m_executeAfterBuild = false;
|
|
}
|
|
}
|
|
|
|
void CustomProjectPart::slotCommandFailed( const TQString& /*command*/ )
|
|
{
|
|
m_lastCompilationFailed = true;
|
|
m_executeAfterBuild = false;
|
|
}
|
|
|
|
bool CustomProjectPart::isDirty()
|
|
{
|
|
if ( m_lastCompilationFailed ) return true;
|
|
|
|
TQStringList fileList = allFiles();
|
|
TQStringList::Iterator it = fileList.begin();
|
|
while ( it != fileList.end() )
|
|
{
|
|
TQString fileName = *it;
|
|
++it;
|
|
|
|
TQMap<TQString, TQDateTime>::Iterator it = m_timestamp.find( fileName );
|
|
TQDateTime t = TQFileInfo( projectDirectory(), fileName ).lastModified();
|
|
if ( it == m_timestamp.end() || *it != t )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
TQStringList CustomProjectPart::allMakeEnvironments() const
|
|
{
|
|
TQDomDocument &dom = *projectDom();
|
|
|
|
TQStringList allConfigs;
|
|
|
|
TQDomNode node =
|
|
DomUtil::elementByPath( dom , "/kdevcustomproject/make/environments" );
|
|
// extract the names of the different make environments
|
|
TQDomElement childEl = node.firstChild().toElement();
|
|
while ( !childEl.isNull() )
|
|
{
|
|
TQString config = childEl.tagName();
|
|
allConfigs.append( config );
|
|
childEl = childEl.nextSibling().toElement();
|
|
}
|
|
if ( allConfigs.isEmpty() )
|
|
allConfigs.append( "default" );
|
|
|
|
return allConfigs;
|
|
}
|
|
|
|
|
|
TQString CustomProjectPart::currentMakeEnvironment() const
|
|
{
|
|
TQStringList allEnvs = allMakeEnvironments();
|
|
TQDomDocument &dom = *projectDom();
|
|
TQString environment = DomUtil::readEntry( dom, "/kdevcustomproject/make/selectedenvironment" );
|
|
if ( environment.isEmpty() || !allEnvs.contains( environment ) )
|
|
environment = allEnvs[0];
|
|
return environment;
|
|
}
|
|
|
|
/*!
|
|
\fn CustomProjectPart::distFiles() const
|
|
*/
|
|
TQStringList CustomProjectPart::distFiles() const
|
|
{
|
|
TQStringList sourceList = allFiles();
|
|
// Scan current source directory for any .pro files.
|
|
TQString projectDir = projectDirectory();
|
|
TQDir dir( projectDir );
|
|
TQStringList files = dir.entryList( "*README*" );
|
|
return sourceList + files;
|
|
}
|
|
|
|
bool CustomProjectPart::containsNonProjectFiles( const TQString& dir )
|
|
{
|
|
if ( isInBlacklist( dir ) )
|
|
return false;
|
|
TQStringList fileentries = TQDir( dir ).entryList( filetypes().join( ";" ) );
|
|
TQStringList dirs = TQDir( dir ).entryList( TQDir::Dirs );
|
|
TQStringList subentries = fileentries + dirs;
|
|
subentries.remove( "." );
|
|
subentries.remove( ".." );
|
|
for ( TQStringList::const_iterator it = subentries.begin(); it != subentries.end(); ++it )
|
|
{
|
|
if ( isInBlacklist( *it ) )
|
|
continue;
|
|
if ( TQFileInfo( dir + "/" + *it ).isDir() && !isInBlacklist( *it ) )
|
|
{
|
|
if ( containsNonProjectFiles( dir + "/" + *it ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if ( !project()->isProjectFile( URLUtil::canonicalPath( dir + "/" + *it ) )
|
|
&& !isInBlacklist( *it ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CustomProjectPart::containsProjectFiles( const TQString& dir )
|
|
{
|
|
if ( isInBlacklist( dir ) )
|
|
return false;
|
|
|
|
TQStringList fileentries = TQDir( dir ).entryList( filetypes().join( ";" ) );
|
|
TQStringList dirs = TQDir( dir ).entryList( TQDir::Dirs );
|
|
TQStringList subentries = fileentries + dirs;
|
|
subentries.remove( "." );
|
|
subentries.remove( ".." );
|
|
for ( TQStringList::const_iterator it = subentries.begin(); it != subentries.end(); ++it )
|
|
{
|
|
if ( isInBlacklist( *it ) )
|
|
continue;
|
|
|
|
if ( TQFileInfo( dir + "/" + *it ).isDir() && !isInBlacklist( *it ) )
|
|
{
|
|
if ( containsProjectFiles( dir + "/" + *it ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if ( project()->isProjectFile( URLUtil::canonicalPath( dir + "/" + *it ) ) && !isInBlacklist( *it ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TQStringList CustomProjectPart::projectFilesInDir( const TQString& dir )
|
|
{
|
|
TQStringList result;
|
|
TQStringList fileentries = TQDir( projectDirectory() + "/" + dir ).entryList( filetypes().join( ";" ) );
|
|
TQStringList dirs = TQDir( projectDirectory() + "/" + dir ).entryList( TQDir::Dirs );
|
|
TQStringList subentries = fileentries + dirs;
|
|
subentries.remove( "." );
|
|
subentries.remove( ".." );
|
|
for ( TQStringList::const_iterator it = subentries.begin(); it != subentries.end(); ++it )
|
|
{
|
|
if ( isInProject( dir + "/" + *it ) )
|
|
{
|
|
result << ( *it );
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TQStringList CustomProjectPart::filetypes( ) const
|
|
{
|
|
return DomUtil::readListEntry( *projectDom(), "/kdevcustomproject/filetypes", "filetype" );
|
|
}
|
|
|
|
bool CustomProjectPart::isProjectFileType( const TQString& filename ) const
|
|
{
|
|
TQStringList types = filetypes();
|
|
TQRegExp re( "", true, true );
|
|
for ( TQStringList::const_iterator it = types.begin(); it != types.end(); ++it )
|
|
{
|
|
re.setPattern( *it );
|
|
int pos = re.search( filename );
|
|
uint len = re.matchedLength();
|
|
if ((( *it ).find( "*" ) != -1 || ( *it ).find( "?" ) != -1 ) && pos + len == filename.length() )
|
|
return true;
|
|
else if ( filename.find( "/" ) != -1 && filename.find( *it ) != -1 )
|
|
return true;
|
|
else if ( filename.find( "/" ) == -1 && filename == *it )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CustomProjectPart::switchBlacklistEntry( const TQString& path )
|
|
{
|
|
TQStringList blacklist = this->blacklist();
|
|
kdDebug( 9025 ) << "Switching path " << path << endl;
|
|
if ( !isInBlacklist( path ) )
|
|
{
|
|
blacklist << path;
|
|
m_recursive = true;
|
|
removeFile( path );
|
|
m_recursive = false;
|
|
}
|
|
else
|
|
{
|
|
blacklist.remove( path );
|
|
}
|
|
updateBlacklist( blacklist );
|
|
}
|
|
|
|
TQString CustomProjectPart::relativeToProject( const TQString& abspath ) const
|
|
{
|
|
TQString path = abspath.mid( projectDirectory().length() + 1 );
|
|
kdDebug( 9025 ) << "abspath: " << "|project dir: " << projectDirectory() << "|path: " << path << endl;
|
|
if ( path.endsWith( "/" ) )
|
|
path = path.mid( 0, path.length() - 1 );
|
|
if ( path.startsWith( "/" ) )
|
|
path = path.mid( 1, path.length() );
|
|
return path;
|
|
}
|
|
|
|
bool CustomProjectPart::isInBlacklist( const TQString& path ) const
|
|
{
|
|
TQString relpath = path;
|
|
TQStringList blacklist = this->blacklist();
|
|
if ( !TQFileInfo( relpath ).isRelative() )
|
|
relpath = relativeToProject( path );
|
|
if ( blacklist.find( relpath ) != blacklist.end() )
|
|
return true;
|
|
TQStringList paths = TQStringList::split( "/", relpath );
|
|
TQString parentpath;
|
|
for ( TQStringList::const_iterator it = paths.begin(); it != paths.end(); ++it )
|
|
{
|
|
parentpath += *it;
|
|
if ( blacklist.find( parentpath ) != blacklist.end() )
|
|
return true;
|
|
parentpath = parentpath + "/";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CustomProjectPart::updateBlacklist( const TQStringList& l )
|
|
{
|
|
DomUtil::writeListEntry( *projectDom(), "kdevcustomproject/blacklist", "path", l );
|
|
}
|
|
|
|
TQStringList CustomProjectPart::blacklist() const
|
|
{
|
|
return DomUtil::readListEntry( *projectDom(), "kdevcustomproject/blacklist", "path" );
|
|
}
|
|
|
|
void CustomProjectPart::addNewFilesToProject( const TQStringList& filelist )
|
|
{
|
|
TQStringList addfiles;
|
|
for ( TQStringList::const_iterator it = filelist.begin(); it != filelist.end(); ++it )
|
|
{
|
|
if (( ! isInProject( *it ) ) && ( isProjectFileType( *it ) || TQFileInfo( projectDirectory() + "/" + *it ).isDir() ) && !isInBlacklist( *it ) )
|
|
{
|
|
addfiles << *it;
|
|
}
|
|
}
|
|
|
|
if ( addfiles.isEmpty() )
|
|
return;
|
|
|
|
SelectNewFilesDialog *dlg = new SelectNewFilesDialog( addfiles, mainWindow()->main() );
|
|
if ( dlg->exec() == KDialog::Accepted )
|
|
{
|
|
m_first_recursive = false;
|
|
m_recursive = false;
|
|
TQStringList blacklist = this->blacklist();
|
|
TQStringList excludelist = dlg->excludedPaths();
|
|
TQStringList removeFromExcludes;
|
|
for ( TQStringList::const_iterator it = excludelist.begin(); it != excludelist.end(); ++it )
|
|
{
|
|
if ( TQFileInfo( projectDirectory() + "/" + *it ).isDir() )
|
|
{
|
|
for ( ProjectFilesSet::ConstIterator it2 = m_sourceFilesSet.constBegin(); it2 != m_sourceFilesSet.constEnd(); ++it2 )
|
|
{
|
|
if ( it2.key().find( *it ) != -1 )
|
|
{
|
|
removeFromExcludes << *it;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for ( TQStringList::const_iterator it = removeFromExcludes.begin(); it != removeFromExcludes.end(); ++it )
|
|
{
|
|
excludelist.remove( *it );
|
|
}
|
|
blacklist += excludelist;
|
|
updateBlacklist( blacklist );
|
|
addFiles( dlg->includedPaths() );
|
|
}
|
|
}
|
|
|
|
void CustomProjectPart::setFiletypes( const TQStringList& l )
|
|
{
|
|
DomUtil::writeListEntry( *projectDom(), "kdevcustomproject/filetypes", "filetype", l );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Is a given file (or a directory) part of this project?
|
|
*
|
|
* @param fileName
|
|
*/
|
|
bool CustomProjectPart::isInProject( const TQString& fileName ) const
|
|
{
|
|
return m_sourceFilesSet.contains( fileName );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Add a file (or a directory) to this project.
|
|
*
|
|
* @param fileName
|
|
*
|
|
* @see removeFromProject()
|
|
*/
|
|
void CustomProjectPart::addToProject( const TQString& fileName )
|
|
{
|
|
m_sourceFilesSet.insert( fileName, false );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Remove a file (or a directory) from this project.
|
|
*
|
|
* @param fileName
|
|
*/
|
|
void CustomProjectPart::removeFromProject( const TQString& fileName )
|
|
{
|
|
m_sourceFilesSet.remove( fileName );
|
|
}
|
|
|
|
|
|
#include "customprojectpart.moc"
|
|
|