Интеграция IDE Code::Blocks с программным комплексом Altera Quartus II. Часть III.

5. Приближение четвёртое. Сливаемся с местностью.
Главное правило любого диверсанта — необходимо замаскировать результаты своего творчества так, как будто так всё и было. Наша цель — сделать видимость среды, изначально проектировавшейся для разработки на HDL. Что бы убрать всё, что нас выдаёт надо прикрутить честные действия по Build и Run (написать модуль типа Compiller), автодополнение кода для интересующих нас языков (перегрузить модуль CodeCopletion), прикрутить деббагер, симулятор, RTL-вьювер… Да много чего нужно, но мы это делать сейчас не будем — очень много работы придётся сделать, а для некоторых вещей даже потребуется править исходники самого C::B. Вместо этого мы сделаем пару небольших, но полезных хаков.
5.1) Псевдо-компилятор.
Как я уже сказал, писать плугин компилятора, который будет перегружать действия на Build и Run, и делать прочие полезности, мы не будем, но шаги по интеграции квартуса в инфраструктуру сборки C::B мы всё-таки предпримем. Путём нехитрых манипуляций на прошлом этапе мы получили возможность запускать компилятор квартуса по кнопке Build в виде Pre-Build команды. Занятным бонусом к этому является то, что анализатор вывода компилятора применяется не только непосредственно к выводу компилятора, но и к выводу Pre/Post-Build команд, а значит мы можем понюхать выхлопы квартуса.
Лезем в Settings->Compiler and Debugger… и попадаем в диалог настройки компиляторов. Там для каждого компилятора можно подкрутить такие вещи, как глобальные настройки компиляторов, имена и пути исполняемых файлов, настройки отображения состояния в процессе сборки. Но нас интересует немножко другое. Жмём кнопку Copy и в появившемся диалоге вписываем «Quartus». Теперь у нас есть копия конфига компилятора GCC (ну или какого-то другого, не важно), над которой можно издеваться:

Поскольку ничего компилировать мы им не будем, то все эти тонны настроек нас не интересуют. Мы идём на вкладку Other settings и жмём кнопку Advanced options (кстати, обратите внимание на поле Compiller logging — очень рекомендую для всех остальных используемых компиляторов выставлять значение Full command line). Послав нафиг грозное предупреждение мы попадаем в диалог продвинутой настройки. Настройки тут, кстати, действительно продвинутые — можно почти что как угодно издеваться над тулчейном (но увы, лишь в рамках концепции «вызов компилятора на каждый файл исходников», что нам не катит). В частности, это полезно для прикручивания кодогенераторов [16]. Но нам дольше — вкладка «Output parsing». Здесь описывается как нужно трактовать сообщения тулчейна. Выбора не много — обычный текст, замечания, предупреждения и ошибки — но большего, в общем-то, и не нужно.

Сносим все имеющиеся записи и начинаем думать, что нам нужно (кстати, у меня тут возник косяк — часть убитых правил после перезапуска C::B воскресла, но я особо не разбирался, просто забил из пустыми строками, что б не мешались). Quartus генерит ноты, варнинги, критикал варнинги и ерроры. В принципе, совпадает, если объединить варнинги и крит. варнинги — почему бы и нет? Но тут вот какой момент — Quartus все типы сообщений раскидывает по разным спискам, да ещё и умеет группировать и сворачивать многострочные сообщения. В C::B такого нет, он всё будет валить в одно окно Build messages. А нотсов квартус генерит ой как много, фиг что найдёшь потом. В общем, я решил почти все квартусовские ноты объявить простым текстом, который парсер C::B игнорирует.
Погоняв компиляцию пары проектов, я выявил несколько основных форматов сообщений QuartusII. Тип сообщения всегда явным образом указан в самом начале строки (перед ним может быть только символ табуляции для многострочных сообщений), что нам сильно облегчает жизнь. Формат варнингов и ерроров совпадает. Для них после указания типа сообщения может идти пятизначный код ошибки/предупреждения в круглых скобках. Далее, после двоеточия, идёт текст сообщения, в тексте может быть указан файл и строка в формате " at filename(line)", а может и нет. Кроме того, сообщения с указанием места бывают короткие («с пином в файл(строка) случилась байда») и длинные («у Вас байда в файл(строка): Вы налажали, так делать нельзя»), мне почему-то захотелось из длинных выкинуть путь — он всё равно будет отображаться сбоку, в читать удобнее. А ещё в некоторых сообщениях об ошибках в конце зачем-то ещё раз указывается путь к файлу (на этот раз полный) и номер строки — тоже выкинем. В варнингах я пока такого не видел. Критические варнинги, вроде как, бывают только простые, без указания кодов и путей, во всяком случае я других не видел. Нотсы я особо не разглядывал, но мне понравился один тип — где указывается что именно выполняется, вот их то я и решил оставить.
Итак, перед нами форма для забивания правил. Слева список правил, справа описание выбранного. Самое важное здесь — регулярное выражение. RegExp — это такая прикольная шняга, которую лично я умею только писать, но не читать =). Подробнее о регэкспах читаем на педевикии [17]. С полями Description и Type всё ясно, а вот что находится ниже регулярного выражения — это уже интереснее. Для тех, кто не в курсе и ещё не успел проштудировать статью по ссылке выше, поясню. Регулярное выражение — это шаблон, по которому ищется совпадение в некоторой строке. Примера ради, самый примитивный тип шаблонов, которым все пользуются это файловые маски — "*.exe" (совпадает строка в которой сначала любое количество любых символов, а потом идёт подстрока ".exe"). RegExp`ы значительно хитрее — например они позволяют задавать не точное совпадение символов, а равенство одному из группы, указывать количество таких символов, добавлять условия «начало строки», «конец строки» и тд. Кроме того, они позволяет из целевой строки извлечь подстроки, соответствующие определённым условиям (группы). Вот эти группы и используются для разбора входящего сообщения на поля «сообщение», «имя файла», «строка». Поля Additional message 2 и 3 — это просто дополнительные строки, которые склеиваются через пробел с основным полем Message.
Снизу, кстати, есть ещё отладочное поле «Test parsing expressions». Берем строчку из выхлопа квартуса и смотрим — признал ли её C::B, или нет. Жаль только не уточняет, какое именно правило отработало. Ну ладно, пишем правила:
  1. Desc: Error with line long
    Type: Error
    RegExp: \t?([Ee]rror \([0-9]{5}\):[ \t].*) at ([ \t#%$~A-Za-z0-9_:+/\.-]+)\(([0-9]+)\)(: .*) File: .+ Line: [0-9]+
    Msg: 1
    Msg 2: 4
    Filename: 2
    Line: 3
  2. Desc: Error with line
    Type: Error
    RegExp: \t?([Ee]rror \([0-9]{5}\):[ \t].*) at ([ \t#%$~A-Za-z0-9_:+/\.-]+)\(([0-9]+)\)(: .*)
    Msg: 1
    Msg 2: 4
    Filename: 2
    Line: 3
  3. Desc: Error with line long (2)
    Type: Error
    RegExp: \t?([Ee]rror \([0-9]{5}\):[ \t].* at ([ \t#%$~A-Za-z0-9_:+/\.-]+)\(([0-9]+)\).*) File: .+ Line: [0-9]+
    Msg: 1
    Filename: 2
    Line: 3
  4. Desc: Error with line (2)
    Type: Error
    RegExp: \t?([Ee]rror \([0-9]{5}\):[ \t].* at ([ \t#%$~A-Za-z0-9_:+/\.-]+)\(([0-9]+)\).*)
    Msg: 1
    Filename: 2
    Line: 3
  5. Desc: General error
    Type: Error
    RegExp: \t?([Ee]rror:[ \t].*)
    Msg: 1
  6. Desc: General error (2)
    Type: Error
    RegExp: \t?([Ee]rror \([0-9]{5}\):[ \t].*)
    Msg: 1
  7. Desc: Critical warning
    Type: Warning
    RegExp: \t?([Cc]ritical [Ww]arning:[ \t].*)
    Msg: 1
  8. Desc: Warning with line
    Type: Warning
    RegExp: \t?([Ww]arning \([0-9]{5}\):[ \t].*) at ([ \t#%$~A-Za-z0-9_:+/\.-]+)\(([0-9]+)\)(: .*)
    Msg: 1
    Msg 2: 4
    Filename: 2
    Line: 3
  9. Desc: Warning with line (2)
    Type: Warning
    RegExp: \t?([Ww]arning \([0-9]{5}\):[ \t].* at ([ \t#%$~A-Za-z0-9_:+/\.-]+)\(([0-9]+)\).*)
    Msg: 1
    Filename: 2
    Line: 3
  10. Desc: General warning
    Type: Warning
    RegExp: \t?([Ww]arning:[ \t].*)
    Msg: 1
  11. Desc: General warning (2)
    Type: Warning
    RegExp: \t?([Ww]arning \([0-9]{5}\):[ \t].*)
    Msg: 1
  12. Desc: Info Command
    Type: Info
    RegExp: ([Ii]nfo: [Cc]ommand:[ \t].*)
    Msg: 1
  13. Desc: Info
    Type: Normal
    RegExp: \t?([Ii]nfo:[ \t].*)
    Msg: 1
  14. Desc: Unmatched
    Type: Error
    RegExp: (.*)
    Msg: 1
Последнее правило нужно что бы не пропустить сообщения с неучтённым форматом — вдруг что-то новенькое появится.
Всё вбили? Ну и зря, в прилагаемом архиве есть кусок конфига C::B, который надо просто вставить в конфиг C::B (default.conf). Главное — найти, в какое место =).
Теперь надо назначить в свойствах проекта только что созданный компилятор и можно собирать.
Разбор вывода компилятора
Для использования компилятора «Quartus» в новых проектах поправим наш визард, добавив одну страничку в функцию BeginWizard():

        Wizard.AddCompilerPage(_T(""), _T("quartus"), true, false);

Теперь при создании нового проета будет отображаться дополнительная преддефайненная страница выбора компилятора, в которой список компиляторов будет ограничиваться одним нашим квартусом. Кстати, если там написать GCC (точнее тот компиллер, из которого Вы делали Quartus), то в списке будет выбор из gcc и quartus — то есть сам компилятор и его модификации. Удобно когда нужно передефайнить какие-то действия или параметры компилятора.

5.2) Виртуальные каталоги.
В дереве проектов C::B сортирует файлы по типам — заголовки, исходники, ресурсы и тд. Увы, о наших HDL`ах он, почему-то, ничего не знает и они все попадают в категорию Others, где смешиваются с добавленными в визарде файлами проекта квартуса и отчётов компиляции. Исправить это упущение довольна просто — идём в конфиг C::B (default.conf) и ищем там секцию project_manager->file_groups и добавляем в неё следующий код:

			<group8>
				<NAME>
					<str>
						<![CDATA[HDL Sources]]>
					</str>
				</NAME>
				<MASK>
					<str>
						<![CDATA[*.verilog;*.vlg;*.v;*.sv;*.vhd;*.vhdl;*.tdf;*.sopc;*.sopcinfo;*.cmp;*.qsys;*.smf;*.qxp;*.ptf;*.bdf;*.gdf;*.mdl;*.edn;*.edif;*.edf;*.vqm;]]>
					</str>
				</MASK>
			</group8>

(номер группы выбираем по месту).
Теперь мухи отдельно, котлеты отдельно:


5.3) Недо-плугин.
Время от времени приходится добавлять в проект новые файлы, а иногда — удалять старые. Писать визард нового файла для HDL я смысла не вижу, а вот иметь возможность добавить файлы из проекта C::B в проект Quartus`а без его запуска удобно. Реализовать такую функциональность легко с помощью скриптового плугина.
Что такое скриптовый плугин? На самом деле, это просто такой скрипт, в котором реализован объект, наследуемый от класса cbScriptPlugin. По сути, настоящим плагином он на самом деле не является, то есть не фигурирует в плагин менеджере, не значится в Help->Plugins, имеет другую схему загрузки и так далее. Чего же у него общего с номальными плугинами? Общее у них только одно — возможность интегрироваться в главное меню и в контекстное меню. Вот этим мы и воспользуемся — добавим в контекстное меню дерева проектов пункт «добавить файл в проект квартуса», если он ещё не добавлен и «удалить файл из проекта», если уже добавлен. Проверять наличие/отсутствие будем просматривая файл проекта, и добавлять/удалять — редактируя его.

// Script plugins must extend cbScriptPlugin
class QuartusPlugin extends cbScriptPlugin //объявляем наш объект типа плугин
{
    prj_filename = ::wxString(); //временные переменные
    src_filename = ::wxString(); //для сохранения информации
    src_file_ext = ::wxString(); //между созданием меню
    act_add_file = true;		//и выховом пункта
    // mandatory to setup the plugin's info
    constructor()
    { // информация о плугине; видимо есть планы в будущем её использовать =)
        info.name = _T("QuartusPlugin");
        info.title = _T("Plugin for managing files in the Quartus II project");
        info.version = _T("0.1a");
        info.license = _T("BSD");
    }

    function DetectEol(str) //вспомогательная функция для определения linefeed`ов в файле
    {
        local eol = _T("\r\n");
        if(str.Find(eol) == -1)
            if(str.Find(_T("\r")) != -1)
                eol = _T("\r");
            else
                eol = _T("\n");
        return eol;
    }

    // optional to create context menu entries
    function GetModuleMenu(who, data) //пользователь запросил контекстное меню - генерим наш пункт
    {
        local entries = ::wxArrayString();

        if (who == ::mtProjectManager && data.GetKind() == ::ftdkFile) //проверяем, что это контекстное меню для файла в менеджере проектов
        {
            local project = data.GetProject(); //получаем проект, к которому принадлежит файл, для которого генерится контекстное меню
            local target = project.GetBuildTarget(0); //получаем первый таргет в этом проекте
            local compiler_name = target.GetCompilerID(); //и считываем ID компилятора
            compiler_name = compiler_name.Lower();
            if(compiler_name.Matches(_T("quartus"))) //если у проекта, к которому принадлежит файл, выбран компилятор Quartus (который мы создали чуть выше)
            {
                local file_mask = _(".verilog;.vlg;.v;.sv;.vhd;.vhdl;.tdf;.sopc;.sopcinfo;.cmp;.h;.qsys;.smf;.qxp;.ptf;.bdf;.gdf;.mdl;.cpp;.edn;.edif;.edf;.vqm;"); //список файлов, которые мы трактуем как исходники для квартуса
                local f = wxFileName();
                f.Assign(data.GetFolder(), ::wxPATH_NATIVE); //получаем имя файла, на котором кликнул юзер
                local ext = _T(".") + f.GetExt() + _T(";"); //выделяем расширение файла
                if(file_mask.Find(ext) != -1) //если файл является исходников для квартуса
                {
                    local projfile = project.GetFileByFilename(project.GetTitle() + _T(".qsf"), true, true);
                    if(!IsNull(projfile)) //если в проекте есть файл настроек квартуса
                    {
                        local prj_path = projfile.file.GetPath(::wxPATH_GET_VOLUME|::wxPATH_GET_SEPARATOR, ::wxPATH_NATIVE); //путь к проекту
                        if(f.MakeRelativeTo(prj_path, ::wxPATH_NATIVE)) //преобразуем путь к файлу из абсолютного в относительный
                        {
                            src_filename = f.GetFullPath(::wxPATH_NATIVE); //получаем относительный путь в файлу проекта в текстовом виде
                            prj_filename = projfile.file.GetFullPath(::wxPATH_NATIVE); //получаем абсолютный путь к файлу настроек в текстовом виде
                            src_file_ext = ext; //сохраняем расширение файла, что б потом лишнюю работу не делать
                            local buffer = IO.ReadFileContents(prj_filename); //читаем файл настроек
                            buffer.Replace(_T("\r\n"), _T("\n"), true); //workaround C::B bug, see patch ID #3162
                            buffer += _T("\n");
    
                            if((buffer.Find(_T(" ") + src_filename + _T("\n")) == -1) && (buffer.Find(_T(" \"") + src_filename + _T("\"\n")) == -1)) //проверяем, добавлен ли уже файл в проект
                            {
                                act_add_file = true; //файл ещё не в проекте - добавляем
                                entries.Add(_T("Add file ") + src_filename + _T(" to project ") + projfile.file.GetFullName(), 1); //добавляем пункт "Add file to project" в контекстное меню
                            }
                            else
                            {
                                act_add_file = false; //файл уже в проекте - удаляем
                                entries.Add(_T("Remove file ") + src_filename + _T(" from project ") + projfile.file.GetFullName(), 1); //добавляем пункт "Remove file from project" в контекстное меню
                            }
                        }
                    }
                }
            }
        }
        return entries;
    }


    // optional calback for context menu items clicking
    function OnModuleMenuClicked(index) //пользователь ткнул на наш пункт меню
    {
        if (index == 0) //если пользователь ткнул на первый пункт меню (а других у нас и нет)
        {
            local src_file_type = ::wxString();
            local file_mask = ::wxString();
            local str = ::wxString();
//к какому типу относится файл?
            file_mask = _T(".verilog;.vlg;.v;");
            if(file_mask.Find(src_file_ext) != -1)
                src_file_type = _T("VERILOG_FILE");
//<...> далее в том же духе для всех известных типов
            if((src_filename.Find(_T(" ")) == -1) && (src_filename.Find(_T("\t")) == -1)) //если в имени файла есть пробелы, заключаем его в кавычки
                str = _T("set_global_assignment -name " + src_file_type + _T(" ") + src_filename + _T("\n"));
            else
                str = _T("set_global_assignment -name " + src_file_type + _T(" \"") + src_filename + _T("\"\n"));
            local buffer = IO.ReadFileContents(prj_filename); //читаем файл настроек
            local prj_file_eol = DetectEol(buffer); //проверяем какого типа в нём концы строк

            buffer.Replace(prj_file_eol, _T("\n"), true) //workaround C::B bug, see patch ID #3162
            local tmp = buffer.Right(1);
            if(!tmp.Matches(_T("\n")))
                buffer += _T("\n");

            if(act_add_file)
            { //если добавляем файл, то просто дописываем строку в конец
                buffer += str;
            }
            else
            { //иначе 
                local i = buffer.Find(str); //ищем строку в файле
                if(i != -1)
                {
                    buffer = buffer.Remove(i, str.Length()); //и вырезаем её
                }
                else
                { //если не удалось найти - косяк какой-то, что-то сломалось
                    ::ShowMessage(_T("Filed to remove file ") + src_filename + _T(" from proj ") + prj_filename = _T(":\r\n") +
                                  _T("can not find string \r\n") + str);
                    return;
                }
            }
            buffer.Replace(_T("\n"), prj_file_eol, true) //workaround C::B bug, see patch ID #3162

            if(IO.WriteFileContents(prj_filename, buffer)) //записываем изменённый файл проекта
            {
                ::ShowMessage(_T("File ") + prj_filename + _T(" modified"));
            }
            else
            {
                ::ShowMessage(_T("Can not write file ") + prj_filename);
            }
        }
        else
            ::ShowMessage(_T("?!? Functionality not implemented yet"));
    }
}

// this call actually registers the script plugin with Code::Blocks
RegisterPlugin(QuartusPlugin());

// if you want to call this plugin's Execute() function, use this in a script:
// ExecutePlugin(_T("QuartusPlugin"));

Теперь надо этот скрипт скормить кодблоксу. По аналогии с визардом сохраняем наш файл в пользовательском каталоге C:\Documents and Settings\[username]\Application Data\codeblocks\share\codeblocks\scripts под каким-нибудь понятным именем, например quartus_add_to_project.script. Далее в C::B жмём Settings->Edit startup script, нас предупредят, что скрипт пытается скопировать файл — соглашаемся и в редакторе открывается startup.script. Добавляем в него строчку, подключающую наш скрипт:

Include(_T("quartus_add_to_project.script"));

Перезагружаем C::B и проверяем.


Ну вот, пожалуй для начала этого хватит. Уже вполне комфортно работать, а ведь мы ещё даже не начали писать настоящие плагины! Описанными выше способами можно адаптировать C::B в работе с самыми разными программаторами и системами, хотя, конечно, хотелось бы что бы всё это было из коробки =).
PS: принимаются идеи под доработке и улучшению =)

Часть IЧасть II — Часть III

Ссылки
  1. Моя очередь мигать диодиками — статья на we.easyelectronics.ru
  2. Scintilla — статья в wikipedia
  3. Дефолтовые кейбиндинги в сцинцилле
  4. Squirrel — статья в wikipedia
  5. Файлы проектов Code::Blocks — официальное wiki Code::Blocks
  6. Squirrel programming language — документация на официальном сайте
  7. Раздел о скриптовании — официальное wiki Code::Blocks
  8. Code::Blocks SDK
  9. Документация на wxWidgets
  10. Инструкция по скачиванию исходников Code::Blocks из VCS
  11. Описание основных функций, использующихся для создания визардов — официальное wiki Code::Blocks
  12. Описание формата ресурсов XRC
  13. Сравнение различных RAD для wxWidgets
  14. События, генерируемые системой при работе визарда — официальное wiki Code::Blocks
  15. Вводная в написание собственных визардов — официальное wiki Code::Blocks
  16. Adding support for non C/C++ files to the build system — официальное wiki Code::Blocks
  17. Регулярные_выражения — статья в wikipedia

  • +3
  • 30 мая 2011, 10:59
  • Alatar
  • 1
Файлы в топике: cb_quartus.zip

Комментарии (8)

RSS свернуть / развернуть
Забавно.
Я не очень-то ковырял CB, но есть некоторые мысли.
1) Подозреваю я, что плагин компилера где-то хранит описания компиляторов к таком виде, что их можно допилить. Например, вменяемые опции добавить (или просто удалить ненужные).
2) Не пробовал воспользоваться все-таки умением плагина компилера вызывать тулзы тулчейна? Можно ведь например compile single file привязать к расширениям сорцов VHDL, а вызывать по ним свою тулзу, которая будет вносить их в файл проекта квартуса (стоит правда еще подумать, где можно синхронизироваться на удаление). А общую компиляцию проекта можно попробовать зацепить на место линкера.

Алсо, еще вроде можно генерировать кастомный мейкфайл и скормить его C::B вместо обработки плагином Compiler. А мейк — штука довольно гибкая.
0
  • avatar
  • Vga
  • 30 мая 2011, 23:00
>>1) Подозреваю я, что плагин компилера где-то хранит описания компиляторов к таком виде, что их можно допилить. Например, вменяемые опции добавить (или просто удалить ненужные).

Есть такое дело, хранит, можно допилить, но до определённой степени — КомпилерПлагин не такой гибкий, как хотелось бы. Например, ты можешь сказать, что инклуды надо подключать не -I, а %include=, но не факт, что получится сказать не подключать их вообще. Кстати, на этой почве возникли проблемы с прикручиванием Keil C51.

>>2) Не пробовал воспользоваться все-таки умением плагина компилера вызывать тулзы тулчейна?

А? Что? Это где? Ты про то, что по 16-ой ссылке, или про что-то другое? В принципе можно, но надо экспериментировать, я думал над возможным применением этой штуки, но ничего толкового не придумал.

>>Можно ведь например compile single file привязать к расширениям сорцов VHDL, а вызывать по ним свою тулзу, которая будет вносить их в файл проекта квартуса (стоит правда еще подумать, где можно синхронизироваться на удаление).

Это что-бы полностью автоматизировать управление файлами в квартус проекте? Может проще тогда написать скрипт, который синхронизирует список файлов, между проектами CB и QII (и добавление и удаление). А потом, при желании, его можно будет в пребилд запихнуть. Хотя я не так часто добавляю новые файлы в проект, что бы шерстить файл проекта при каждой перекомпиляции.

>> А общую компиляцию проекта можно попробовать зацепить на место линкера.

Боюсь, в квартус таки посыпется лишняя инфа, которую не получится выпилить, хотя надо пробовать.

>> Алсо, еще вроде можно генерировать кастомный мейкфайл и скормить его C::B вместо обработки плагином Compiler. А мейк — штука довольно гибкая.

Это да, можно. Я так один AVR проект собирал с --combine (там тоже надо один вызов компилятора на все файлы). Но CB с ними не очень дружит, может всего несколько команд выполнять, типа make all, make clean и тд., так что очевидного профита для решения данной задачи я не придумал — программирование один фиг на тулсу вешать придётся.
0
Там ЕМНИП есть на первой вкладке несколько задач, вида Compile one file, etc. Там строки вида $compiler $includes $files etc, т.е, насколько я понял, шаблон всей командной строки. Здесь, теоретически, можно выпилить лишнюю инфу.
Ну и еще хотелось бы подредактировать список опций компилятора на первой странице его настроек.
0
Ну да, там можно всё ненужное убрать, согласен. А вот список опций компилятора, боюсь, правится только написанием нового CompilerPlugin.
0
Ну, для разных компилеров же он разный. Где-то должен быть прописан. Надеюсь, не в хардкоде, это всеж-таки не айс и нормальные программеры стараются этого избегать.
0
>> Надеюсь, не в хардкоде, это всеж-таки не айс и нормальные программеры стараются этого избегать.

Ну как бы тебе сказать… Я уже давно порываюсь переписать там половину подсистем, отвечающих за компиляторы и проекты, но как окину взглядом фронт работы, сразу находятся дела по-важнее. Да и боюсь, разработчики CB могут не оценить =)
0
Это что-бы полностью автоматизировать управление файлами в квартус проекте?
Да. Возможны конечно разные варианты, просто хотелось, чтобы оно все через имеющийся билд отрабатывало)
0
Алсо, статье не хватает подзаголовка «скрещиваем ужа с ежом»)
0
  • avatar
  • Vga
  • 30 мая 2011, 23:04
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.