1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "plugingenerator.h"
35 #include "pluginoptions.h"
37 #include <coreplugin/basefilewizard.h>
39 #include <cpptools/abstracteditorsupport.h>
41 #include <QtCore/QFileInfo>
42 #include <QtCore/QDir>
43 #include <QtCore/QSet>
45 static QString headerGuard(const QString &header)
47 return header.toUpper().replace(QRegExp(QLatin1String("[^A-Z0-9]+")), QLatin1String("_"));
50 namespace Qt4ProjectManager {
53 struct ProjectContents {
60 // Create a binary icon file
61 static inline Core::GeneratedFile generateIconFile(const QString &source, const QString &target, QString *errorMessage)
64 QFile iconFile(source);
65 if (!iconFile.open(QIODevice::ReadOnly)) {
66 *errorMessage = PluginGenerator::tr("Cannot open icon file %1.").arg(source);
67 return Core::GeneratedFile();
69 const QByteArray iconData = iconFile.readAll();
70 Core::GeneratedFile rc(target);
71 rc.setBinaryContents(iconData);
76 QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationParameters& p, const PluginOptions &options,
77 QString *errorMessage)
79 const QChar slash = QLatin1Char('/');
80 const QChar blank = QLatin1Char(' ');
81 QList<Core::GeneratedFile> rc;
83 QString baseDir = p.path;
85 baseDir += p.fileName;
86 const QString slashLessBaseDir = baseDir;
89 QSet<QString> widgetLibraries;
90 QSet<QString> widgetProjects;
91 QMap<QString,ProjectContents> widgetProjectContents;
92 QString pluginIncludes;
93 QString pluginAdditions;
94 QString pluginHeaders;
95 QString pluginSources;
96 QSet<QString> pluginIcons;
100 // First create the widget wrappers (plugins) and - if requested - skeletons
102 const int widgetCount = options.widgetOptions.size();
103 for (int i = 0; i < widgetCount; i++) {
104 const PluginOptions::WidgetOptions &wo = options.widgetOptions.at(i);
106 sm.insert(QLatin1String("SINGLE_INCLUDE_GUARD"), headerGuard(wo.pluginHeaderFile));
107 sm.insert(QLatin1String("PLUGIN_CLASS"), wo.pluginClassName);
108 const QString pluginHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.h"), sm, errorMessage);
109 if (pluginHeaderContents.isEmpty())
110 return QList<Core::GeneratedFile>();
111 Core::GeneratedFile pluginHeader(baseDir + wo.pluginHeaderFile);
112 pluginHeader.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.pluginHeaderFile, wo.pluginClassName)
113 + pluginHeaderContents);
114 rc.push_back(pluginHeader);
116 sm.remove(QLatin1String("SINGLE_INCLUDE_GUARD"));
117 sm.insert(QLatin1String("PLUGIN_HEADER"), wo.pluginHeaderFile);
118 sm.insert(QLatin1String("WIDGET_CLASS"), wo.widgetClassName);
119 sm.insert(QLatin1String("WIDGET_HEADER"), wo.widgetHeaderFile);
120 sm.insert(QLatin1String("WIDGET_GROUP"), wo.group);
121 QString iconResource;
122 if (!wo.iconFile.isEmpty()) {
123 iconResource = QLatin1String("QLatin1String(\":/");
124 iconResource += QFileInfo(wo.iconFile).fileName();
125 iconResource += QLatin1String("\")");
127 sm.insert(QLatin1String("WIDGET_ICON"),iconResource);
128 sm.insert(QLatin1String("WIDGET_TOOLTIP"), cStringQuote(wo.toolTip));
129 sm.insert(QLatin1String("WIDGET_WHATSTHIS"), cStringQuote(wo.whatsThis));
130 sm.insert(QLatin1String("WIDGET_ISCONTAINER"), wo.isContainer ? QLatin1String("true") : QLatin1String("false"));
131 sm.insert(QLatin1String("WIDGET_DOMXML"), cStringQuote(wo.domXml));
132 sm.insert(QLatin1String("SINGLE_PLUGIN_EXPORT"),
133 options.widgetOptions.count() == 1 ?
134 QLatin1String("\nQ_EXPORT_PLUGIN2(") +
136 QLatin1String(", ") +
140 const QString pluginSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.cpp"), sm, errorMessage);
141 if (pluginSourceContents.isEmpty())
142 return QList<Core::GeneratedFile>();
143 Core::GeneratedFile pluginSource(baseDir + wo.pluginSourceFile);
144 pluginSource.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.pluginSourceFile, wo.pluginClassName)
145 + pluginSourceContents);
146 if (i == 0 && widgetCount == 1) // Open first widget unless collection
147 pluginSource.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
148 rc.push_back(pluginSource);
150 if (wo.sourceType == PluginOptions::WidgetOptions::LinkLibrary)
151 widgetLibraries.insert(QLatin1String("-l") + wo.widgetLibrary);
153 widgetProjects.insert(QLatin1String("include(") + wo.widgetProjectFile + QLatin1String(")"));
154 pluginIncludes += QLatin1String("#include \"") + wo.pluginHeaderFile + QLatin1String("\"\n");
156 QLatin1String(" m_widgets.append(new ") + wo.pluginClassName + QLatin1String("(this));\n");
157 pluginHeaders += QLatin1Char(' ') + wo.pluginHeaderFile;
158 pluginSources += QLatin1Char(' ') + wo.pluginSourceFile;
159 if (!wo.iconFile.isEmpty())
160 pluginIcons.insert(wo.iconFile);
162 if (wo.createSkeleton) {
163 ProjectContents &pc = widgetProjectContents[wo.widgetProjectFile];
164 if (pc.headers.isEmpty()) {
165 if (wo.sourceType == PluginOptions::WidgetOptions::LinkLibrary) {
166 pc.library = wo.widgetLibrary;
167 pc.tmpl = p.templatePath + QLatin1String("/tpl_widget_lib.pro");
169 pc.tmpl = p.templatePath + QLatin1String("/tpl_widget_include.pri");
171 widgetProjectContents.insert(wo.widgetProjectFile, pc);
173 if (pc.library != wo.widgetLibrary) {
174 *errorMessage = tr("Creating multiple widget libraries (%1, %2) in one project (%3) is not supported.")
175 .arg(pc.library, wo.widgetLibrary, wo.widgetProjectFile);
176 return QList<Core::GeneratedFile>();
179 pc.headers += blank + wo.widgetHeaderFile;
180 pc.sources += blank + wo.widgetSourceFile;
183 sm.insert(QLatin1String("WIDGET_INCLUDE_GUARD"), headerGuard(wo.widgetHeaderFile));
184 sm.insert(QLatin1String("WIDGET_BASE_CLASS"), wo.widgetBaseClassName);
185 sm.insert(QLatin1String("WIDGET_CLASS"), wo.widgetClassName);
186 const QString widgetHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_widget.h"), sm, errorMessage);
187 if (widgetHeaderContents.isEmpty())
188 return QList<Core::GeneratedFile>();
189 Core::GeneratedFile widgetHeader(baseDir + wo.widgetHeaderFile);
190 widgetHeader.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.widgetHeaderFile, wo.widgetClassName)
191 + widgetHeaderContents);
192 rc.push_back(widgetHeader);
194 sm.remove(QLatin1String("WIDGET_INCLUDE_GUARD"));
195 sm.insert(QLatin1String("WIDGET_HEADER"), wo.widgetHeaderFile);
196 const QString widgetSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_widget.cpp"), sm, errorMessage);
197 if (widgetSourceContents.isEmpty())
198 return QList<Core::GeneratedFile>();
199 Core::GeneratedFile widgetSource(baseDir + wo.widgetSourceFile);
200 widgetSource.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.widgetSourceFile, wo.widgetClassName)
201 + widgetSourceContents);
202 rc.push_back(widgetSource);
206 // Then create the project files for the widget skeletons.
207 // These might create widgetLibraries or be included into the plugin's project.
208 QMap<QString,ProjectContents>::const_iterator it = widgetProjectContents.constBegin();
209 const QMap<QString,ProjectContents>::const_iterator end = widgetProjectContents.constEnd();
210 for (; it != end; ++it) {
211 const ProjectContents &pc = it.value();
213 sm.insert(QLatin1String("WIDGET_HEADERS"), pc.headers);
214 sm.insert(QLatin1String("WIDGET_SOURCES"), pc.sources);
215 if (!pc.library.isEmpty())
216 sm.insert(QLatin1String("WIDGET_LIBRARY"), pc.library);
217 const QString widgetPriContents = processTemplate(pc.tmpl, sm, errorMessage);
218 if (widgetPriContents.isEmpty())
219 return QList<Core::GeneratedFile>();
220 Core::GeneratedFile widgetPri(baseDir + it.key());
221 widgetPri.setContents(widgetPriContents);
222 rc.push_back(widgetPri);
225 // Create the sources for the collection if necessary.
226 if (widgetCount > 1) {
228 sm.insert(QLatin1String("COLLECTION_INCLUDE_GUARD"), headerGuard(options.collectionHeaderFile));
229 sm.insert(QLatin1String("COLLECTION_PLUGIN_CLASS"), options.collectionClassName);
230 const QString collectionHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.h"), sm, errorMessage);
231 if (collectionHeaderContents.isEmpty())
232 return QList<Core::GeneratedFile>();
233 Core::GeneratedFile collectionHeader(baseDir + options.collectionHeaderFile);
234 collectionHeader.setContents(CppTools::AbstractEditorSupport::licenseTemplate(options.collectionHeaderFile, options.collectionClassName)
235 + collectionHeaderContents);
236 rc.push_back(collectionHeader);
238 sm.remove(QLatin1String("COLLECTION_INCLUDE_GUARD"));
239 sm.insert(QLatin1String("PLUGIN_INCLUDES"),
241 QLatin1String("#include \"") +
242 options.collectionHeaderFile +
243 QLatin1String("\""));
244 sm.insert(QLatin1String("PLUGIN_ADDITIONS"), pluginAdditions);
245 sm.insert(QLatin1String("COLLECTION_PLUGIN_EXPORT"),
246 QLatin1String("Q_EXPORT_PLUGIN2(") +
248 QLatin1String(", ") +
249 options.collectionClassName +
251 const QString collectionSourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.cpp"), sm, errorMessage);
252 if (collectionSourceFileContents.isEmpty())
253 return QList<Core::GeneratedFile>();
254 Core::GeneratedFile collectionSource(baseDir + options.collectionSourceFile);
255 collectionSource.setContents(CppTools::AbstractEditorSupport::licenseTemplate(options.collectionSourceFile, options.collectionClassName)
256 + collectionSourceFileContents);
257 collectionSource.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
258 rc.push_back(collectionSource);
260 pluginHeaders += blank + options.collectionHeaderFile;
261 pluginSources += blank + options.collectionSourceFile;
264 // Copy icons that are not in the plugin source base directory yet (that is,
265 // probably all), add them to the resource file
267 foreach (QString icon, pluginIcons) {
268 const QFileInfo qfi(icon);
269 if (qfi.dir() != slashLessBaseDir) {
270 const QString newIcon = baseDir + qfi.fileName();
271 const Core::GeneratedFile iconFile = generateIconFile(icon, newIcon, errorMessage);
272 if (iconFile.path().isEmpty())
273 return QList<Core::GeneratedFile>();
274 rc.push_back(iconFile);
275 icon = qfi.fileName();
277 iconFiles += QLatin1String(" <file>") + icon + QLatin1String("</file>\n");
279 // Create the resource file with the icons.
281 sm.insert(QLatin1String("ICON_FILES"), iconFiles);
282 const QString resourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_resources.qrc"), sm, errorMessage);
283 if (resourceFileContents.isEmpty())
284 return QList<Core::GeneratedFile>();
285 Core::GeneratedFile resourceFile(baseDir + options.resourceFile);
286 resourceFile.setContents(resourceFileContents);
287 rc.push_back(resourceFile);
289 // Finally create the project for the plugin itself.
291 sm.insert(QLatin1String("PLUGIN_NAME"), options.pluginName);
292 sm.insert(QLatin1String("PLUGIN_HEADERS"), pluginHeaders);
293 sm.insert(QLatin1String("PLUGIN_SOURCES"), pluginSources);
294 sm.insert(QLatin1String("PLUGIN_RESOURCES"), options.resourceFile);
295 sm.insert(QLatin1String("WIDGET_LIBS"), QStringList(widgetLibraries.toList()).join(QString(blank)));
296 sm.insert(QLatin1String("INCLUSIONS"), QStringList(widgetProjects.toList()).join(QLatin1String("\n")));
297 const QString proFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_plugin.pro"), sm, errorMessage);
298 if (proFileContents.isEmpty())
299 return QList<Core::GeneratedFile>();
300 Core::GeneratedFile proFile(baseDir + p.fileName + QLatin1String(".pro"));
301 proFile.setContents(proFileContents);
302 proFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute);
303 rc.push_back(proFile);
307 QString PluginGenerator::processTemplate(const QString &tmpl,
308 const SubstitutionMap &substMap,
309 QString *errorMessage)
312 if (!tpl.open(QIODevice::ReadOnly|QIODevice::Text)) {
313 *errorMessage = tr("Cannot open %1: %2").arg(tmpl, tpl.errorString());
317 QString cont = QString::fromUtf8(tpl.readAll());
318 const QChar atChar = QLatin1Char('@');
321 const int start = cont.indexOf(atChar, offset);
324 const int end = cont.indexOf(atChar, start + 1);
326 const QString keyword = cont.mid(start + 1, end - start - 1);
327 const QString replacement = substMap.value(keyword);
328 cont.replace(start, end - start + 1, replacement);
329 offset = start + replacement.length();
334 QString PluginGenerator::cStringQuote(QString s)
336 s.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
337 s.replace(QLatin1Char('"'), QLatin1String("\\\""));
338 s.replace(QLatin1Char('\t'), QLatin1String("\\t"));
339 s.replace(QLatin1Char('\n'), QLatin1String("\\n"));