KProcess bug or something wrong in kdevelop?

Andreas Pakulat apaku at gmx.de
Wed Nov 8 23:32:33 GMT 2006


Hi,

today I discovered a problem with KProcess. I need to use
KProcess::Block to execute a qmake command and read the output on
stderr. Figuring out how it should work was easy and actually it works
99%. The problem is that the remaining 1% creates a zombie process of
whatever I start via KProcess, be it a shell command starting qmake,
simply kcalc without shell or kate.

adymo and teatime tried to help me find out whats going on, but both
gave up - they could reproduce the problem, though.

Another thing is that this only happens inside kdevelop (where I need
the code), putting the same code into a simple kde app works perfectly.
The same for a kpart-app according to teatime.

Of course this makes debugging a lot harder, because anybody who wants
to reproduce needs to checkout kdevelop. I'm attaching the patch that
adds the class with KProcess and the call to it inside the scope.cpp in
buildtools/qmake.

I'd really appreciate it if one of the core-devs with more knowledge
about KProcess could take a look at it. Currently I'm stuck with using
system() and sending the stderr-output to a temp-file, which is really
ugly.

Andreas, who's totally confused about this

-- 
A few hours grace before the madness begins again.
-------------- next part --------------
diff -u -x.svn -xMakefile.in -Nur kdevelop_org/buildtools/qmake/Makefile.am kdevelop/buildtools/qmake/Makefile.am
--- kdevelop_org/buildtools/qmake/Makefile.am	2006-11-07 22:17:27.000000000 +0100
+++ kdevelop/buildtools/qmake/Makefile.am	2006-11-08 15:41:17.000000000 +0100
@@ -15,12 +15,12 @@
 	$(top_builddir)/buildtools/lib/base/libkdevbuildbase.la \
 	$(top_builddir)/buildtools/lib/parsers/qmake/libkdevqmakeparser.la
 
-libkdevtrollproject_la_SOURCES = projectconfigurationdlgbase.ui \
-	newwidgetdlgbase.ui newwidgetdlg.cpp projectconfigurationdlg.cpp trollprojectpart.cpp \
-	trollprojectwidget.cpp choosesubprojectdlgbase.ui choosesubprojectdlg.cpp trolllistview.cpp \
-	pathutil.cpp scope.cpp qmakescopeitem.cpp qmakeoptionswidgetbase.ui \
-	qmakeoptionswidget.cpp createscopedlgbase.ui createscopedlg.cpp disablesubprojectdlgbase.ui \
-	disablesubprojectdlg.cpp
+libkdevtrollproject_la_SOURCES = choosesubprojectdlg.cpp \
+	choosesubprojectdlgbase.ui createscopedlg.cpp createscopedlgbase.ui disablesubprojectdlg.cpp \
+	disablesubprojectdlgbase.ui newwidgetdlg.cpp newwidgetdlgbase.ui pathutil.cpp \
+	projectconfigurationdlg.cpp projectconfigurationdlgbase.ui qmakedefaultopts.cpp \
+	qmakeoptionswidget.cpp qmakeoptionswidgetbase.ui qmakescopeitem.cpp scope.cpp \
+	trolllistview.cpp trollprojectpart.cpp trollprojectwidget.cpp
 
 
 METASOURCES = AUTO
@@ -30,5 +30,5 @@
 
 rcdir = $(kde_datadir)/kdevtrollproject
 rc_DATA = kdevtrollproject.rc
-noinst_HEADERS = trolllistview.h qmakescopeitem.h qmakeoptionswidget.h \
-	createscopedlg.h disablesubprojectdlg.h
+noinst_HEADERS = createscopedlg.h disablesubprojectdlg.h qmakedefaultopts.h \
+	qmakeoptionswidget.h qmakescopeitem.h trolllistview.h
diff -u -x.svn -xMakefile.in -Nur kdevelop_org/buildtools/qmake/qmakedefaultopts.cpp kdevelop/buildtools/qmake/qmakedefaultopts.cpp
--- kdevelop_org/buildtools/qmake/qmakedefaultopts.cpp	1970-01-01 01:00:00.000000000 +0100
+++ kdevelop/buildtools/qmake/qmakedefaultopts.cpp	2006-11-08 22:21:06.000000000 +0100
@@ -0,0 +1,114 @@
+/***************************************************************************
+*   Copyright (C) 2006 by Andreas Pakulat                                 *
+*   apaku at 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 "qmakedefaultopts.h"
+
+#include <kprocess.h>
+#include <kdebug.h>
+
+#include <qdir.h>
+#include <qregexp.h>
+#include <qprocess.h>
+
+#include "trollprojectpart.h"
+
+QMakeDefaultOpts::QMakeDefaultOpts( QObject *parent, const char *name )
+    : QObject(parent, name), qmakefile(0), makefile(0), proc(0)
+{
+
+}
+
+void QMakeDefaultOpts::readVariables( const QString& qtdir, const QString& projdir )
+{
+    makefile = new KTempFile(projdir+"/", ".mf");
+    qmakefile = new KTempFile("/tmp/", ".pro");
+    if( makefile->status() == 0 && qmakefile->status() == 0  )
+    {
+        if( !makefile->close() )
+            kdDebug(9024) << "Couldn't close makefile" << endl;
+        if( !qmakefile->close() )
+            kdDebug(9024) << "Couldn't close makefile" << endl;
+        QString qmakebin = qtdir + QString( QChar( QDir::separator() ) ) + "bin" + QString( QChar( QDir::separator() ) ) + "qmake";
+
+        if( proc )
+            delete proc;
+
+        proc = new KProcess(this);
+        proc->setUseShell(true);
+        *proc << KProcess::quote( qmakebin ) << "-d";
+        proc->setWorkingDirectory( projdir );
+        *proc << "-o" << KProcess::quote( makefile->name() ) << KProcess::quote( qmakefile->name() );
+        connect( proc, SIGNAL( processExited( KProcess* ) ),
+                 this, SLOT( slotQMakeFinished( KProcess* ) ) );
+        connect( proc, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
+                 this, SLOT( slotStderr( KProcess*, char*, int ) ) );
+        kdDebug(9024) << "Running process: " << proc->args() << endl;
+        proc->start( KProcess::Block, KProcess::Stderr );
+        if( !proc->normalExit() || proc->exitStatus() != 0 )
+            kdDebug(9024) << "Couldn't start process for qmake vars" << proc->normalExit() << proc->coreDumped() << proc->exitStatus() << endl;
+
+    }
+}
+
+QMakeDefaultOpts::~QMakeDefaultOpts()
+{
+    delete proc;
+    proc = 0;
+    delete makefile;
+    makefile = 0;
+    delete qmakefile;
+    qmakefile = 0;
+    m_variables.clear();
+}
+
+void QMakeDefaultOpts::slotStderr( KProcess* , char* buf, int len )
+{
+    kdDebug(9024) << "error slot" << endl;
+    QString buffer = QString::fromLocal8Bit(buf, len);
+    QStringList lines = QStringList::split("\n", buffer);
+    for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it )
+    {
+        QRegExp re("DEBUG 1: ([^ =:]+) === (.*)");
+        if( re.exactMatch(*it) )
+        {
+            QString var = re.cap(1);
+            QStringList values = QStringList::split(" :: ", re.cap(2));
+            m_variables[var] = values;
+        }
+    }
+}
+
+void QMakeDefaultOpts::slotQMakeFinished( KProcess* )
+{
+    makefile->unlink();
+    qmakefile->unlink();
+    proc->closeAll();
+    delete proc;
+    proc = 0;
+}
+
+QStringList QMakeDefaultOpts::variableValues( const QString& var ) const
+{
+    QStringList result;
+    if( m_variables.contains(var) )
+        result = m_variables[var];
+    return result;
+}
+
+QStringList QMakeDefaultOpts::variables() const
+{
+    return m_variables.keys();
+}
+
+#include "qmakedefaultopts.moc"
+
+
+// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on
diff -u -x.svn -xMakefile.in -Nur kdevelop_org/buildtools/qmake/qmakedefaultopts.h kdevelop/buildtools/qmake/qmakedefaultopts.h
--- kdevelop_org/buildtools/qmake/qmakedefaultopts.h	1970-01-01 01:00:00.000000000 +0100
+++ kdevelop/buildtools/qmake/qmakedefaultopts.h	2006-11-08 22:07:20.000000000 +0100
@@ -0,0 +1,49 @@
+/***************************************************************************
+*   Copyright (C) 2006 by Andreas Pakulat                                 *
+*   apaku at 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.                                   *
+*                                                                         *
+***************************************************************************/
+
+#ifndef QMAKEDEFAULTOPTS_H
+#define QMAKEDEFAULTOPTS_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <ktempfile.h>
+
+class KProcess;
+class TrollProjectPart;
+
+class QMakeDefaultOpts : public QObject
+{
+    Q_OBJECT
+public:
+    QMakeDefaultOpts( QObject *parent = 0, const char *name = 0 );
+
+    ~QMakeDefaultOpts();
+
+    void readVariables( const QString& qtdir, const QString& projdir );
+
+    QStringList variableValues( const QString& ) const;
+    QStringList variables() const;
+
+private slots:
+    void slotStderr( KProcess*, char*, int );
+    void slotQMakeFinished( KProcess* );
+
+private:
+    QMap<QString, QStringList> m_variables;
+    KTempFile* qmakefile;
+    KTempFile* makefile;
+    KProcess* proc;
+};
+
+#endif
+
+
+// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on
diff -u -x.svn -xMakefile.in -Nur kdevelop_org/buildtools/qmake/scope.cpp kdevelop/buildtools/qmake/scope.cpp
--- kdevelop_org/buildtools/qmake/scope.cpp	2006-11-08 22:23:13.000000000 +0100
+++ kdevelop/buildtools/qmake/scope.cpp	2006-11-08 22:27:31.000000000 +0100
@@ -23,6 +23,7 @@
 
 #include "pathutil.h"
 #include "trollprojectpart.h"
+#include "qmakedefaultopts.h"
 
 const QStringList Scope::KnownVariables = QStringList() << "QT" << "CONFIG" << "TEMPLATE" << "SUBDIRS" << "VERSION" << "LIBS" << "target.path" << "INSTALLS" << "MAKEFILE" << "TARGETDEPS" << "INCLUDEPATH" << "TARGET" << "DESTDIR" << "DEFINES" << "QMAKE_CXXFLAGS_DEBUG" << "QMAKE_CXXFLAGS_RELEASE" << "OBJECTS_DIR" << "UI_DIR" << "MOC_DIR" << "IDL_COMPILER" << "IDL_OPTIONS" << "RCC_DIR" << "IDLS" << "RESOURCES" << "IMAGES" << "LEXSOURCES" << "DISTFILES" << "YACCSOURCES" << "TRANSLATIONS" << "HEADERS" << "SOURCES" << "INTERFACES" << "FORMS" ;
 
@@ -31,6 +32,12 @@
 Scope::Scope( const QString &filename, TrollProjectPart* part )
     : m_root( 0 ), m_incast( 0 ), m_parent( 0 ), m_num(0), m_isEnabled( true ), m_part(part)
 {
+    // The following should work, but always leaves a zombie process when used on my system.
+    QMakeDefaultOpts m_defaultopts;
+    m_defaultopts.readVariables( DomUtil::readEntry( *m_part->projectDom(), "/kdevcppsupport/qt/root", "" ), QFileInfo( filename ).dirPath( true ) );
+    QStringList vars = m_defaultopts.variables();
+    for( QStringList::const_iterator it = vars.begin(); it != vars.end(); ++it )
+        kdDebug(9024) << *it << "===" << m_defaultopts.variableValues( *it ).join( "::" ) << endl;
     if ( !loadFromFile( filename ) )
     {
         if( !QFileInfo( filename ).exists() )
diff -u -x.svn -xMakefile.in -Nur kdevelop_org/buildtools/qmake/scope.h kdevelop/buildtools/qmake/scope.h
--- kdevelop_org/buildtools/qmake/scope.h	2006-11-07 22:17:27.000000000 +0100
+++ kdevelop/buildtools/qmake/scope.h	2006-11-08 22:05:25.000000000 +0100
@@ -17,26 +17,12 @@
 #include <qmap.h>
 
 #include "qmakeast.h"
+#include "qmakedefaultopts.h"
 
 #ifdef DEBUG
 #include "qmakeastvisitor.h"
 #endif
 
-/*
- * TODO:
- * - Ask adymo again about variables, we can't just always put a new assignment at the end of the file, because that clutters the file after a few open/save cycles
-        -> recursively check for other assignments to the variable (into scopes)
- * - talk to adymo about the possibility to add a way to parse only part of a QMake project file in case the include-creation code doesn't work
-        -> see mail, find out wether the proposed function could be better, flex reading from memory...
- * - talk to adymo why function calls are "automatically" scopes and not function calls
-        -> line 243 in parser, in init() do special handling of FunctionCall's, make a FunctionScope+AssignmentAST out of it, or use http://rafb.net/paste/results/EE43DN82.html patch
- * - talk to adymo about memory handling in AST's, do they delete their children upon deletion?
-        -> deletion of childs.
- * - talk to adymo about how to handle unparsable file's - overwrite or set m_root to 0?
-        -> to rare to handle specially.
- * - Handle multiple function calls with different arguments
- */
-
 class Scope;
 class TrollProjectPart;
 
@@ -256,6 +242,7 @@
     unsigned int m_num;
     bool m_isEnabled;
     TrollProjectPart* m_part;
+
 #ifdef DEBUG
     class PrintAST : QMake::ASTVisitor
     {
@@ -289,6 +276,7 @@
         QString getIndent();
         QString replaceWs(QString);
         int indent;
+
     };
 #endif
 
diff -u -x.svn -xMakefile.in -Nur kdevelop_org/buildtools/qmake/trollprojectpart.h kdevelop/buildtools/qmake/trollprojectpart.h
--- kdevelop_org/buildtools/qmake/trollprojectpart.h	2006-11-07 22:17:27.000000000 +0100
+++ kdevelop/buildtools/qmake/trollprojectpart.h	2006-11-08 15:36:06.000000000 +0100
@@ -26,6 +26,7 @@
 class KDialogBase;
 class TrollProjectWidget;
 class KDirWatch;
+class QMakeDefaultOpts;
 
 class TrollProjectPart : public KDevBuildTool
 {
@@ -95,6 +96,7 @@
 
     friend class TrollProjectWidget;
     friend class ProjectRunOptionsDlg;
+    friend class QMakeDefaultOpts;
 };
 
 #endif


More information about the kde-core-devel mailing list