diff --git a/share/qtcreator/templates/wizards/README.txt b/share/qtcreator/templates/wizards/README.txt
index 695c1ef7e969a5f02300a9e01adbd30e84703675..3a2e94862246b9a0a233ac46b27f182fa54a9f44 100644
--- a/share/qtcreator/templates/wizards/README.txt
+++ b/share/qtcreator/templates/wizards/README.txt
@@ -1,6 +1,7 @@
-Qt Creator custom wizard are located in this directory.
+Qt Creator custom wizards are located in this directory.
 
-The subdirectories 'helloworld' and 'listmodel' are provided as examples.
+The subdirectories 'helloworld', 'listmodel' and 'scriptgeneratedproject'
+are provided as examples.
 To see how they work in Qt Creator, rename the 'wizard_sample.xml' files
 to 'wizard.xml'.
 
diff --git a/share/qtcreator/templates/wizards/scriptgeneratedproject/generate.pl b/share/qtcreator/templates/wizards/scriptgeneratedproject/generate.pl
new file mode 100755
index 0000000000000000000000000000000000000000..3f4103c18139e929ef379c38ad4e499c7d882512
--- /dev/null
+++ b/share/qtcreator/templates/wizards/scriptgeneratedproject/generate.pl
@@ -0,0 +1,108 @@
+#!/usr/bin/perl -w
+
+# *************************************************************************
+#
+# This file is part of Qt Creator
+#
+# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+#
+# Contact: Nokia Corporation (qt-info@nokia.com)
+#
+# Commercial Usage
+#
+# Licensees holding valid Qt Commercial licenses may use this file in
+# accordance with the Qt Commercial License Agreement provided with the
+# Software or, alternatively, in accordance with the terms contained in
+# a written agreement between you and Nokia.
+#
+# GNU Lesser General Public License Usage
+#
+# Alternatively, this file may be used under the terms of the GNU Lesser
+# General Public License version 2.1 as published by the Free Software
+# Foundation and appearing in the file LICENSE.LGPL included in the
+# packaging of this file.  Please review the following information to
+# ensure the GNU Lesser General Public License version 2.1 requirements
+# will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+#
+# If you are unsure which license is appropriate for your use, please
+# contact the sales department at http://qt.nokia.com/contact.
+#
+# *************************************************************************
+
+use strict;
+use Getopt::Long;
+use IO::File;
+
+my $optDryRun = 0;
+my $fieldClassName = 'MyClass';
+my $standardFieldProjectName = 'MyProject';
+my $standardFieldCppHeaderSuffix = 'h';
+my $standardFieldCppSourceSuffix = 'cpp';
+
+my $USAGE=<<EOF;
+Usage: generate.pl [--dry-run] <parameter-mappings>
+
+Custom wizard project generation example script.
+
+Known parameters: ClassName=<value>
+EOF
+
+if (!GetOptions("dry-run" => \$optDryRun)) {
+    print $USAGE;
+    exit (1);
+}
+
+if (scalar(@ARGV) == 0) {
+    print $USAGE;
+    exit (1);
+}
+
+# -- Parse the 'field=value' pairs
+foreach my $arg (@ARGV) {
+    my ($key, $value) = split('=', $arg);
+    $fieldClassName = $value if ($key eq 'ClassName');
+#   -- Standard fields defined by the custom project wizard
+    $standardFieldProjectName = $value if ($key eq 'ProjectName');
+    $standardFieldCppHeaderSuffix = $value if ($key eq 'CppHeaderSuffix');
+    $standardFieldCppSourceSuffix = $value if ($key eq 'CppSourceSuffix');
+}
+
+# -- Determine file names
+my $baseFileName = lc($fieldClassName);
+my $sourceFileName = $baseFileName . '.' . $standardFieldCppSourceSuffix;
+my $headerFileName = $baseFileName . '.' . $standardFieldCppHeaderSuffix;
+my $mainSourceFileName = 'main.' . $standardFieldCppSourceSuffix;
+my $projectFileName = lc($standardFieldProjectName) . '.pro';
+
+if ($optDryRun) {
+#   -- Step 1) Dry run: Print file names along with attributes
+    print $sourceFileName,",openeditor\n";
+    print $headerFileName,",openeditor\n";
+    print $mainSourceFileName,",openeditor\n";
+    print $projectFileName,",openproject\n";
+} else {
+#   -- Step 2) Actual file creation
+    print 'Generating ',  $headerFileName, ' ', $sourceFileName, ' ',
+          $mainSourceFileName, ' ', $projectFileName, "\n";
+    my $headerFile = new IO::File('>' . $headerFileName) or die ('Unable to open ' . $headerFileName . ' :' . $!);
+    print $headerFile '#ifndef ', uc($fieldClassName), "_H\n#define ", uc($fieldClassName), "_H\n\n",
+          'class ', $fieldClassName, "{\npublic:\n    ", $fieldClassName, "();\n\n};\n\n#endif\n";
+    $headerFile->close();
+
+    my $sourceFile = new IO::File('>' . $sourceFileName) or die ('Unable to open ' . $sourceFileName . ' :' . $!);
+    print $sourceFile  '#include "', $headerFileName ,"\"\n\n",
+            $fieldClassName,'::', $fieldClassName, "()\n{\n}\n";
+    $sourceFile->close();
+
+    my $mainSourceFile = new IO::File('>' . $mainSourceFileName) or die ('Unable to open ' . $mainSourceFileName . ' :' . $!);
+    print $mainSourceFile  '#include "', $headerFileName ,"\"\n\n",
+          "int main(int argc, char *argv[])\n{\n    ", $fieldClassName,' ', lc($fieldClassName),
+          ";\n    return 0;\n}\n";
+    $mainSourceFile->close();
+
+    my $projectFile = new IO::File('>' . $projectFileName) or die ('Unable to open ' . $projectFileName . ' :' . $!);
+    print $projectFile "TEMPLATE = app\nQT -= core\nCONFIG += console\nTARGET = ", $standardFieldProjectName,
+          "\nSOURCES += ", $sourceFileName, ' ',$headerFileName, ' ', $mainSourceFileName,
+          "\nHEADERS += ", $headerFileName,"\n";
+    $projectFile->close();
+}
diff --git a/share/qtcreator/templates/wizards/scriptgeneratedproject/wizard_sample.xml b/share/qtcreator/templates/wizards/scriptgeneratedproject/wizard_sample.xml
new file mode 100644
index 0000000000000000000000000000000000000000..09d55cd24ee3f272c894e5a31be65730ec917298
--- /dev/null
+++ b/share/qtcreator/templates/wizards/scriptgeneratedproject/wizard_sample.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+Custom class wizard example configuration file. -->
+<wizard version="1" class="qt4project" firstpage="10" kind="project" id="A.ScriptGeneratedProject" category="B.CustomProjects">
+    <description>Creates a simple project using a generator script</description>
+    <displayname>Simple Script-Generated Project</displayname>;
+    <displaycategory>Custom Projects</displaycategory>
+    <!-- Specify the generator script -->
+    <files generatorscript="generate.pl"/>
+    <!-- Create parameter wizard page -->
+    <fieldpagetitle>Simple Script-Generated Project Parameters</fieldpagetitle>
+    <fields>
+        <field name="ClassName">
+            <fieldcontrol class="QLineEdit" validator="^[a-zA-Z0-9_]+$" defaulttext="MyClass" />
+            <fielddescription>Class name:</fielddescription>
+        </field>
+    </fields>
+</wizard>
diff --git a/src/plugins/coreplugin/basefilewizard.cpp b/src/plugins/coreplugin/basefilewizard.cpp
index 9ab88b19118eb8f2b08b0b258853d49616f09dd4..789bdf48032238244f38c405abed83bb2c7e5094 100644
--- a/src/plugins/coreplugin/basefilewizard.cpp
+++ b/src/plugins/coreplugin/basefilewizard.cpp
@@ -552,12 +552,11 @@ QStringList BaseFileWizard::runWizard(const QString &path, QWidget *parent)
     }
 
     // Write
-    foreach (const GeneratedFile &generatedFile, files) {
-        if (!generatedFile.write(&errorMessage)) {
-            QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage);
-            return QStringList();
-        }
+    if (!writeFiles(files, &errorMessage)) {
+        QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage);
+        return QStringList();
     }
+
     bool removeOpenProjectAttribute = false;
     // Run the extensions
     foreach (IFileWizardExtension *ex, extensions) {
@@ -585,6 +584,17 @@ QStringList BaseFileWizard::runWizard(const QString &path, QWidget *parent)
     return result;
 }
 
+// Write
+bool BaseFileWizard::writeFiles(const GeneratedFiles &files, QString *errorMessage)
+{
+    foreach (const GeneratedFile &generatedFile, files)
+        if (!(generatedFile.attributes() & GeneratedFile::CustomGeneratorAttribute))
+            if (!generatedFile.write(errorMessage))
+                return false;
+    return true;
+}
+
+
 void BaseFileWizard::setupWizard(QWizard *w)
 {
     w->setOption(QWizard::NoCancelButton, false);
diff --git a/src/plugins/coreplugin/basefilewizard.h b/src/plugins/coreplugin/basefilewizard.h
index ac094bed7b8855711f431b1f38998cd8865fc626..aa130b073ac5b3284ed314a370594024e13efa87 100644
--- a/src/plugins/coreplugin/basefilewizard.h
+++ b/src/plugins/coreplugin/basefilewizard.h
@@ -65,7 +65,14 @@ class GeneratedFilePrivate;
 class CORE_EXPORT GeneratedFile
 {
 public:
-    enum Attribute { OpenEditorAttribute = 0x01, OpenProjectAttribute = 0x02 };
+    enum Attribute { // Open this file in editor
+                     OpenEditorAttribute = 0x01,
+                     // Open project
+                     OpenProjectAttribute = 0x02,
+                     /* File is generated by external scripts, do not write out,
+                      * see BaseFileWizard::writeFiles() */
+                     CustomGeneratorAttribute = 0x4
+                   };
     Q_DECLARE_FLAGS(Attributes, Attribute)
 
     GeneratedFile();
@@ -200,6 +207,10 @@ protected:
     virtual GeneratedFiles generateFiles(const QWizard *w,
                                          QString *errorMessage) const = 0;
 
+    /* Physically write files. Re-implement (calling the base implementation)
+     * to create files with CustomGeneratorAttribute set. */
+    virtual bool writeFiles(const GeneratedFiles &files, QString *errorMessage);
+
     // Overwrite for ProjectWizard kind and return the path to the generated project file
     virtual QString generatedProjectFilePath(const QWizard *wizard) const;
 
diff --git a/src/plugins/projectexplorer/customwizard/customwizard.cpp b/src/plugins/projectexplorer/customwizard/customwizard.cpp
index 33345c6db07bedd0a9a0847a6e0233a4931e2cf5..cb8eab04fcc215437eecf0b2f9809daa2c63cf23 100644
--- a/src/plugins/projectexplorer/customwizard/customwizard.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizard.cpp
@@ -32,6 +32,7 @@
 #include "customwizardpage.h"
 #include "projectexplorer.h"
 #include "baseprojectwizarddialog.h"
+#include "customwizardscriptgenerator.h"
 
 #include <coreplugin/icore.h>
 #include <coreplugin/messagemanager.h>
@@ -186,30 +187,80 @@ Core::GeneratedFiles CustomWizard::generateFiles(const QWizard *dialog, QString
     // Look for the Custom field page to find the path
     const Internal::CustomWizardPage *cwp = findWizardPage<Internal::CustomWizardPage>(dialog);
     QTC_ASSERT(cwp, return Core::GeneratedFiles())
-    QString path = cwp->path();
-    const FieldReplacementMap fieldMap = replacementMap(dialog);
+
+    CustomWizardContextPtr ctx = context();
+    ctx->targetPath = cwp->path();
+    ctx->replacements = replacementMap(dialog);
     if (CustomWizardPrivate::verbose) {
         QString logText;
         QTextStream str(&logText);
-        str << "CustomWizard::generateFiles: " << path << '\n';
-        const FieldReplacementMap::const_iterator cend = fieldMap.constEnd();
-        for (FieldReplacementMap::const_iterator it = fieldMap.constBegin(); it != cend; ++it)
+        str << "CustomWizard::generateFiles: " << ctx->targetPath << '\n';
+        const FieldReplacementMap::const_iterator cend = context()->replacements.constEnd();
+        for (FieldReplacementMap::const_iterator it = context()->replacements.constBegin(); it != cend; ++it)
             str << "  '" << it.key() << "' -> '" << it.value() << "'\n";
         qWarning("%s", qPrintable(logText));
     }
-    return generateWizardFiles(path, fieldMap, errorMessage);
+    return generateWizardFiles(errorMessage);
 }
 
-Core::GeneratedFiles CustomWizard::generateWizardFiles(const QString &targetPath,
-                                                       const FieldReplacementMap &fieldReplacementMap,
-                                                       QString *errorMessage) const
+
+bool CustomWizard::writeFiles(const Core::GeneratedFiles &files, QString *errorMessage)
+{
+    if (!Core::BaseFileWizard::writeFiles(files, errorMessage))
+        return false;
+    if (d->m_parameters->filesGeneratorScript.isEmpty())
+        return true;
+
+    // Prepare run of the custom script to generate. In the case of a
+    // project wizard that is entirely created by a script,
+    // the target project directory might not exist.
+    const CustomWizardContextPtr ctx = context();
+    QDir targetPathDir(ctx->targetPath);
+    if (!targetPathDir.exists()) {
+        if (CustomWizardPrivate::verbose)
+            qDebug("Creating directory %s", qPrintable(ctx->targetPath));
+        if (!targetPathDir.mkpath(ctx->targetPath)) {
+            *errorMessage = QString::fromLatin1("Unable to create the target directory '%1'").arg(ctx->targetPath);
+            return false;
+        }
+    }
+    // Run the custom script to actually generate the files.
+    if (!Internal::runCustomWizardGeneratorScript(ctx->targetPath, d->m_parameters->filesGeneratorScriptFullPath(),
+                                                  ctx->replacements, errorMessage))
+        return false;
+    // Paranoia: Check on the files generated by the script:
+    foreach (const Core::GeneratedFile &generatedFile, files)
+        if (generatedFile.attributes() & Core::GeneratedFile::CustomGeneratorAttribute)
+            if (!QFileInfo(generatedFile.path()).isFile()) {
+                *errorMessage = QString::fromLatin1("%1 failed to generate %2").
+                        arg(d->m_parameters->filesGeneratorScript, generatedFile.path());
+                return false;
+            }
+    return true;
+}
+
+Core::GeneratedFiles CustomWizard::generateWizardFiles(QString *errorMessage) const
 {
-    if (CustomWizardPrivate::verbose)
-        qDebug() << "Replacements" << fieldReplacementMap;
-    // Create files
     Core::GeneratedFiles rc;
+    const CustomWizardContextPtr ctx = context();
+
+    QTC_ASSERT(!ctx->targetPath.isEmpty(),  return rc)
+
+    if (CustomWizardPrivate::verbose)
+        qDebug() << "CustomWizard::generateWizardFiles: in "
+                 << ctx->targetPath << ", using: " << ctx->replacements;
+
+    // If generator script is non-empty, do a dry run to get it's files.
+    if (!d->m_parameters->filesGeneratorScript.isEmpty()) {
+        rc += Internal::dryRunCustomWizardGeneratorScript(ctx->targetPath,
+                                                          d->m_parameters->filesGeneratorScriptFullPath(),
+                                                          ctx->replacements, errorMessage);
+        if (rc.isEmpty())
+            return rc;
+    }
+    // Add the template files specified by the <file> elements.
     foreach(const Internal::CustomWizardFile &file, d->m_parameters->files)
-        if (!createFile(file, d->m_parameters->directory, targetPath, fieldReplacementMap, &rc, errorMessage))
+        if (!createFile(file, d->m_parameters->directory, ctx->targetPath, context()->replacements, &rc, errorMessage))
             return Core::GeneratedFiles();
     return rc;
 }
@@ -437,13 +488,22 @@ Core::GeneratedFiles CustomProjectWizard::generateFiles(const QWizard *w, QStrin
 {
     const BaseProjectWizardDialog *dialog = qobject_cast<const BaseProjectWizardDialog *>(w);
     QTC_ASSERT(dialog, return Core::GeneratedFiles())
-    const QString targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName();
-    // Add project name as macro.
+    // Add project name as macro. Path is here under project directory
+    CustomWizardContextPtr ctx = context();
+    ctx->targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName();
     FieldReplacementMap fieldReplacementMap = replacementMap(dialog);
     fieldReplacementMap.insert(QLatin1String("ProjectName"), dialog->projectName());
+    ctx->replacements = fieldReplacementMap;
     if (CustomWizardPrivate::verbose)
-        qDebug() << "CustomProjectWizard::generateFiles" << dialog << targetPath << fieldReplacementMap;
-    return generateWizardFiles(targetPath, fieldReplacementMap, errorMessage);
+        qDebug() << "CustomProjectWizard::generateFiles" << dialog << ctx->targetPath << ctx->replacements;
+    const Core::GeneratedFiles generatedFiles = generateWizardFiles(errorMessage);
+    // Find the project file and store in context
+    foreach(const Core::GeneratedFile &f, generatedFiles)
+        if (f.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
+            ctx->projectFilePath = f.path();
+            break;
+        }
+    return generatedFiles;
 }
 
 bool CustomProjectWizard::postGenerateOpen(const Core::GeneratedFiles &l, QString *errorMessage)
@@ -461,23 +521,11 @@ bool CustomProjectWizard::postGenerateOpen(const Core::GeneratedFiles &l, QStrin
     return BaseFileWizard::postGenerateOpenEditors(l, errorMessage);
 }
 
-QString CustomProjectWizard::generatedProjectFilePath(const QWizard *wizard) const
+QString CustomProjectWizard::generatedProjectFilePath(const QWizard *) const
 {
-    const BaseProjectWizardDialog *dialog = qobject_cast<const BaseProjectWizardDialog *>(wizard);
-    QTC_ASSERT(dialog, return QString())
-    const QString targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName();
-    const QChar slash =  QLatin1Char('/');
-    // take the first from parameters()->files list which have cwFile.openProject set
-    foreach(const Internal::CustomWizardFile &file, parameters()->files) {
-        if (file.openProject) {
-            FieldReplacementMap fieldReplacementMap = replacementMap(dialog);
-            fieldReplacementMap.insert(QLatin1String("ProjectName"), dialog->projectName());
-            QString target = file.target;
-            Internal::CustomWizardContext::replaceFields(fieldReplacementMap, &target);
-            return QDir::toNativeSeparators(targetPath + slash + target);
-        }
-    }
-    return QString();
+    if (CustomWizardPrivate::verbose)
+        qDebug("CustomProjectWizard::generatedProjectFilePath: '%s'", qPrintable(context()->projectFilePath));
+    return context()->projectFilePath;
 }
 
 bool CustomProjectWizard::postGenerateFiles(const QWizard *, const Core::GeneratedFiles &l, QString *errorMessage)
diff --git a/src/plugins/projectexplorer/customwizard/customwizard.h b/src/plugins/projectexplorer/customwizard/customwizard.h
index 5eb6d37e4be2587a73d2d0333e369cb89512e39d..fd864458dd8c75f8dc8cf41411ecc613afe9167a 100644
--- a/src/plugins/projectexplorer/customwizard/customwizard.h
+++ b/src/plugins/projectexplorer/customwizard/customwizard.h
@@ -119,11 +119,10 @@ protected:
                           const WizardPageList &extensionPages) const;
 
     // generate files in path
-    Core::GeneratedFiles generateWizardFiles(const QString &path,
-                                             const FieldReplacementMap &defaultFields,
-                                             QString *errorMessage) const;
+    Core::GeneratedFiles generateWizardFiles(QString *errorMessage) const;
     // Create replacement map as static base fields + QWizard fields
     FieldReplacementMap replacementMap(const QWizard *w) const;
+    virtual bool writeFiles(const Core::GeneratedFiles &files, QString *errorMessage);
 
     CustomWizardParametersPtr parameters() const;
     CustomWizardContextPtr context() const;
diff --git a/src/plugins/projectexplorer/customwizard/customwizard.pri b/src/plugins/projectexplorer/customwizard/customwizard.pri
index ac8bf2ea2601015aae5bd8eb4ee0a90556c2a35c..6b0066cbc650f2b0cb637f83ee3f5d9c468565b7 100644
--- a/src/plugins/projectexplorer/customwizard/customwizard.pri
+++ b/src/plugins/projectexplorer/customwizard/customwizard.pri
@@ -2,8 +2,10 @@ INCLUDEPATH *= $$PWD
 HEADERS += $$PWD/customwizard.h \
     $$PWD/customwizardparameters.h \
     $$PWD/customwizardpage.h \
-    customwizard/customwizardpreprocessor.h
+    customwizard/customwizardpreprocessor.h \
+    customwizard/customwizardscriptgenerator.h
 SOURCES += $$PWD/customwizard.cpp \
     $$PWD/customwizardparameters.cpp \
     $$PWD/customwizardpage.cpp \
-    customwizard/customwizardpreprocessor.cpp
+    customwizard/customwizardpreprocessor.cpp \
+    customwizard/customwizardscriptgenerator.cpp
diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp
index 848944b4dbdc5acf96393990c3ca50f080d1e707..eab3f8616554b49565d0c570f3df6a5fcb3fb5fd 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp
@@ -70,12 +70,10 @@ static const char fieldMandatoryAttributeC[] = "mandatory";
 static const char fieldControlElementC[] = "fieldcontrol";
 
 static const char filesElementC[] = "files";
+static const char filesGeneratorScriptAttributeC[] = "generatorscript";
 static const char fileElementC[] = "file";
-static const char fileNameSourceAttributeC[] = "source";
-static const char fileNameTargetAttributeC[] = "target";
-static const char fileNameOpenEditorAttributeC[] = "openeditor";
-static const char fileNameOpenProjectAttributeC[] = "openproject";
-
+static const char fileSourceAttributeC[] = "source";
+static const char fileTargetAttributeC[] = "target";
 
 enum ParseState {
     ParseBeginning,
@@ -95,6 +93,9 @@ enum ParseState {
 namespace ProjectExplorer {
 namespace Internal {
 
+const char customWizardFileOpenEditorAttributeC[] = "openeditor";
+const char customWizardFileOpenProjectAttributeC[] = "openproject";
+
 CustomWizardField::CustomWizardField() :
     mandatory(false)
 {
@@ -134,6 +135,7 @@ void CustomWizardParameters::clear()
     directory.clear();
     files.clear();
     fields.clear();
+    filesGeneratorScript.clear();
     firstPageId = -1;
 }
 
@@ -496,12 +498,15 @@ CustomWizardParameters::ParseResult
                         state = ParseWithinComboEntry;
                 }
                      break;
+                case ParseWithinFiles:
+                    filesGeneratorScript = attributeValue(reader, filesGeneratorScriptAttributeC);
+                    break;
                 case ParseWithinFile: { // file attribute
                         CustomWizardFile file;
-                        file.source = attributeValue(reader, fileNameSourceAttributeC);
-                        file.target = attributeValue(reader, fileNameTargetAttributeC);
-                        file.openEditor = booleanAttributeValue(reader, fileNameOpenEditorAttributeC, false);
-                        file.openProject = booleanAttributeValue(reader, fileNameOpenProjectAttributeC, false);
+                        file.source = attributeValue(reader, fileSourceAttributeC);
+                        file.target = attributeValue(reader, fileTargetAttributeC);
+                        file.openEditor = booleanAttributeValue(reader, customWizardFileOpenEditorAttributeC, false);
+                        file.openProject = booleanAttributeValue(reader, customWizardFileOpenProjectAttributeC, false);
                         if (file.target.isEmpty())
                             file.target = file.source;
                         if (file.source.isEmpty()) {
@@ -554,11 +559,23 @@ CustomWizardParameters::ParseResult
     return parse(configFile, configFileFullPath, bp, errorMessage);
 }
 
+QString CustomWizardParameters::filesGeneratorScriptFullPath() const
+{
+    if (filesGeneratorScript.isEmpty())
+        return QString();
+    QString rc = directory;
+    rc += QLatin1Char('/');
+    rc += filesGeneratorScript;
+    return rc;
+}
+
 QString CustomWizardParameters::toString() const
 {
     QString rc;
     QTextStream str(&rc);
     str << "Directory: " << directory << " Klass: '" << klass << "'\n";
+    if (!filesGeneratorScript.isEmpty())
+        str << "Script: '" <<  filesGeneratorScript << "'\n";
     foreach(const CustomWizardFile &f, files) {
         str << "  File source: " << f.source << " Target: " << f.target;
         if (f.openEditor)
@@ -652,6 +669,9 @@ void CustomWizardContext::reset()
                             mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)));
     baseReplacements.insert(QLatin1String("CppHeaderSuffix"),
                             mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)));
+    replacements.clear();
+    targetPath.clear();
+    projectFilePath.clear();
 }
 
 QString CustomWizardContext::processFile(const FieldReplacementMap &fm, QString in)
diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.h b/src/plugins/projectexplorer/customwizard/customwizardparameters.h
index 3f5b75196a894f117a14c078d058415921d82108..64f601d5f7145a341dd21537ef65f6d3b83aaaf4 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardparameters.h
+++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.h
@@ -81,9 +81,12 @@ public:
                       Core::BaseFileWizardParameters *bp, QString *errorMessage);
     QString toString() const;
 
+    QString filesGeneratorScriptFullPath() const;
+
     QString directory;
     QString klass;
     QList<CustomWizardFile> files;
+    QString filesGeneratorScript;
     QString fieldPageTitle;
     QList<CustomWizardField> fields;
     int firstPageId;
@@ -110,8 +113,16 @@ struct CustomWizardContext {
     static QString processFile(const FieldReplacementMap &fm, QString in);
 
     FieldReplacementMap baseReplacements;
+    FieldReplacementMap replacements;
+    // Where files should be created, that is, choosen path for simple wizards
+    // or "path/project" for project wizards.
+    QString targetPath;
+    QString projectFilePath;
 };
 
+extern const char customWizardFileOpenEditorAttributeC[];
+extern const char customWizardFileOpenProjectAttributeC[];
+
 } // namespace Internal
 } // namespace ProjectExplorer
 
diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4fb95c5d42b07178f2ece505f3c686b33acdc8d4
--- /dev/null
+++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp
@@ -0,0 +1,210 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "customwizardscriptgenerator.h"
+#include "customwizard.h"
+#include "customwizardparameters.h" // XML attributes
+
+#include <QtCore/QProcess>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDebug>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QSharedPointer>
+
+namespace ProjectExplorer {
+namespace Internal {
+
+typedef QSharedPointer<QTemporaryFile> TemporaryFilePtr;
+
+// Format pattern for temporary files
+static inline QString tempFilePattern()
+{
+    QString tempPattern = QDir::tempPath();
+    if (!tempPattern.endsWith(QLatin1Char('/')))
+        tempPattern += QLatin1Char('/');
+    tempPattern += QLatin1String("qtcreatorXXXXXX.txt");
+    return tempPattern;
+}
+
+// Create a temporary file with content
+static inline TemporaryFilePtr writeTemporaryFile(const QString &content)
+{
+    TemporaryFilePtr temporaryFile(new QTemporaryFile(tempFilePattern()));
+    if (!temporaryFile->open())
+        return TemporaryFilePtr();
+    temporaryFile->write(content.toLocal8Bit());
+    temporaryFile->close();
+    return temporaryFile;
+}
+
+// Helper for running the optional generation script.
+static bool
+    runGenerationScriptHelper(const QString &workingDirectory,
+                              QString binary, bool dryRun,
+                              const QMap<QString, QString> &fieldMap,
+                              QString *stdOut /* = 0 */, QString *errorMessage)
+{
+    typedef QSharedPointer<QTemporaryFile> TemporaryFilePtr;
+    typedef QList<TemporaryFilePtr> TemporaryFilePtrList;
+    typedef QMap<QString, QString>::const_iterator FieldConstIterator;
+
+    QProcess process;
+    QStringList arguments;
+    // Check on the process
+    const QFileInfo binaryInfo(binary);
+    if (!binaryInfo.isFile()) {
+        *errorMessage = QString::fromLatin1("The Generator script %1 does not exist").arg(binary);
+        return false;
+    }
+#ifdef Q_OS_WIN // Windows: Cannot run scripts by QProcess, do 'cmd /c'
+    const QString extension = binaryInfo.suffix();
+    if (!extension.isEmpty() && extension.compare(QLatin1String("exe"), Qt::CaseInsensitive) != 0) {
+        arguments.push_back(QLatin1String("/C"));
+        arguments.push_back(binary);
+        binary = QString::fromLocal8Bit(qgetenv("COMSPEC"));
+        if (binary.isEmpty())
+            binary = QLatin1String("cmd.exe");
+    }
+#endif // Q_OS_WIN
+    // Arguments
+    if (dryRun)
+        arguments << QLatin1String("--dry-run");
+    // Turn the field replacement map into a list of arguments "key=value".
+    // Pass on free-format-texts as a temporary files indicated by a colon
+    // separator "key:filename"
+    TemporaryFilePtrList temporaryFiles;
+
+    const FieldConstIterator cend = fieldMap.constEnd();
+    for (FieldConstIterator it = fieldMap.constBegin(); it != cend; ++it) {
+        const QString &value = it.value();
+        // Is a temporary file required?
+        const bool passAsTemporaryFile = value.contains(QLatin1Char('\n'));
+        if (passAsTemporaryFile) {
+            // Create a file and pass on as "key:filename"
+            TemporaryFilePtr temporaryFile = writeTemporaryFile(value);
+            if (temporaryFile.isNull()) {
+                *errorMessage = QString::fromLatin1("Cannot create temporary file");
+                return false;
+            }
+            temporaryFiles.push_back(temporaryFile);
+            arguments << (it.key() + QLatin1Char(':') + QDir::toNativeSeparators(temporaryFile->fileName()));
+        } else {
+            // Normal value "key=value"
+            arguments << (it.key() + QLatin1Char('=') + value);
+        }
+    }
+    process.setWorkingDirectory(workingDirectory);
+    if (CustomWizard::verbose())
+        qDebug("In %s, running:\n%s\n%s\n", qPrintable(workingDirectory),
+               qPrintable(binary),
+               qPrintable(arguments.join(QString(QLatin1Char(' ')))));
+    process.start(binary, arguments);
+    if (!process.waitForStarted()) {
+        *errorMessage = QString::fromLatin1("Unable to start generator script %1: %2").
+                arg(binary, process.errorString());
+        return false;
+    }
+    if (!process.waitForFinished()) {
+        *errorMessage = QString::fromLatin1("Generator script %1 timed out").arg(binary);
+        return false;
+    }
+    if (process.exitStatus() != QProcess::NormalExit) {
+        *errorMessage = QString::fromLatin1("Generator script %1 crashed").arg(binary);
+        return false;
+    }
+    if (process.exitCode() != 0) {
+        const QString stdErr = QString::fromLocal8Bit(process.readAllStandardError());
+        *errorMessage = QString::fromLatin1("Generator script %1 returned %2 (%3)").
+                arg(binary).arg(process.exitCode()).arg(stdErr);
+        return false;
+    }
+    if (stdOut) {
+        *stdOut = QString::fromLocal8Bit(process.readAllStandardOutput());
+        stdOut->remove(QLatin1Char('\r'));
+        if (CustomWizard::verbose())
+            qDebug("Output: '%s'\n", qPrintable(*stdOut));
+    }
+    return true;
+}
+
+// Do a dry run of the generation script to get a list of files
+Core::GeneratedFiles
+    dryRunCustomWizardGeneratorScript(const QString &targetPath,
+                                      const QString &script,
+                                      const QMap<QString, QString> &fieldMap,
+                                      QString *errorMessage)
+{
+    // Run in temporary directory as the target path may not exist yet.
+    QString stdOut;
+    if (!runGenerationScriptHelper(QDir::tempPath(), script, true,
+                             fieldMap, &stdOut, errorMessage))
+        return Core::GeneratedFiles();
+    Core::GeneratedFiles files;
+    // Parse the output consisting of lines with ',' separated tokens.
+    // (file name + attributes matching those of the <file> element)
+    foreach (const QString &line, stdOut.split(QLatin1Char('\n'))) {
+        const QString trimmed = line.trimmed();
+        if (!trimmed.isEmpty()) {
+            Core::GeneratedFile file;
+            Core::GeneratedFile::Attributes attributes = Core::GeneratedFile::CustomGeneratorAttribute;
+            const QStringList tokens = line.split(QLatin1Char(','));
+            const int count = tokens.count();
+            for (int i = 0; i < count; i++) {
+                const QString &token = tokens.at(i);
+                if (i) {
+                    if (token == QLatin1String(customWizardFileOpenEditorAttributeC))
+                        attributes |= Core::GeneratedFile::OpenEditorAttribute;
+                    else if (token == QLatin1String(customWizardFileOpenProjectAttributeC))
+                            attributes |= Core::GeneratedFile::OpenProjectAttribute;
+                } else {
+                    // Token 0 is file name. Wizard wants native names.
+                    const QString fullPath = targetPath + QLatin1Char('/') + token;
+                    file.setPath(QDir::toNativeSeparators(fullPath));
+                }
+            }
+            file.setAttributes(attributes);
+            files.push_back(file);
+        }
+    }
+    if (CustomWizard::verbose())
+        foreach(const Core::GeneratedFile &f, files)
+            qDebug() << script << " generated: " << f.path() << f.attributes();
+    return files;
+}
+
+bool runCustomWizardGeneratorScript(const QString &targetPath, const QString &script,
+                                    const QMap<QString, QString> &fieldMap, QString *errorMessage)
+{
+    return runGenerationScriptHelper(targetPath, script, false, fieldMap,
+                                     0, errorMessage);
+}
+
+} // namespace Internal
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.h b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.h
new file mode 100644
index 0000000000000000000000000000000000000000..d47168b3f934ae8d4364be6733e3a818047939f9
--- /dev/null
+++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.h
@@ -0,0 +1,81 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef CUSTOMWIZARDSCRIPTGENERATOR_H
+#define CUSTOMWIZARDSCRIPTGENERATOR_H
+
+#include <QtCore/QMap>
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+namespace Core {
+class GeneratedFile;
+};
+
+namespace ProjectExplorer {
+namespace Internal {
+
+/* Custom wizard script generator functions. In addition to the <file> elements
+ * that define template files in which macros are replaced, it is possible to have
+ * a custom wizard call a generation script (specified in the "generatorscript"
+ * attribute of the <files> element) which actually creates files.
+ * The command line of the script must follow the convention
+ *
+ * script [--dry-run] [Field1=Value1 [Field2=Value2] [Field3:Filename3]]]...
+ *
+ * Multiline texts will be passed on as temporary files using the colon
+ * separator.
+ * The parameters are the field values from the UI.
+ * As Qt Creator needs to know the file names before actually creates them to
+ * do overwrite checking etc., this is  2-step process:
+ * 1) Determine file names and attributes: The script is called with the
+ *    --dry-run option and the field values. It then prints the relative path
+ *    names it intends to create followed by comma-separated attributes
+ *    matching those of the <file> element, for example:
+ *        myclass.cpp,openeditor
+ * 2) The script is called with the parameters only in the working directory
+ * and then actually creates the files. If that involves directories, the script
+ * should create those, too.
+ */
+
+// Step 1) Do a dry run of the generation script to get a list of files on stdout
+QList<Core::GeneratedFile>
+    dryRunCustomWizardGeneratorScript(const QString &targetPath, const QString &script,
+                                      const QMap<QString, QString> &fieldMap,
+                                      QString *errorMessage);
+
+// Step 2) Generate files
+bool runCustomWizardGeneratorScript(const QString &targetPath, const QString &script,
+                                    const QMap<QString, QString> &fieldMap,
+                                    QString *errorMessage);
+
+} // namespace Internal
+} // namespace ProjectExplorer
+
+#endif // CUSTOMWIZARDSCRIPTGENERATOR_H