Инструменты пользователя

Инструменты сайта



// Qt Creator, CMake: отображение всех файлов в стоковом плагине

Немного в сторону от CMakeProjectManager2, в направлении стокового плагина.

Дискуссия на LOR навела на мысль. Ради спортивного интереса попробовал реализовать и… получилось! :)

За подробностями под кат.

// CMakeProjectManager2: теперь и с Server Mode

Вот и прошло почти два месяца с последних изменений.

Время было потрачено:

  1. для ожидания некоторой стабилизации апстрима, так как, по сути, они требовали писать абсолютно новый код для реализации текущего функционала, а в условиях сильных и фатальных изменений, переписывать свой код на каждый чих… у меня столько времени нет.
  2. была предпринята неудачная попытка продвинуть код в апстрим.
  3. ну и изучался новый код, когда выпадало время, плюс раздумья - как применить функциональность и для серверного режима работы CMake.

Так как код отвергли, а это, по сути, написанная с нуля реализация старого функционала, то он был взят за основу новых изменений. Ну как основу - 99%.

Есть и изменения. Малой кровью была создана обёртка поверх Server Mode Reader'а, которая предоставляет всего его внутренние профиты за исключением классического вида дерева файлов.

Соответственно изменения, сделанные для старого ридера (Tealeaf Reader), который используется для версий CMake младше 3.7, в части отображения всех файлов, добавления, удаления и переименования теперь применимы и для Server Mode Reader.

Сразу же был найден и первый баг: неправильно обрабатываются дефайны из CMAke (add_definition()). Патч уже отослан, у себя в коде изменений делать не буду, подожду, пока попадёт в апстрим, потом засинхронизируюсь.

Функциональность же передачи Toolchain файлов выла дропнута. Можно делать настройки через Kit или же вручную создать параметр CMAKE_TOOLCHAIN_FILE и задать нужный. Главное не забудьте сделать Build → Clear CMake Configuration после этого. Ну и будьте готовы, что парсер C++ вас перестанет понимать, как минимум, частично.

Пакеты для Ubuntu 14.04 и 16.04 и производных уже доступны через PPA. Не забываем внимательно читать описание репозитория.

В ближайших планах подготовить ветку для стабильной версии QtC - 4.2. Там окажется только текущая функциональность и нового появляться не будет (если только кто-то не возьмёт её на сопровождение). Есть вероятность, что серверный режим будет выключен, так как он требует много смежных изменений и бекпортирования. Явные косяки тоже будут переноситься. В будущем, планирую саппортить только текущую версию QtC. Для старых не планирую даже исправления ошибок переносить. На всё это нужно время.

Если кто-то предложит варианты, как делать пребилды плагина для официальных версий QtC в автоматическом или полуавтоматическом режиме, буду рад выслушать.

Ну и с ошибками и предложениями: https://github.com/h4tr3d/cmakeprojectmanager2/issues

// CMake и Qt Creator: на пути к CMake server mode

В транке появилась пачка изменений, ориентированные на использование CMake Server Mode, в связи с чем плагин теперь может работать только с CMake версии 3.0 и более новым. Тобиас крепко взялся за плагин и будем надеяться, что, как минимум, скоро не будет требоваться:

  1. промежуточная генерация CodeBlocks проекта, дабы распарсить цели, получить список файлов и параметров компилятора.
  2. ручное парсирование файла кеша, для получения списка опций и их изменения.

Пока CMake Server Mode большего не предоставлят: в основном информация, но не изменение её. Так что ожидать автоматическое добавление файла к нужной цели или переименование файла в билд-системе средствами этого нового режима не стоит.

Ну а в самом QtC пока только инфраструктурные изменения, чтобы эту фичу начать поддерживать.

И от себя: в CMakeProjectManager2 добавил возможность использовать трюк от cmake, что бы задать варианты возможных значений для какого-то параметра и выводить их при редактировании в виде выпадающего списка. Фичу портировал в апстрим и завёл ревью, кому нужно, голосуйте: https://codereview.qt-project.org/#/c/173340

Про сам трюк:

Если коротко, то в вашем CMakeLists.txt, для задания возможных значений для параметра, нужно добавить конструкцию:

set_property(CACHE OptionName PROPERTY STRINGS PossibleValue1 PossibleValue2 PossibleValue3 ... PossibleValueN)

Ни на что, кроме как для подсказки GUI этот параметр не влияет: CMake не делает валидацию введённых значений, поэтому возможность задать любое другое - остаётся.

Ссылки по теме:

// Обновление Qt Creator

Пока LOG, Habrahabr и другие гудят по поводу смены лицензионной политики в части Qt (переход на LPGL3) и QtC (переход с LGPL2.1 на GPL3 /именно GPL/ с исключением для плагинов), у меня дошли руки обновить PPA: https://launchpad.net/~adrozdoff/+archive/ubuntu/qtcreator-git

Ну и несколько интересных (для меня) изменений, которые стали доступны в этом билде. Добро пожаловать под кат.

// OpenOCD, GDB и (сильно)удалённая отладка

Статью изначально опубликовал на Хабре: http://habrahabr.ru/post/274179/

Дано:

  1. устройство с ARM926E-JS (Cypress FX3) на борту;
  2. находится на другом континенте;
  3. подключено (JTAG+USB+COM) к Linux компу;
  4. на комп есть SSH доступ (и больше ничего, только SSH порт).

Проблема: устройство нужно отлаживать и писать под него код. И делать это, желательно, удобно.

Решение с использованием OpenOCD, GDB и Qt Creator, а так же описание пути к нему, под катом.

// Qt Creator из Git, VirtualBox и Linux Guest

Заголовок не отражает всей сути. Конкретно:

Проблема: при запуске крешится. Выводится такой стек-трейс:

Нажмите, чтобы отобразить

Нажмите, чтобы скрыть

alexd@alexd-VirtualBox ~ $ qtcreator-git
libGL error: pci id for fd 18: 80ee:beef, driver (null)
OpenGL Warning: glFlushVertexArrayRangeNV not found in mesa table
OpenGL Warning: glVertexArrayRangeNV not found in mesa table
OpenGL Warning: glCombinerInputNV not found in mesa table
OpenGL Warning: glCombinerOutputNV not found in mesa table
OpenGL Warning: glCombinerParameterfNV not found in mesa table
OpenGL Warning: glCombinerParameterfvNV not found in mesa table
OpenGL Warning: glCombinerParameteriNV not found in mesa table
OpenGL Warning: glCombinerParameterivNV not found in mesa table
OpenGL Warning: glFinalCombinerInputNV not found in mesa table
OpenGL Warning: glGetCombinerInputParameterfvNV not found in mesa table
OpenGL Warning: glGetCombinerInputParameterivNV not found in mesa table
OpenGL Warning: glGetCombinerOutputParameterfvNV not found in mesa table
OpenGL Warning: glGetCombinerOutputParameterivNV not found in mesa table
OpenGL Warning: glGetFinalCombinerInputParameterfvNV not found in mesa table
OpenGL Warning: glGetFinalCombinerInputParameterivNV not found in mesa table
OpenGL Warning: glDeleteFencesNV not found in mesa table
OpenGL Warning: glFinishFenceNV not found in mesa table
OpenGL Warning: glGenFencesNV not found in mesa table
OpenGL Warning: glGetFenceivNV not found in mesa table
OpenGL Warning: glIsFenceNV not found in mesa table
OpenGL Warning: glSetFenceNV not found in mesa table
OpenGL Warning: glTestFenceNV not found in mesa table
libGL error: core dri or dri2 extension not found
libGL error: failed to load driver: vboxvideo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
OpenGL Warning: glXGetFBConfigAttrib for 0000000001deadb0, failed to get XVisualInfo
OpenGL Warning: XGetVisualInfo returned 0 visuals for 0000000001deadb0
OpenGL Warning: Retry with 0x8002 returned 0 visuals
Could not initialize GLX
0  libLLVM-3.6.so.1           0x00007fe411df7912 llvm::sys::PrintStackTrace(_IO_FILE*) + 34
1  libLLVM-3.6.so.1           0x00007fe411df5eb1
2  libpthread.so.0            0x00007fe431190340
3  libc.so.6                  0x00007fe43085ecc9 gsignal + 57
4  libc.so.6                  0x00007fe4308620d8 abort + 328
5  libQt5Core.so.5            0x00007fe43143230e
6  libqxcb-glx-integration.so 0x00007fe4325e52dc
7  libqxcb-glx-integration.so 0x00007fe4325e60f8
8  libqxcb-glx-integration.so 0x00007fe4325e484b
9  libQt5XcbQpa.so.5          0x00007fe429bbcf91 QXcbIntegration::createPlatformOpenGLContext(QOpenGLContext*) const + 49
10 libQt5Gui.so.5             0x00007fe4303e721d QOpenGLContext::create() + 45
11 libUtils.so.1              0x00007fe43205afc5 Utils::HostOsInfo::canCreateOpenGLContext(QString*) + 165
12 libQmlDesigner.so          0x00007fe40a098f2c QmlDesigner::QmlDesignerPlugin::initialize(QStringList const&, QString*) + 28
13 libExtensionSystem.so.1    0x00007fe4323111e6 ExtensionSystem::Internal::PluginSpecPrivate::initializePlugin() + 246
14 libExtensionSystem.so.1    0x00007fe43230920c ExtensionSystem::Internal::PluginManagerPrivate::loadPlugin(ExtensionSystem::PluginSpec*, ExtensionSystem::PluginSpec::State) + 828
15 libExtensionSystem.so.1    0x00007fe43230d070 ExtensionSystem::Internal::PluginManagerPrivate::loadPlugins() + 256
16 qtcreator                  0x0000000000409a31
17 libc.so.6                  0x00007fe430849ec5 __libc_start_main + 245
18 qtcreator                  0x0000000000409db2
Аварийный останов

Существует какая-то проблема в реализации поддержки OpenGL в гостевых расширениях VirtualBox. В результате это аффектит два плагина: QmlDesigner и QmlProfiler.

В качестве work-around можно не загружать эти плагины (для меня не критично, но для кого-то может оказаться - очень, но тогда добивайтесь более действенного результата сами). Для этого сначала запустить Qt Creator так:

qtcreator-git -noload QmlDesigner -noload QmlProfiler

после чего выключить загрузку этих плагинов в настройках и запускать QtC обычным образом.

// Новые интересные фичи в Qt Creator Master

Для начала: создал репозиторий PPA, куда буду с разной периодичностью бубликовать снапшот QtC из master-бранча. Кроме того, туда же поместил свой плагин CMakeProjectManager2:
https://launchpad.net/~adrozdoff/+archive/ubuntu/qtcreator-git

Установка:

sudo add-apt-repository ppa:adrozdoff/qtcreator-git
sudo apt-get update
sudo apt-get install qtcrator-git qtcreator-git-plugin-cmake2

Для разработчиков плагинов будет полезен пакет qtcretor-git-dev который ставит в /usr/src/qtcreator-git заголовочники и .pri файлы. Пример использования можно поглядеть в спеках отстройки qtcreator-git-plugin-cmake2:

sudo apt-get install qtcrator-git-dev

Ну а теперь фичи.

Кодовая модель

Уже давно появились приятные дополнительные инструменты вроде «Reparse Externally Changed Files» (удобно при наличии внешних генераторов, тот же протобуф) и «Inspect C++ Code Model…», позволяющая понять, что не так при парсинге и отослать более вменяемый отчёт.

Сравнительно же недавно появилась возможность задавать индивидуально для каждого файла дополнительные директивы препроцессора: «Additional Preprocessor Directives…». Очень удобная штука, когда парсинг почему-то затыкается (например какая-то опция может подхватываться из переданных CXXFLAGS) - обозначил и вперёд! Или правишь код на одной системы, а нужно исправить блок для другой: определяем «#define WIN32» и пробуем что получилось. Для быстрого доступа служит иконка с «#» в верхнем правом углу рядом с номером строки и колонки.

GUI

Тоже появилось сравнительно давно, многие просили: открытие в новом окне (Window → Open in New Window иди Ctrl+E,4). Удобно при навигации и рефакторинге при наличии нескольких мониторов.

CMake

Про свой плагин не пишу. В мейнстриме появилась возможность задавать не один фиксированный cmake, а разные для разных наборов инструментов (Kits).

External Tools

Данный функционал оооочень давно в QtC, но очень многие его не замечают. А ведь очень много проблем можно решить с его помощью. К примеру: править исходники локально, а потом засабмитить на билд-сервер и выполнить на нём процедуру билда. Если чуть поднапрячься, можно и парсинг ошибок разобрать.

Либо вызвать cppcheck на текущий проект, либо… что в голову взбредёт.

Чего хотелось бы, но нет

- Настройки лицензии (шапки для новых файлов) индивидуально для проекта. - Замена отдельных частей Kit индивидуально для проекта - Более гибкая система настройки устройств для Baremetal: сейчас устройство намертво привязано к Kit со своими настройками доступа к, примеру, OpenOCD и gdb. Вот для Cypress SDK нужно как минимум два: для основной прошивки и для Bootloader.

Засим пока всё.

// Внешний вид шрифтов Qt 5.4.1 в LinuxMint/Cinnamon

В Qt 5.4.1 вернули рендеринг шрифтов в Linux а-ля Qt4. Поставил Qt 5.4.1 (отсюда), собрал Qt Creator из git, запустил, шрифты явно не похожи на то, что используется в системе (Linux Mint 17.x/Cinnamon).

Поиск в инетрнетах привёл на ссылку: https://forum.manjaro.org/index.php?topic=14931.0

Если коротко, то установка переменной окружения:

export XDG_CURRENT_DESKTOP=GNOME

волшебным образом преобразует внешний вид приложения. Будет хорошим тоном прописать это в qtcreator.sh или аналогичный враппер для приложения, а не делать общесистемным параметром.

По умолчанию этот параметр для Cinnamon содержит: X-Cinnamon, что соответствует спекам XDG.

Стоить отметить, что после перезагрузки всё стало на свои места и без этого параметра. Но если проблема будет проглядываться - попробуйте такой вариант.

// CMakeProjectManager2: улучшения в диалоге конфигурирования (2)

Очередная порция изменений в развитие CMakeProjectManager2: улучшения в диалоге конфигурирования:

  1. Исправлен креш при создании конфигурации, при указании тулчейн файла, когда он лежит в директории исходников. Изредка креш проявлялся при переключении уже готовых конфигураций.
  2. Добавлена кнопка обзора для выбора тулчейн-файла. По умолчанию открывается в дереве исходников проекта, если тулчейн выбран в другом месте (к примеру, у вас коллекция тулчейнов независимых от проекта), то открывается в месте, где лежит тулчейн. В быстрые закладки добавляется ссылка на дерево проекта, что бы было можно быстро туда перейти. Закрыта задача #2.
  3. Добавлена возможность очищать кеш при запуске CMake, что бывает необходимо при изменении некоторых параметров, одним из них, к примеру, является CMAKE_TOOLCHAIN_FILE, другим популярным (особенно при выборе разных версий Qt) является CMAKE_PREFIX_PATH. Изменение этих параметров без удаления кеша ни к чему не приводит, поэтому добавил параметр «Reset cache on CMake run». Стоит отметить, что при изменении тулчейна это действие обязательно, поэтому, если тулчейн поменялся, но этот параметр не включен, то будет выдаваться вопрос: «You change toolchain. This action requires CMake cache reset. Continue?». Закрыл задачу #1
  4. При встроенном редактировании тулчейна теперь открывается не пустое окно, если ничего не было, а пример тулчейна для Mingw. Можно оставлять только опции, задающие компилятор, что бы выбрать нужную версию оно в системе, если их стоит несколько.
  5. Небольшое косметическое изменение: кнопка Edit стала поменьше.

// CMakeProjectManager2: улучшения в диалоге конфигурирования

Для тех, кто не в курсе, что такое CMakeProjectManager2, вот ссылка на статью 2012 года: CMakeProjectManager2 - последние изменения. Собственно тогда и были сделаны последние крупные изменения, которых мне хватало до сегодняшнего дня. В остальном была работа по синхронизации кодовой базы с апстримом, что бы плагин продолжал собираться и радовать глаз. Код располагается на GitHub: https://github.com/h4tr3d/cmakeprojectmanager2. Инструкции по сборке приложены в README.txt

Итак, как бы ни хотелось, но времени заниматься реализацией TODO листа нет и не предвидится (суть списка: сделать парсер и обойтись без генерации файла для CodeBlocks, а всё остальное уже на это опирается). Но есть и другие задачи. В частности, в сегодняшнем обновлении несколько изменён вид окна «Run CMake» мастера, а именно:

  1. Добавлена возможность выбирать тип сборки
  2. Добавлена возможность назначать тулчейн (это файл с настройками среды сборки, особенно актуально для кросс-сборки: http://www.vtk.org/Wiki/CMake_Cross_Compiling#The_toolchain_file).

По сути, эти параметры служат для задания в более дружественной формы параметров для CMake: -DCMAKE_BUILD_TYPE= и -DCMAKE_TOOLCHAIN_FILE= соответственно.

Про тулчейн немного подробнее. Изначально планировалось три способа его задания:

  1. Автоматическое конструирование на основе Qt Creator Kit
  2. Ручное задание файла (пока сделано без возможности открытия диалога поиска, только ручной ввод: issue #2)
  3. «Инлайн» тулчейн: редактирует во встроенном редакторе, при запуске контент сохраняется в директории отстройки под именем QtCreator-toolchain-override.cmake

Пока вариант на основе Qt Creator Kit выключен. Будет время - доделаю.

Плюс есть нюанс: согласно документации CMake, смена тулчейна возможно только на новой конфигурации либо на полной очистке текущей (удаления CMakeFiles и CMakeCache.txt), поэтому, если замечены изменения настроек тулчейна, производится полное переконфигурирование без использования кеша. Планирую добавить диалог с предупреждением (issue #1).

Стоит отметить, что пользовательский ввод параметров сохранён, более того, определяется, если параметр уже задан, то будет использоваться пользовательский.

Ну и картинка:

// Qt Creator и C++11

Небольшая заметка о том, как форсировать поддержку C++11 в парсере для различных билд-систем. Заметки касаются master-снапшота QTC (брать тут: http://download.qt-project.org/snapshots/qtcreator/master/latest/ или собирать из исходников).

Нужно отметить, что синтаксис нового стандарта поддерживается сейчас по-умолчанию. Проблемы касаются правильного определения констант компилятора, подключения частей стандартной библиотеки и т.п.

// Qt Creator и perforce

UPD: Изменения, на основе этого патча, заинтегрированы в Qt Creator другим человеком. От меня была только ревью кода.

Во времена, когда проект Qt и все смежные тулы были под управлением VCS Perforce, появился плагин для Qt Creator'а для интеграции работы с этой системой, да вот только в своём развитии он конкретно и застрял в тех стародавних временах. Причина проста: проект переехал на Git, а перфорс нафиг никому не сдался. Как результат: плагин есть, и даже собирается, да вот только считать его хоть малость рабочим… не получается.

Как минимум одна особенность (читать бага), перечёркивает весь (и без того убогий) функционал: он не может определить директорию верхнего уровня для файлов.

При этом, это вроде как работает, когда забиваешь настройки, да вот только они там и поселяются навсегда, прибитые к одному серверу и одному пользователю. А кроме того, за директорию верхнего уровня принимается текущая рабочая директория (читать: та, из которой запущен Qt Creator).

Немного повозившись, родился патч:

Perforce-VCS-plugin-fix-top-level-directory-detection.patch
From c111318c78acb2b7ddd41533de3577fa7dc64b1d Mon Sep 17 00:00:00 2001
From: Alexander Drozdov <adrozdoff@gmail.com>
Date: Tue, 10 Sep 2013 17:33:55 +1100
Subject: [PATCH] Perforce VCS plugin: fix top-level directory detection
 
---
 src/plugins/perforce/perforcechecker.cpp        | 21 ++++++++-
 src/plugins/perforce/perforcechecker.h          |  5 ++-
 src/plugins/perforce/perforceconstants.h        |  2 +-
 src/plugins/perforce/perforceplugin.cpp         | 57 ++++++++++++++++++++-----
 src/plugins/perforce/perforceplugin.h           | 16 ++++++-
 src/plugins/perforce/perforceversioncontrol.cpp |  2 +-
 6 files changed, 86 insertions(+), 17 deletions(-)
 
diff --git a/src/plugins/perforce/perforcechecker.cpp b/src/plugins/perforce/perforcechecker.cpp
index 5971c00..b9b0395 100644
--- a/src/plugins/perforce/perforcechecker.cpp
+++ b/src/plugins/perforce/perforcechecker.cpp
@@ -28,6 +28,7 @@
 ****************************************************************************/
 
 #include "perforcechecker.h"
+#include "perforceconstants.h"
 
 #include <utils/qtcassert.h>
 #include <utils/synchronousprocess.h>
@@ -78,7 +79,8 @@ void PerforceChecker::resetOverrideCursor()
 
 void PerforceChecker::start(const QString &binary,
                             const QStringList &basicArgs,
-                            int timeoutMS)
+                            int timeoutMS,
+                            const QString &workingDirectory)
 {
     if (isRunning()) {
         emitFailed(QLatin1String("Internal error: process still running"));
@@ -91,6 +93,15 @@ void PerforceChecker::start(const QString &binary,
     m_binary = binary;
     QStringList args = basicArgs;
     args << QLatin1String("client") << QLatin1String("-o");
+
+    if (Perforce::Constants::debug)
+        qDebug() << "PerforceChecker::start: [" << workingDirectory << "]" << m_binary << args;
+
+    if (!workingDirectory.isEmpty())
+    {
+        m_process.setWorkingDirectory(workingDirectory);
+    }
+
     m_process.start(m_binary, args);
     m_process.closeWriteChannel();
     // Timeout handling
@@ -105,6 +116,11 @@ void PerforceChecker::start(const QString &binary,
     }
 }
 
+bool PerforceChecker::waitForFinished(int msec)
+{
+    return m_process.waitForFinished(msec);
+}
+
 void PerforceChecker::slotTimeOut()
 {
     if (!isRunning())
@@ -169,6 +185,9 @@ static inline QString clientRootFromOutput(const QString &in)
 
 void PerforceChecker::parseOutput(const QString &response)
 {
+    if (Perforce::Constants::debug)
+        qDebug() << "PerforceChecker::parseOutput: " << response;
+
     if (!response.contains(QLatin1String("View:")) && !response.contains(QLatin1String("//depot/"))) {
         emitFailed(tr("The client does not seem to contain any mapped files."));
         return;
diff --git a/src/plugins/perforce/perforcechecker.h b/src/plugins/perforce/perforcechecker.h
index e466250..aada848 100644
--- a/src/plugins/perforce/perforcechecker.h
+++ b/src/plugins/perforce/perforcechecker.h
@@ -50,10 +50,13 @@ public:
 public slots:
     void start(const QString &binary,
                const QStringList &basicArgs = QStringList(),
-               int timeoutMS = -1);
+               int timeoutMS = -1,
+               const QString &workingDirectory = QString());
 
     bool isRunning() const;
 
+    bool waitForFinished(int msec = -1);
+
     bool useOverideCursor() const;
     void setUseOverideCursor(bool v);
 
diff --git a/src/plugins/perforce/perforceconstants.h b/src/plugins/perforce/perforceconstants.h
index 316fc24..1f8c2a1 100644
--- a/src/plugins/perforce/perforceconstants.h
+++ b/src/plugins/perforce/perforceconstants.h
@@ -55,7 +55,7 @@ const char SUBMIT_CURRENT[] = "Perforce.SubmitCurrentLog";
 const char DIFF_SELECTED[] = "Perforce.DiffSelectedFilesInLog";
 const char SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.p4.submit";
 
-enum { debug = 0 };
+enum { debug = 1 };
 
 } // Constants
 } // Perforce
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index 8fd5957..1f9480d 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -824,7 +824,11 @@ bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLeve
     const bool rc = managesDirectoryFstat(directory);
     if (topLevel) {
         if (rc)
+        {
+            if (Perforce::Constants::debug)
+                qDebug() << "Top Level: " << m_settings.topLevelSymLinkTarget();
             *topLevel = m_settings.topLevelSymLinkTarget();
+        }
         else
             topLevel->clear();
     }
@@ -834,28 +838,48 @@ bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLeve
 bool PerforcePlugin::managesDirectoryFstat(const QString &directory)
 {
     if (!m_settings.isValid())
+    {
+        if (Perforce::Constants::debug)
+            qDebug() << "Settings invalid";
         return false;
+    }
     // Cached?
     const ManagedDirectoryCache::const_iterator cit = m_managedDirectoryCache.constFind(directory);
     if (cit != m_managedDirectoryCache.constEnd())
-        return cit.value();
+    {
+        const DirectoryCacheEntry &entry = cit.value();
+        if (Perforce::Constants::debug)
+            qDebug() << "Directory: " << directory << " is cached and managed: " << entry.isManaged;
+
+        setNewTopLevel(entry.topLevel);
+
+        return entry.isManaged;
+    }
     // Determine value and insert into cache
     bool managed = false;
     do {
         // Quick check: Must be at or below top level and not "../../other_path"
         const QStringList relativeDirArgs = m_settings.relativeToTopLevelArguments(directory);
         if (!relativeDirArgs.empty() && relativeDirArgs.front().startsWith(QLatin1String("..")))
-            break;
+        {
+            if (Perforce::Constants::debug)
+                qDebug() << "Directory " << directory << " is a relative path to current top level dir [" << relativeDirArgs << "], try find new top level.";
+            getTopLevel(directory, true);
+        }
         // Is it actually managed by perforce?
         QStringList args;
         args << QLatin1String("fstat") << QLatin1String("-m1") << perforceRelativeFileArguments(relativeDirArgs);
         const PerforceResponse result = runP4Cmd(m_settings.topLevel(), args,
                                                  RunFullySynchronous);
+
+        if (Perforce::Constants::debug)
+            qDebug() << "Perforce result:\n" << result.stdOut << "\n---\n" << result.stdErr << "\n---\n" << result.message;
+
         managed = result.stdOut.contains(QLatin1String("depotFile"))
                   || result.stdErr.contains(QLatin1String("... - no such file(s)"));
     } while (false);
 
-    m_managedDirectoryCache.insert(directory, managed);
+    m_managedDirectoryCache.insert(directory, DirectoryCacheEntry(managed, m_settings.topLevel()));
     return managed;
 }
 
@@ -1489,12 +1513,7 @@ PerforceVersionControl *PerforcePlugin::perforceVersionControl() const
 
 void PerforcePlugin::slotTopLevelFound(const QString &t)
 {
-    m_settings.setTopLevel(t);
-    const QString msg = tr("Perforce repository: %1").
-                        arg(QDir::toNativeSeparators(t));
-    VcsBase::VcsBaseOutputWindow::instance()->appendSilently(msg);
-    if (Perforce::Constants::debug)
-        qDebug() << "P4: " << t;
+    setNewTopLevel(t);
 }
 
 void PerforcePlugin::slotTopLevelFailed(const QString &errorMessage)
@@ -1504,7 +1523,7 @@ void PerforcePlugin::slotTopLevelFailed(const QString &errorMessage)
         qDebug() << errorMessage;
 }
 
-void PerforcePlugin::getTopLevel()
+void PerforcePlugin::getTopLevel(const QString &workingDirectory, bool isSync)
 {
     // Run a new checker
     if (m_settings.p4BinaryPath().isEmpty())
@@ -1514,7 +1533,23 @@ void PerforcePlugin::getTopLevel()
     connect(checker, SIGNAL(failed(QString)), checker, SLOT(deleteLater()));
     connect(checker, SIGNAL(succeeded(QString)), this, SLOT(slotTopLevelFound(QString)));
     connect(checker, SIGNAL(succeeded(QString)),checker, SLOT(deleteLater()));
-    checker->start(m_settings.p4BinaryPath(), m_settings.commonP4Arguments(QString()), 30000);
+    checker->start(m_settings.p4BinaryPath(), m_settings.commonP4Arguments(QString()), 30000, workingDirectory);
+
+    if (isSync)
+        checker->waitForFinished();
+}
+
+void PerforcePlugin::setNewTopLevel(const QString &newTopLevel)
+{
+    if (m_settings.topLevel() != newTopLevel)
+    {
+        m_settings.setTopLevel(newTopLevel);
+        const QString msg = tr("Perforce repository: %1").
+                            arg(QDir::toNativeSeparators(newTopLevel));
+        VcsBase::VcsBaseOutputWindow::instance()->appendSilently(msg);
+        if (Perforce::Constants::debug)
+            qDebug() << "P4: " << newTopLevel;
+    }
 }
 
 #ifdef WITH_TESTS
diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h
index 8eeb1e2..8e08500 100644
--- a/src/plugins/perforce/perforceplugin.h
+++ b/src/plugins/perforce/perforceplugin.h
@@ -146,7 +146,18 @@ protected:
 
 
 private:
-    typedef QHash<QString, bool> ManagedDirectoryCache;
+    struct DirectoryCacheEntry
+    {
+        DirectoryCacheEntry(bool isManaged, const QString &topLevel)
+            : isManaged(isManaged),
+              topLevel(topLevel)
+        {}
+
+        bool    isManaged;
+        QString topLevel;
+    };
+
+    typedef QHash<QString, DirectoryCacheEntry> ManagedDirectoryCache;
 
     Core::IEditor *showOutputInEditor(const QString& title, const QString output,
                                       int editorType, const QString &source,
@@ -193,7 +204,8 @@ private:
     bool isCommitEditorOpen() const;
     QSharedPointer<Utils::TempFileSaver> createTemporaryArgumentFile(const QStringList &extraArgs,
                                                                      QString *errorString) const;
-    void getTopLevel();
+    void getTopLevel(const QString &workingDirectory = QString(), bool isSync = false);
+    void setNewTopLevel(const QString &newTopLevel);
     QString pendingChangesData();
 
     void updateCheckout(const QString &workingDir = QString(),
diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp
index 19303a6..ad4f35b 100644
--- a/src/plugins/perforce/perforceversioncontrol.cpp
+++ b/src/plugins/perforce/perforceversioncontrol.cpp
@@ -178,7 +178,7 @@ bool PerforceVersionControl::managesDirectory(const QString &directory, QString
         QDebug nsp = qDebug().nospace();
         nsp << "managesDirectory" << directory << rc;
         if (topLevel)
-            nsp << topLevel;
+            nsp << *topLevel;
     }
     return rc;
 }
-- 
1.8.3.msysgit.0

// Qt Creator: C/C++ parser and "pre-included" headers

Некоторые компиляторы (если не все) поддерживают такую функциональность: через параметр командной строки можно указать файл или файлы, которые будут подключаться автоматически к каждому обрабатываемому файлу. У GCC это опция -include <file>.

В некоторых проектах используют эту особенность, как результат - явного включения файла нет, а парсер в Qt Creator'е не видит части объявлений.

Решения единого нет: для каждого типа проекта (qmake, cmake, generic & etc), теоретически, оно своё (если есть вообще). В моём случае используется Generic project, для него решение существует.

И решение достаточно простое: Generic Project Mananger использует несколько файлов для хранения настроек проекта:

  • ProjectName.creator - головной файл, по сути не содержит никакой полезной информации
  • ProjectName.files - список файлов проекта (может содержать и не исходники)
  • ProjectName.includes - пути поиска заголовочных файлов
  • ProjectName.config - как гласит комментарий внутри этого файла: «ADD PREDEFINED MACROS HERE!»

Вот последний файл самый интересный. Комментарий немного наводит в ступор и мы решаем, что тут можно только писать код вида:

#define MACRO some_value

На самом деле, парсером этот файл воспринимается как обычный заголовочный файл и информация из него используется для парсинга всего остального. Чуете профит? Если нет, подсказываю: по сути это и есть наш «pre-included» header для парсера! И писать в нём мы можем всё, что понимается препроцессором (если быть точнее: парсером скеатора). Т.е. мы можем сделать такое:

#include "includes/pre-included.h"

И всё, что объявлено в нашем pre-included.h станет доступно парсеру при обработке каждого файла. Дело сделано!

Было бы любопытно узнать как такое можно реализовать для Qmake и Cmake проектов.

PS вопрос этот я поднял в списке рассылки Qt Creator'а, пока ждал ответ, в голову пришла идея с #include, в результате список только подтвердил моё изыскание :)

UPDATE: рано обрадовался, внезапно, решение отказалось работать. Имхо, причина в несовсем корректном использовании .config файла.

// Qt Creator: "the debugger could not load the debugging helper library" на Windows XP

Решение подсказывают тут и тут. Если коротко: нужен GDB скомпилированный с поддержкой Python и Qt Creator на это расчитывает. По первой ссылке его рекомендуют брать в QtSDK, но качать его сильно накладно. Недолгое гугление привело на эту ссылку: http://origin.releases.qt-project.org/gdb/windows-xp/.

Устанавливатся просто: скачиваем, распаковываем, в настройках китов в Qt Creator (Tool → Options… → Build & Run → Kits) выбираем новый отладчик (для MinGW) и живём.

PS другие версии: http://origin.releases.qt-project.org/gdb/ PPS более корректные ссылки: http://builds.qt-project.org/job/gdb-windowsxp/ и http://builds.qt-project.org/job/gdb-windows/

// CMakeProjectManager2 - последние изменения

Как я уже писал, вяло пилю модифицированную версию плагина CMakeProjectManager для Qt Creator'а. Там же указаны причины, вынудившие меня на это. Но разговор, не про это, а про то, что с момента прошлого поста было сделано.

Итак:

  1. Для каждого профиля сборки сохраняются введённые параметры для CMake, так что, выбрав в следующий раз «Run CMake» не нужно вспоминать, с какими параметрами вы его запускали и легче управлять профилями сборки. Вкупе с последней фичей из апстрима: сохранения глобальной истории параметров для CMake, получается достаточно мощный механизм.
  2. Используя вышеприведённую информацию, появилась возможность при модификации дерева исходников (добавление, удаление, переименование) в фоновом режиме запускать обновление CBP файла и дерева сборки, что особо актуально при использовании глоббинга.
  3. По сравнению с первым вариантом, получилось значительно сократить расходование памяти при использовании плагина, особенно когда в дереве проекта много вспомогательных модулей, временного C/C++ кода.

Кодовая база периодически синхронизируется с GIT версией Qt Creator. Если кто-то будет делать клоны для стабильных релизов, просьба отписываться с информацией об оных.

HINT:
Так как, при добавлении, удалении или переименовывании файла, не осуществляется модификация CMakeLists.txt, то нужно вносить изменения самому, либо использовать globbing:

# UTILS
file(GLOB_RECURSE UTIL_SOURCES "../util/*.cpp")
file(GLOB_RECURSE UTIL_HEADERS "../util/*.h" "../util/*.hpp")

На полноценный парсер пока времени нет (хотя уже адоптирован на чистый C++/Qt оный из KDevelop), к тому же, в списке рассылки Qt Creator проскакивала новость, что одна команда разрабатывает полноценный плагин (тыц). Зная это, не хочется делать бесполезную работу, при том, что текущий функционал уже вполне удовлетворяет.

// Qt Creator & doxygen

Сегодня пролетело в рассылке:

Hi,

Qt Creator master has recently got a feature, which has been requested a
few times, for Doxygen blocks generation. If you type /** or /*! and
press enter/return before a declaration, you'll get something like:
/*!
 *
 * \param data
 * \param options
 * \return
 */
QString generate(const Data &data, const Options &options);


You can also opt whether or not you want an additional \brief command
and leading asterisks for comment continuation (when breaking lines).
The options are in Text Editor→Completion→Documentation Comments.

Notice that the style is identified from the comment beginning. If you
start it with /*! then the Qt style is used. Otherwise, the Java style
with the @ prefix for the commands is picked.

Cheers,
Leandro

Проверил - работает, удобно :)

// Qt Creator Plugins Docs

По мотивам рассылки qt-creator@qt.nokia.com, подборка документации по написанию плагинов для Qt Creator'а:

Для примера можно поглядеть различные плагины на гиториусе: https://gitorious.org/search?q=qt+creator+plugins

Если будет появляться новая информация, буду дополнять пост.

// Qt Creator и CMake - продолжение

Некоторое время я поднимал тему связки Qt Creator и CMake, тогда всё показалось не очень хорошо.

В общем, собрался и сделал несколько лучше: малость допилил плагин CMakeProjectManager, реализовав следующие фичи:

  • Дерево проекта берётся не из .cbp файла, а сканированием дерева проекта. Как вариант может оказаться медленно на больших проектах, с другой стороны, релоадинг дерева происходит не каждый раз, а при смене CMakeLists.txt или при добавлении, удалении, переименовывании файлов (этого, кстати, в базовом плагине нет)
  • Теперь можно создавать новые файлы в дереве проектов непосредственно из Qt Creator'а
  • Появилась возможность переименовывать файлы
  • Появилась возможность удалять файлы с диска

Изменения оформлены в виде отдельного плагина (основано на GIT версии Qt Creator, 2.2.81) - CMakeProjectManager2 доступного на Gitorious: http://gitorious.org/hatred-qt-creator-plugins/cmakeprojectmanager2, как устанавливать - читать README.txt, написано моим дряным английским, но, в общем, должно быть понятно.

Кроме того, в основное дерево Qt Creator я подал мёрж-реквест: http://qt.gitorious.org/qt-creator/qt-creator/merge_requests/280

Кроме того, случайно наткнулся мастера новых проектов на CMake: http://apachelog.wordpress.com/2010/09/27/qt-creator-cmake-wizards/ устанавливаются просто.

// Qt Creator и CMake

Пакость: Qt Creator умеет импортировать CMake проекты, проблема в том, что в дереве далеко не все файлы отображаются.

Причина: делается этот импорт через откровенную задницу: вызывает cmake с генератором «-G'CodeBlocks - Unix Makefiles'», генерируя тем самым XML-файл проекта формата CodeBlocks. Но тут накладывается вторая задница: сам генератор обрабатывает файлы только для таргетов: executable, static_library, shared_library, module_library, всё остальное он забывает запихнуть в результирующий '.cbp'.

Решение

Для начала накладываем патч на CMake и пересобираем его:

cmExtraCodeBlocksGenerator.cxx.diff
--- cmExtraCodeBlocksGenerator.cxx.orig 2011-03-15 14:28:30.692010962 +1000
+++ cmExtraCodeBlocksGenerator.cxx      2011-03-15 15:01:05.612566928 +1000
@@ -410,12 +410,14 @@
     for (cmTargets::iterator ti = targets.begin();
          ti != targets.end(); ti++)
       {
+      //std::cout << "Type: " << ti->second.GetType() << std::endl;
       switch(ti->second.GetType())
         {
         case cmTarget::EXECUTABLE:
         case cmTarget::STATIC_LIBRARY:
         case cmTarget::SHARED_LIBRARY:
         case cmTarget::MODULE_LIBRARY:
+        case cmTarget::UTILITY:
           {
           const std::vector<cmSourceFile*>&sources=ti->second.GetSourceFiles();
           for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();

Потом, для тех файлов, которые хотим видеть в дереве создаём фейковый таргет, примерно так:

set(script-files
    process-filelist.sh
)
# hack for display in Qt Creator (with patch for CMake)
add_custom_target(scripts true SOURCES ${script-files})

Всё, после регенерации файлы появятся в списке.

Решение костыльное, но время переписывать импортёр CMake в Qt Creator просто нет, будем надеяться, что разработчики обратят на это внимание.

// Пара замечаний

Замечание 1: Qt Creator (одна из последних git сборок)
При включенном отображении Outline в левой панели, при работе появляются значительные тормоза даже на небольших проектах (при моих 1024Мб RAM и Atom 1.6Гц). Решение: выбрать другой режим, тем более что в значительный промежуток времени эта панель вообще не нужна (убирается и вновь показывается при помощи Alt-0), да и есть мощный инструмент Locate (Ctrl-K)

Замечание 2: sshfs и не уходим в sleep
Я активно пользуюсь sshfs для подключения удалённых ресурсов, удобно, быстро, не нужно дополнительных плясок. Недавно стал наблюдать, что система при каких-то условиях перестала засыпать на нетбуке. Опытным путем выяснилось условие: ресурс, примонтированный при помощи sshfs, был отлючен с использованием опции lazy у fusermount (иначе ругался на Resource busy). При этом продолжал висеть процесс sshfs, его убийство после, опять позволяло уводить систему в sleep.

// Qt Creator и Generic Projects

Или: можно ли использовать Qt Creator не для Qt? Если коротко - можно.

Почему он? Потому что оказалось, что он достаточно функционален в мелочах, в частности индексирования кода и автодополнения. Крайне удобен в применении его Locator (что вызывается по стандартному сочетанию клавиш Ctrl+K), применение Локатора вообще сводит использование мыши в Qt Creator к минимуму (что удивительно для среды ориентированной на тулкит Qt4 и создание, в основном, графических приложений). Да, правда обвыкнутся по началу нужно, но потом… Если плагин (http://gitorious.org/creator-plugins), который добавляет функционал по автоматическому созданию реализации функции/метода по его прототипу, так же Я, до того, как узнал про этот плагин, делал свой патч, со схожим функционалом. В общем, все что нужно для удобной работы с проектом (навигация, дополнения, подсветка синтаксиса, отстройка, отладка и т.п.) здесь есть.

Осталось понять, как это добро использовать. Вопросов нет, когда речь идет о родных средствах, типа .pro файла и qmake, а вот при использовании с не qmake/cmake проектами, можно воспользоваться Generic Project, который используют вашу систему сборки, не вмешиваясь в неё. Подробности всегда можно посмотреть на официальном сайте: http://doc.qt.nokia.com/qtcreator-snapshot/creator-project-generic.html

Собственно по ссылке выше и рассказано все, что нужно знать. Тезисно:

  1. используется ваша система сборки, порядок шагов по сборке можно задать в свойствах проекта, по умолчанию, это выполнение команды make all, соответственно там можно задать любую нужную последовательность, которая соответствует вашей системе сборки.
  2. аналогично задаются шаги по очистке проекта.
  3. аналогично задается цель для запуска.
  4. аналогично задаются зависимости между проектами (они должны быть открыты).
  5. при создании указывается директорий вашего проекта, по умолчанию просканируется дерево и добавятся все *.c, *.cpp, *.cc, *.h, *.hpp файлы (как сказано - все известные типы).
  6. после создания проекта и сохранения настроек на диске будет создано несколько файлов:
    • project_name.files - тут список файлов входящих в проект, обычно исходники, в помощь парсеру, можно править вручную, обновляются автоматически, при добавлении файла в проект из самого Creator'а
    • project_name.includes - тут список include директорий, в помощь парсеру, можно править вручную
    • project_name.config - заявляется как обычный C файл, в котором можно записывать ваши макроопределения:
      #define VERSION "0.0.1"

      и т.п.

    • project_name.creator - собственно сам файл проекта, его открывать в Creator'е для загрузки проекта, сейчас никаких настроек больше не содержит.
    • в процессе работы появится файл project_name.creator.user в котором будут сохранены ваши параметры сборки, очистки проекта, информация о запускаемых модулях.

Теперь как это можно использовать? Покажу на примере своего небольшого Makefile:

PROJECT_NAME=empty-test
EXE = $(PROJECT_NAME)
 
CC = gcc
CXX = g++
 
CFLAGS   += -include $(PROJECT_NAME).config
CXXFLAGS += -include $(PROJECT_NAME).config
 
BIN_DIR = bin
 
SRC_CPP = ${shell cat $(PROJECT_NAME).files | grep '.cpp$$'}
SRC_C   = ${shell cat $(PROJECT_NAME).files | grep '.c$$'}
 
OBJ := $(SRC_CPP:.cpp=.o)
OBJ += $(SRC_C:.c=.o)
 
.PHONY : all clean
 
all: $(OBJ)
	$(CXX) -o $(BIN_DIR)/$(EXE) $(LDFLAGS) $(OBJ)
 
%.o : %.cpp
	$(CXX) -c $(LDFLAGS) $(CFLAGS) $(CXXFLAGS) $< -o $@
 
%.o : %.c
	$(CC) -c $(LDFLAGS) $(CFLAGS) $< -o $@
 
clean:
	rm -f $(OBJ) $(BIN_DIR)/$(EXE)

Что тут делается, во первых - автоматически получается список файлов проекта из project_name.files, далее, компилятором автоматически подключается project_name.config, и все объявленные там макросы будут доступны внутри кода.

Естественно тут нет и намека на множественные цели, но проект можно разбивать на подпроекты, их зависимости друг от друга задавать в Creator'е, а самих объединять в рабочие области (workspace). В общем и целом - ограничено все только вашей фантазией, и, по сути, данные действия превращают Qt Creator в продвинутый редактор кода, с базовыми функциями управления проектами, чего часто бывает достаточно.

Да, индексация проекта происходит при его загрузке, что требует времени, поэтому есть лишний повод бить большие проекты на части :)

// Qt Creator и автоматическое создание реализации метода/прототипа

UPD: Информация больше не актуальна, теперь подобные средства кодогенерации встроены в Qt Creator.

Долгое время пользовался для написание программ, с использованием Qt4 интегрированную среду разработки QDevelop, и хотя она почти всем устраивает, за несколькими исключениями:

  • непонятно работает автодополнение
  • на 500 строках и больше начинает ощутимо поддтормаживать