OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / genericprojectmanager / genericproject.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "genericproject.h"
34
35 #include "genericbuildconfiguration.h"
36 #include "genericprojectconstants.h"
37 #include "generictarget.h"
38
39 #include <projectexplorer/buildenvironmentwidget.h>
40 #include <projectexplorer/headerpath.h>
41 #include <projectexplorer/customexecutablerunconfiguration.h>
42 #include <projectexplorer/toolchainmanager.h>
43 #include <projectexplorer/projectexplorerconstants.h>
44 #include <cplusplus/ModelManagerInterface.h>
45 #include <extensionsystem/pluginmanager.h>
46 #include <utils/pathchooser.h>
47 #include <coreplugin/icore.h>
48
49 #include <QtCore/QDir>
50 #include <QtCore/QProcessEnvironment>
51
52 #include <QtGui/QFormLayout>
53 #include <QtGui/QMainWindow>
54 #include <QtGui/QComboBox>
55
56 using namespace GenericProjectManager;
57 using namespace GenericProjectManager::Internal;
58 using namespace ProjectExplorer;
59
60 namespace {
61 const char * const TOOLCHAIN_KEY("GenericProjectManager.GenericProject.Toolchain");
62 } // end of anonymous namespace
63
64 ////////////////////////////////////////////////////////////////////////////////////
65 // GenericProject
66 ////////////////////////////////////////////////////////////////////////////////////
67
68 GenericProject::GenericProject(Manager *manager, const QString &fileName)
69     : m_manager(manager),
70       m_fileName(fileName),
71       m_toolChain(0)
72 {
73     QFileInfo fileInfo(m_fileName);
74     QDir dir = fileInfo.dir();
75
76     m_projectName      = fileInfo.completeBaseName();
77     m_filesFileName    = QFileInfo(dir, m_projectName + QLatin1String(".files")).absoluteFilePath();
78     m_includesFileName = QFileInfo(dir, m_projectName + QLatin1String(".includes")).absoluteFilePath();
79     m_configFileName   = QFileInfo(dir, m_projectName + QLatin1String(".config")).absoluteFilePath();
80
81     m_file = new GenericProjectFile(this, fileName);
82     m_rootNode = new GenericProjectNode(this, m_file);
83
84     m_manager->registerProject(this);
85 }
86
87 GenericProject::~GenericProject()
88 {
89     m_codeModelFuture.cancel();
90     m_manager->unregisterProject(this);
91
92     delete m_rootNode;
93     // do not delete m_toolChain
94 }
95
96 GenericTarget *GenericProject::activeTarget() const
97 {
98     return static_cast<GenericTarget *>(Project::activeTarget());
99 }
100
101 QString GenericProject::filesFileName() const
102 { return m_filesFileName; }
103
104 QString GenericProject::includesFileName() const
105 { return m_includesFileName; }
106
107 QString GenericProject::configFileName() const
108 { return m_configFileName; }
109
110 static QStringList readLines(const QString &absoluteFileName)
111 {
112     QStringList lines;
113
114     QFile file(absoluteFileName);
115     if (file.open(QFile::ReadOnly)) {
116         QTextStream stream(&file);
117
118         forever {
119             QString line = stream.readLine();
120             if (line.isNull())
121                 break;
122
123             lines.append(line);
124         }
125     }
126
127     return lines;
128 }
129
130 bool GenericProject::saveRawFileList(const QStringList &rawFileList)
131 {
132     // Make sure we can open the file for writing
133     QFile file(filesFileName());
134     if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
135         return false;
136
137     QTextStream stream(&file);
138     foreach (const QString &filePath, rawFileList)
139         stream << filePath << QLatin1Char('\n');
140
141     file.close();
142     refresh(GenericProject::Files);
143     return true;
144 }
145
146 bool GenericProject::addFiles(const QStringList &filePaths)
147 {
148     QStringList newList = m_rawFileList;
149
150     QDir baseDir(QFileInfo(m_fileName).dir());
151     foreach (const QString &filePath, filePaths)
152         newList.append(baseDir.relativeFilePath(filePath));
153
154     return saveRawFileList(newList);
155 }
156
157 bool GenericProject::removeFiles(const QStringList &filePaths)
158 {
159     QStringList newList = m_rawFileList;
160
161     foreach (const QString &filePath, filePaths) {
162         QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
163         if (i != m_rawListEntries.end())
164             newList.removeOne(i.value());
165     }
166
167     return saveRawFileList(newList);
168 }
169
170 void GenericProject::parseProject(RefreshOptions options)
171 {
172     if (options & Files) {
173         m_rawListEntries.clear();
174         m_rawFileList = readLines(filesFileName());
175         m_files = processEntries(m_rawFileList, &m_rawListEntries);
176     }
177
178     if (options & Configuration) {
179         m_projectIncludePaths = processEntries(readLines(includesFileName()));
180
181         // TODO: Possibly load some configuration from the project file
182         //QSettings projectInfo(m_fileName, QSettings::IniFormat);
183
184         m_defines.clear();
185
186         QFile configFile(configFileName());
187         if (configFile.open(QFile::ReadOnly))
188             m_defines = configFile.readAll();
189     }
190
191     if (options & Files)
192         emit fileListChanged();
193 }
194
195 void GenericProject::refresh(RefreshOptions options)
196 {
197     QSet<QString> oldFileList;
198     if (!(options & Configuration))
199         oldFileList = m_files.toSet();
200
201     parseProject(options);
202
203     if (options & Files)
204         m_rootNode->refresh();
205
206     CPlusPlus::CppModelManagerInterface *modelManager =
207         CPlusPlus::CppModelManagerInterface::instance();
208
209     if (modelManager) {
210         CPlusPlus::CppModelManagerInterface::ProjectInfo pinfo = modelManager->projectInfo(this);
211
212         if (m_toolChain) {
213             pinfo.defines = m_toolChain->predefinedMacros();
214             pinfo.defines += '\n';
215
216             foreach (const HeaderPath &headerPath, m_toolChain->systemHeaderPaths()) {
217                 if (headerPath.kind() == HeaderPath::FrameworkHeaderPath)
218                     pinfo.frameworkPaths.append(headerPath.path());
219                 else
220                     pinfo.includePaths.append(headerPath.path());
221             }
222         }
223
224         pinfo.includePaths += allIncludePaths();
225         pinfo.defines += m_defines;
226
227         // ### add _defines.
228         pinfo.sourceFiles = files();
229         pinfo.sourceFiles += generated();
230
231         QStringList filesToUpdate;
232
233         if (options & Configuration) {
234             filesToUpdate = pinfo.sourceFiles;
235             filesToUpdate.append(QLatin1String("<configuration>")); // XXX don't hardcode configuration file name
236             // Full update, if there's a code model update, cancel it
237             m_codeModelFuture.cancel();
238         } else if (options & Files) {
239             // Only update files that got added to the list
240             QSet<QString> newFileList = m_files.toSet();
241             newFileList.subtract(oldFileList);
242             filesToUpdate.append(newFileList.toList());
243         }
244
245         modelManager->updateProjectInfo(pinfo);
246         m_codeModelFuture = modelManager->updateSourceFiles(filesToUpdate);
247     }
248 }
249
250 /**
251  * Expands environment variables in the given \a string when they are written
252  * like $$(VARIABLE).
253  */
254 static void expandEnvironmentVariables(const QProcessEnvironment &env, QString &string)
255 {
256     const static QRegExp candidate(QLatin1String("\\$\\$\\((.+)\\)"));
257
258     int index = candidate.indexIn(string);
259     while (index != -1) {
260         const QString value = env.value(candidate.cap(1));
261
262         string.replace(index, candidate.matchedLength(), value);
263         index += value.length();
264
265         index = candidate.indexIn(string, index);
266     }
267 }
268
269 /**
270  * Expands environment variables and converts the path from relative to the
271  * project to an absolute path.
272  *
273  * The \a map variable is an optional argument that will map the returned
274  * absolute paths back to their original \a entries.
275  */
276 QStringList GenericProject::processEntries(const QStringList &paths,
277                                            QHash<QString, QString> *map) const
278 {
279     const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
280     const QDir projectDir(QFileInfo(m_fileName).dir());
281
282     QStringList absolutePaths;
283     foreach (const QString &path, paths) {
284         QString trimmedPath = path.trimmed();
285         if (trimmedPath.isEmpty())
286             continue;
287
288         expandEnvironmentVariables(env, trimmedPath);
289
290         const QString absPath = QFileInfo(projectDir, trimmedPath).absoluteFilePath();
291         absolutePaths.append(absPath);
292         if (map)
293             map->insert(absPath, trimmedPath);
294     }
295     absolutePaths.removeDuplicates();
296     return absolutePaths;
297 }
298
299 QStringList GenericProject::allIncludePaths() const
300 {
301     QStringList paths;
302     paths += m_includePaths;
303     paths += m_projectIncludePaths;
304     paths.removeDuplicates();
305     return paths;
306 }
307
308 QStringList GenericProject::projectIncludePaths() const
309 { return m_projectIncludePaths; }
310
311 QStringList GenericProject::files() const
312 { return m_files; }
313
314 QStringList GenericProject::generated() const
315 { return m_generated; }
316
317 QStringList GenericProject::includePaths() const
318 { return m_includePaths; }
319
320 void GenericProject::setIncludePaths(const QStringList &includePaths)
321 { m_includePaths = includePaths; }
322
323 QByteArray GenericProject::defines() const
324 { return m_defines; }
325
326 void GenericProject::setToolChain(ToolChain *tc)
327 {
328     if (m_toolChain == tc)
329         return;
330
331     m_toolChain = tc;
332     refresh(Configuration);
333
334     emit toolChainChanged(m_toolChain);
335 }
336
337 ToolChain *GenericProject::toolChain() const
338 {
339     return m_toolChain;
340 }
341
342 QString GenericProject::displayName() const
343 {
344     return m_projectName;
345 }
346
347 QString GenericProject::id() const
348 {
349     return QLatin1String(Constants::GENERICPROJECT_ID);
350 }
351
352 Core::IFile *GenericProject::file() const
353 {
354     return m_file;
355 }
356
357 IProjectManager *GenericProject::projectManager() const
358 {
359     return m_manager;
360 }
361
362 QList<Project *> GenericProject::dependsOn()
363 {
364     return QList<Project *>();
365 }
366
367 QList<BuildConfigWidget*> GenericProject::subConfigWidgets()
368 {
369     QList<BuildConfigWidget*> list;
370     list << new BuildEnvironmentWidget;
371     return list;
372 }
373
374 GenericProjectNode *GenericProject::rootProjectNode() const
375 {
376     return m_rootNode;
377 }
378
379 QStringList GenericProject::files(FilesMode fileMode) const
380 {
381     Q_UNUSED(fileMode)
382     return m_files; // ### TODO: handle generated files here.
383 }
384
385 QStringList GenericProject::buildTargets() const
386 {
387     QStringList targets;
388     targets.append(QLatin1String("all"));
389     targets.append(QLatin1String("clean"));
390     return targets;
391 }
392
393 QVariantMap GenericProject::toMap() const
394 {
395     QVariantMap map(Project::toMap());
396     map.insert(QLatin1String(TOOLCHAIN_KEY), m_toolChain ? m_toolChain->id() : QString());
397     return map;
398 }
399
400 bool GenericProject::fromMap(const QVariantMap &map)
401 {
402     if (!Project::fromMap(map))
403         return false;
404
405     // Sanity check: We need both a buildconfiguration and a runconfiguration!
406     QList<Target *> targetList = targets();
407     foreach (Target *t, targetList) {
408         if (!t->activeBuildConfiguration()) {
409             removeTarget(t);
410             delete t;
411             continue;
412         }
413         if (!t->activeRunConfiguration())
414             t->addRunConfiguration(new CustomExecutableRunConfiguration(t));
415     }
416
417     // Add default setup:
418     if (targets().isEmpty()) {
419         GenericTargetFactory *factory =
420                 ExtensionSystem::PluginManager::instance()->getObject<GenericTargetFactory>();
421         addTarget(factory->create(this, QLatin1String(GENERIC_DESKTOP_TARGET_ID)));
422     }
423
424     QString id = map.value(QLatin1String(TOOLCHAIN_KEY)).toString();
425     const ToolChainManager *toolChainManager = ToolChainManager::instance();
426
427     if (!id.isNull()) {
428         setToolChain(toolChainManager->findToolChain(id));
429     } else {
430         QList<ToolChain *> tcs = toolChainManager->findToolChains(Abi::hostAbi());
431         if (tcs.isEmpty())
432             tcs = toolChainManager->toolChains();
433         if (!tcs.isEmpty())
434             setToolChain(tcs.at(0));
435     }
436
437     setIncludePaths(allIncludePaths());
438
439     refresh(Everything);
440     return true;
441 }
442
443 ////////////////////////////////////////////////////////////////////////////////////
444 // GenericBuildSettingsWidget
445 ////////////////////////////////////////////////////////////////////////////////////
446
447 GenericBuildSettingsWidget::GenericBuildSettingsWidget(GenericTarget *target)
448     : m_target(target), m_toolChainChooser(0), m_buildConfiguration(0)
449 {
450     QFormLayout *fl = new QFormLayout(this);
451     fl->setContentsMargins(0, -1, 0, -1);
452     fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
453
454     // build directory
455     m_pathChooser = new Utils::PathChooser(this);
456     m_pathChooser->setEnabled(true);
457     m_pathChooser->setBaseDirectory(m_target->genericProject()->projectDirectory());
458     fl->addRow(tr("Build directory:"), m_pathChooser);
459     connect(m_pathChooser, SIGNAL(changed(QString)), this, SLOT(buildDirectoryChanged()));
460
461     // tool chain
462     m_toolChainChooser = new QComboBox;
463     m_toolChainChooser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
464     updateToolChainList();
465
466     fl->addRow(tr("Tool chain:"), m_toolChainChooser);
467     connect(m_toolChainChooser, SIGNAL(activated(int)), this, SLOT(toolChainSelected(int)));
468     connect(m_target->genericProject(), SIGNAL(toolChainChanged(ProjectExplorer::ToolChain*)),
469             this, SLOT(toolChainChanged(ProjectExplorer::ToolChain*)));
470     connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainAdded(ProjectExplorer::ToolChain*)),
471             this, SLOT(updateToolChainList()));
472     connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainRemoved(ProjectExplorer::ToolChain*)),
473             this, SLOT(updateToolChainList()));
474 }
475
476 GenericBuildSettingsWidget::~GenericBuildSettingsWidget()
477 { }
478
479 QString GenericBuildSettingsWidget::displayName() const
480 { return tr("Generic Manager"); }
481
482 void GenericBuildSettingsWidget::init(BuildConfiguration *bc)
483 {
484     m_buildConfiguration = static_cast<GenericBuildConfiguration *>(bc);
485     m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory());
486 }
487
488 void GenericBuildSettingsWidget::buildDirectoryChanged()
489 {
490     m_buildConfiguration->setBuildDirectory(m_pathChooser->rawPath());
491 }
492
493 void GenericBuildSettingsWidget::toolChainSelected(int index)
494 {
495     using namespace ProjectExplorer;
496
497     ToolChain *tc = static_cast<ToolChain *>(m_toolChainChooser->itemData(index).value<void *>());
498     m_target->genericProject()->setToolChain(tc);
499 }
500
501 void GenericBuildSettingsWidget::toolChainChanged(ProjectExplorer::ToolChain *tc)
502 {
503     for (int i = 0; i < m_toolChainChooser->count(); ++i) {
504         ToolChain * currentTc = static_cast<ToolChain *>(m_toolChainChooser->itemData(i).value<void *>());
505         if (currentTc != tc)
506             continue;
507         m_toolChainChooser->setCurrentIndex(i);
508         return;
509     }
510 }
511
512 void GenericBuildSettingsWidget::updateToolChainList()
513 {
514     m_toolChainChooser->clear();
515
516     QList<ToolChain *> tcs = ToolChainManager::instance()->toolChains();
517     if (!m_target->genericProject()->toolChain()) {
518         m_toolChainChooser->addItem(tr("<Invalid tool chain>"), qVariantFromValue(static_cast<void *>(0)));
519         m_toolChainChooser->setCurrentIndex(0);
520     }
521     foreach (ToolChain *tc, tcs) {
522         m_toolChainChooser->addItem(tc->displayName(), qVariantFromValue(static_cast<void *>(tc)));
523         if (m_target->genericProject()->toolChain()
524                 && m_target->genericProject()->toolChain()->id() == tc->id())
525             m_toolChainChooser->setCurrentIndex(m_toolChainChooser->count() - 1);
526     }
527 }
528
529 ////////////////////////////////////////////////////////////////////////////////////
530 // GenericProjectFile
531 ////////////////////////////////////////////////////////////////////////////////////
532
533 GenericProjectFile::GenericProjectFile(GenericProject *parent, QString fileName)
534     : Core::IFile(parent),
535       m_project(parent),
536       m_fileName(fileName)
537 { }
538
539 GenericProjectFile::~GenericProjectFile()
540 { }
541
542 bool GenericProjectFile::save(const QString &)
543 {
544     return false;
545 }
546
547 QString GenericProjectFile::fileName() const
548 {
549     return m_fileName;
550 }
551
552 QString GenericProjectFile::defaultPath() const
553 {
554     return QString();
555 }
556
557 QString GenericProjectFile::suggestedFileName() const
558 {
559     return QString();
560 }
561
562 QString GenericProjectFile::mimeType() const
563 {
564     return Constants::GENERICMIMETYPE;
565 }
566
567 bool GenericProjectFile::isModified() const
568 {
569     return false;
570 }
571
572 bool GenericProjectFile::isReadOnly() const
573 {
574     return true;
575 }
576
577 bool GenericProjectFile::isSaveAsAllowed() const
578 {
579     return false;
580 }
581
582 void GenericProjectFile::rename(const QString &newName)
583 {
584     // Can't happen
585     Q_UNUSED(newName);
586     Q_ASSERT(false);
587 }
588
589 Core::IFile::ReloadBehavior GenericProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
590 {
591     Q_UNUSED(state)
592     Q_UNUSED(type)
593     return BehaviorSilent;
594 }
595
596 void GenericProjectFile::reload(ReloadFlag flag, ChangeType type)
597 {
598     Q_UNUSED(flag)
599     Q_UNUSED(type)
600 }