1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "genericproject.h"
35 #include "genericbuildconfiguration.h"
36 #include "genericprojectconstants.h"
37 #include "generictarget.h"
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>
49 #include <QtCore/QDir>
50 #include <QtCore/QProcessEnvironment>
52 #include <QtGui/QFormLayout>
53 #include <QtGui/QMainWindow>
54 #include <QtGui/QComboBox>
56 using namespace GenericProjectManager;
57 using namespace GenericProjectManager::Internal;
58 using namespace ProjectExplorer;
61 const char * const TOOLCHAIN_KEY("GenericProjectManager.GenericProject.Toolchain");
62 } // end of anonymous namespace
64 ////////////////////////////////////////////////////////////////////////////////////
66 ////////////////////////////////////////////////////////////////////////////////////
68 GenericProject::GenericProject(Manager *manager, const QString &fileName)
73 QFileInfo fileInfo(m_fileName);
74 QDir dir = fileInfo.dir();
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();
81 m_file = new GenericProjectFile(this, fileName);
82 m_rootNode = new GenericProjectNode(this, m_file);
84 m_manager->registerProject(this);
87 GenericProject::~GenericProject()
89 m_codeModelFuture.cancel();
90 m_manager->unregisterProject(this);
93 // do not delete m_toolChain
96 GenericTarget *GenericProject::activeTarget() const
98 return static_cast<GenericTarget *>(Project::activeTarget());
101 QString GenericProject::filesFileName() const
102 { return m_filesFileName; }
104 QString GenericProject::includesFileName() const
105 { return m_includesFileName; }
107 QString GenericProject::configFileName() const
108 { return m_configFileName; }
110 static QStringList readLines(const QString &absoluteFileName)
114 QFile file(absoluteFileName);
115 if (file.open(QFile::ReadOnly)) {
116 QTextStream stream(&file);
119 QString line = stream.readLine();
130 bool GenericProject::saveRawFileList(const QStringList &rawFileList)
132 // Make sure we can open the file for writing
133 QFile file(filesFileName());
134 if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
137 QTextStream stream(&file);
138 foreach (const QString &filePath, rawFileList)
139 stream << filePath << QLatin1Char('\n');
142 refresh(GenericProject::Files);
146 bool GenericProject::addFiles(const QStringList &filePaths)
148 QStringList newList = m_rawFileList;
150 QDir baseDir(QFileInfo(m_fileName).dir());
151 foreach (const QString &filePath, filePaths)
152 newList.append(baseDir.relativeFilePath(filePath));
154 return saveRawFileList(newList);
157 bool GenericProject::removeFiles(const QStringList &filePaths)
159 QStringList newList = m_rawFileList;
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());
167 return saveRawFileList(newList);
170 void GenericProject::parseProject(RefreshOptions options)
172 if (options & Files) {
173 m_rawListEntries.clear();
174 m_rawFileList = readLines(filesFileName());
175 m_files = processEntries(m_rawFileList, &m_rawListEntries);
178 if (options & Configuration) {
179 m_projectIncludePaths = processEntries(readLines(includesFileName()));
181 // TODO: Possibly load some configuration from the project file
182 //QSettings projectInfo(m_fileName, QSettings::IniFormat);
186 QFile configFile(configFileName());
187 if (configFile.open(QFile::ReadOnly))
188 m_defines = configFile.readAll();
192 emit fileListChanged();
195 void GenericProject::refresh(RefreshOptions options)
197 QSet<QString> oldFileList;
198 if (!(options & Configuration))
199 oldFileList = m_files.toSet();
201 parseProject(options);
204 m_rootNode->refresh();
206 CPlusPlus::CppModelManagerInterface *modelManager =
207 CPlusPlus::CppModelManagerInterface::instance();
210 CPlusPlus::CppModelManagerInterface::ProjectInfo pinfo = modelManager->projectInfo(this);
213 pinfo.defines = m_toolChain->predefinedMacros();
214 pinfo.defines += '\n';
216 foreach (const HeaderPath &headerPath, m_toolChain->systemHeaderPaths()) {
217 if (headerPath.kind() == HeaderPath::FrameworkHeaderPath)
218 pinfo.frameworkPaths.append(headerPath.path());
220 pinfo.includePaths.append(headerPath.path());
224 pinfo.includePaths += allIncludePaths();
225 pinfo.defines += m_defines;
228 pinfo.sourceFiles = files();
229 pinfo.sourceFiles += generated();
231 QStringList filesToUpdate;
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());
245 modelManager->updateProjectInfo(pinfo);
246 m_codeModelFuture = modelManager->updateSourceFiles(filesToUpdate);
251 * Expands environment variables in the given \a string when they are written
254 static void expandEnvironmentVariables(const QProcessEnvironment &env, QString &string)
256 const static QRegExp candidate(QLatin1String("\\$\\$\\((.+)\\)"));
258 int index = candidate.indexIn(string);
259 while (index != -1) {
260 const QString value = env.value(candidate.cap(1));
262 string.replace(index, candidate.matchedLength(), value);
263 index += value.length();
265 index = candidate.indexIn(string, index);
270 * Expands environment variables and converts the path from relative to the
271 * project to an absolute path.
273 * The \a map variable is an optional argument that will map the returned
274 * absolute paths back to their original \a entries.
276 QStringList GenericProject::processEntries(const QStringList &paths,
277 QHash<QString, QString> *map) const
279 const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
280 const QDir projectDir(QFileInfo(m_fileName).dir());
282 QStringList absolutePaths;
283 foreach (const QString &path, paths) {
284 QString trimmedPath = path.trimmed();
285 if (trimmedPath.isEmpty())
288 expandEnvironmentVariables(env, trimmedPath);
290 const QString absPath = QFileInfo(projectDir, trimmedPath).absoluteFilePath();
291 absolutePaths.append(absPath);
293 map->insert(absPath, trimmedPath);
295 absolutePaths.removeDuplicates();
296 return absolutePaths;
299 QStringList GenericProject::allIncludePaths() const
302 paths += m_includePaths;
303 paths += m_projectIncludePaths;
304 paths.removeDuplicates();
308 QStringList GenericProject::projectIncludePaths() const
309 { return m_projectIncludePaths; }
311 QStringList GenericProject::files() const
314 QStringList GenericProject::generated() const
315 { return m_generated; }
317 QStringList GenericProject::includePaths() const
318 { return m_includePaths; }
320 void GenericProject::setIncludePaths(const QStringList &includePaths)
321 { m_includePaths = includePaths; }
323 QByteArray GenericProject::defines() const
324 { return m_defines; }
326 void GenericProject::setToolChain(ToolChain *tc)
328 if (m_toolChain == tc)
332 refresh(Configuration);
334 emit toolChainChanged(m_toolChain);
337 ToolChain *GenericProject::toolChain() const
342 QString GenericProject::displayName() const
344 return m_projectName;
347 QString GenericProject::id() const
349 return QLatin1String(Constants::GENERICPROJECT_ID);
352 Core::IFile *GenericProject::file() const
357 IProjectManager *GenericProject::projectManager() const
362 QList<Project *> GenericProject::dependsOn()
364 return QList<Project *>();
367 QList<BuildConfigWidget*> GenericProject::subConfigWidgets()
369 QList<BuildConfigWidget*> list;
370 list << new BuildEnvironmentWidget;
374 GenericProjectNode *GenericProject::rootProjectNode() const
379 QStringList GenericProject::files(FilesMode fileMode) const
382 return m_files; // ### TODO: handle generated files here.
385 QStringList GenericProject::buildTargets() const
388 targets.append(QLatin1String("all"));
389 targets.append(QLatin1String("clean"));
393 QVariantMap GenericProject::toMap() const
395 QVariantMap map(Project::toMap());
396 map.insert(QLatin1String(TOOLCHAIN_KEY), m_toolChain ? m_toolChain->id() : QString());
400 bool GenericProject::fromMap(const QVariantMap &map)
402 if (!Project::fromMap(map))
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()) {
413 if (!t->activeRunConfiguration())
414 t->addRunConfiguration(new CustomExecutableRunConfiguration(t));
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)));
424 QString id = map.value(QLatin1String(TOOLCHAIN_KEY)).toString();
425 const ToolChainManager *toolChainManager = ToolChainManager::instance();
428 setToolChain(toolChainManager->findToolChain(id));
430 QList<ToolChain *> tcs = toolChainManager->findToolChains(Abi::hostAbi());
432 tcs = toolChainManager->toolChains();
434 setToolChain(tcs.at(0));
437 setIncludePaths(allIncludePaths());
443 ////////////////////////////////////////////////////////////////////////////////////
444 // GenericBuildSettingsWidget
445 ////////////////////////////////////////////////////////////////////////////////////
447 GenericBuildSettingsWidget::GenericBuildSettingsWidget(GenericTarget *target)
448 : m_target(target), m_toolChainChooser(0), m_buildConfiguration(0)
450 QFormLayout *fl = new QFormLayout(this);
451 fl->setContentsMargins(0, -1, 0, -1);
452 fl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
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()));
462 m_toolChainChooser = new QComboBox;
463 m_toolChainChooser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
464 updateToolChainList();
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()));
476 GenericBuildSettingsWidget::~GenericBuildSettingsWidget()
479 QString GenericBuildSettingsWidget::displayName() const
480 { return tr("Generic Manager"); }
482 void GenericBuildSettingsWidget::init(BuildConfiguration *bc)
484 m_buildConfiguration = static_cast<GenericBuildConfiguration *>(bc);
485 m_pathChooser->setPath(m_buildConfiguration->rawBuildDirectory());
488 void GenericBuildSettingsWidget::buildDirectoryChanged()
490 m_buildConfiguration->setBuildDirectory(m_pathChooser->rawPath());
493 void GenericBuildSettingsWidget::toolChainSelected(int index)
495 using namespace ProjectExplorer;
497 ToolChain *tc = static_cast<ToolChain *>(m_toolChainChooser->itemData(index).value<void *>());
498 m_target->genericProject()->setToolChain(tc);
501 void GenericBuildSettingsWidget::toolChainChanged(ProjectExplorer::ToolChain *tc)
503 for (int i = 0; i < m_toolChainChooser->count(); ++i) {
504 ToolChain * currentTc = static_cast<ToolChain *>(m_toolChainChooser->itemData(i).value<void *>());
507 m_toolChainChooser->setCurrentIndex(i);
512 void GenericBuildSettingsWidget::updateToolChainList()
514 m_toolChainChooser->clear();
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);
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);
529 ////////////////////////////////////////////////////////////////////////////////////
530 // GenericProjectFile
531 ////////////////////////////////////////////////////////////////////////////////////
533 GenericProjectFile::GenericProjectFile(GenericProject *parent, QString fileName)
534 : Core::IFile(parent),
539 GenericProjectFile::~GenericProjectFile()
542 bool GenericProjectFile::save(const QString &)
547 QString GenericProjectFile::fileName() const
552 QString GenericProjectFile::defaultPath() const
557 QString GenericProjectFile::suggestedFileName() const
562 QString GenericProjectFile::mimeType() const
564 return Constants::GENERICMIMETYPE;
567 bool GenericProjectFile::isModified() const
572 bool GenericProjectFile::isReadOnly() const
577 bool GenericProjectFile::isSaveAsAllowed() const
582 void GenericProjectFile::rename(const QString &newName)
589 Core::IFile::ReloadBehavior GenericProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
593 return BehaviorSilent;
596 void GenericProjectFile::reload(ReloadFlag flag, ChangeType type)