понедельник, 16 марта 2009 г.

cygwin+windows!=unix

Забавно, что перед релизом всегда всплывают какие-то фундаментальные проблемы, видимо, это закон природы...

Сейчас случилось "страшное" - используемая при сборке Navi-Sailor система сборки дошла до своего технологического предела...

Возможно, я как-нибудь напишу об этой системе сборки (только не нужно в меня ничем кидаться и навешивать ярлык INH - когда она разрабатывалась в 1999-2000, большая часть ныне популярных инструментов была... э... далеко не столь популярна, а то и не существовала вовсе), но речь сейчас не об этом.

Одно маленькое отступление - основой этой системы сборки является комбинация gmake/perl + bash, все инструменты из состава cygwin, поскольку работаем мы исключительно под Windows.

Есть там понятие модуля, который имеет четко стандартизованные списки экспорта и импорта (что-то по концепции очень близкое к Coala от Philips, но попроще).

Файл описания проекта выглядит примерно так:
#
# Surf2Mesh
# Bleher's Surf Library package
#
# $HeadURL: https://svn.transas.com/navtech-primary/htibs/trunk/Components/Dynamic/Surf2Mesh/module.mak $
# $Id: module.mak 75010 2009-03-11 11:39:03Z amesk $
#

TARGET := $(BIN_DIR)/Surf2Mesh.dll

MODULE := SURF2MESH
SUBSYSTEM_NAME := NAVIGATION
MOD_EXP_INC := $(PRJ_DIR)/include

MOD_USE := WIN32 CRTdll COM_SUPPORT SHLWAPI ATL BOOST BOOST_THREAD_STATIC DYNAMIC_OBJECTS \
ATOMDB_API_INTERFACES GENERALNAVDATA NAVIMATH HORUSBASE TOTUS SURF

MOD_HDR_EXP_USE := NSGUITOOLS

MOD_HDR_USE += MACROS50 TEMPLATES SYSTEM_CONFIGURATION

PCH_FILE := $(PRJ_DIR)/StdAfx.h
OBH_FILE := $(PRJ_OUT)/StdAfx.obh

CXX_SOURCES := \
Surf2MeshImpl \
ShipMovementProvider \
Mesher \
ChartDataProvider \
NavMathHelpers \
HashHelper \
DllMain

include $(MAK_INC)/module.Mak


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

Каждый модуль указывает список модулей, который он использует, и что он экспортирует (в основном - каталоги с заголовочными файлами и библиотеки импорта) - и вот тут-то и порылась проблема....

В систему добавился новый модуль - после этого строка для компилятора с параметрами перестала влезать в 4K. Результат понятен - gmake пишет сакральное "CreateProcess(null)....failed", после чего сборка аварийно завершается.
Но! все это происходит не везде, а только на некоторых машинах разработчиков.... и (!) официальном сборочном компьютере.

Все в печали (включая меня), сроки уже поджимают. Самое интересное, что одним из уволенных сотрудников (о наших зимних злоключениях я упоминал) являлся maintainer этой системы (он же единственный технолог на команду, он же дублер билд-мастера, которого, впрочем, тоже уволили)...

Пришлось подключать голову, и причина проблем выявилась достаточно быстро - при формировании набора INCLUDE-путей (-I XXX) система чуть ли не с рождения неправильно устраняла дубликаты, в результате чего командная строка быстро распухала до неприличных величин.

Вот только что с этим делать - было совершенно непонятно, поскольку вызывать sort + uniq для фильтрации после формирования списка уже бесполезно (мы не сможем породить процесс с такими аргументами).

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

Тогда я решил попытаться, с подачи одного из наших разработчиков (на "слабО" взял), реализовать эту злосчастную функцию средствами самого gmake...

Идея пришла в голову внезапно - это использование рекурсии и встроенной фунции $(filter-out). Мы берем первую подстроку, формируем остаток строки с помощью $(word), из него убираем подстроку через $(filter-out), и рекурсивно вызываем сами себя, опять поделив остаток на первую подстроку и остаток. Критерий окончания рекурсии - невозможность делить строку дальше.

В коде это выглядит так же неаппетитно:
_unique_imp=$(2) $(if $(2),$(call _unique_imp,$(filter-out $(2),$(1)),$(word 1,$(filter-out $(2),$(1)))),)
_unique=$(call _unique_imp,$(1),$(word 1, $(1)))


Алгоритм вызывающе неоптимальный, жрет кучу памяти, но он спас ситуацию - оказалось достаточно вызвать его в двух ключевых точках основного Makefile'а от gmake.
Прямо функциональное программирование какое-то... По обилию скобочек результат напомнил понятно какой язык ;-)

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

Вот такая получилась забавная история...

Комментариев нет: