Как я стенд для сборки с windows на wine мигрировал
ПреамбулаЕсть у меня несколько старых проектов, писанных на С++, которые все еще развиваю по мере сил. Казалось бы — в чем же дело? Увы, это пачка очередных плагинов под мой любимый Adobe InDesign.И каждый раз, когда выходит новый Creative Suite, приходится портировать это дело. Что интересно, основные усилия уходят на то, чтобы собрать новую версию по новым правилам, и подогнуть инсталлятор. Потому как уж если дошел до стадии «оно компилируется», то как правило — работает. Хотя конечно есть нюансы — например в один прекрасный момент PlaceGun перестал раскладывать несколько выбранных изображений, только первое. Но об этом — в следующий раз.
И разумеется — хотелось бы это собирать под все версии и все платформы за раз, а не «открыл вижлу — собрал — закрып — повторил».
Итак, для сборки, нам нужны одновременно
MS VS 2005 MS VS 2005 sp1 MS VS 2008 MS VS 2010 MS VS 2012
Переходим на gmake — профит Почему так? Ах, я не сказал — еще есть MacOSX. Так что прощай nmake без вариантов.И в итоге получаем вот такой кошмар:
Скрытый текст cl-cs3.mak cl-cs4.mak cl-cs5.mak cl-cs55.mak cl-cs6.mak cl-cc.mak cl-cc2014.mak cl.mak commplugs.mak gcc-cs3.mak gcc-cs4.mak gcc-cs5.mak gcc-cs55.mak gcc-cs6.mak gcc-cc.mak gcc-cc2014.mak gcc.mak mac-defs.mak platform-impl.mak platform-targets.mak platform.mak win-defs.mak А запускается это примерно так:
make ARCH=x64 INDD=cc2014 compile Что очевидно, платформа определяется через uname. А каждая часть собирается стандартным образом как make -C foo.
Описание каждого компонента выглядит примерно так
Скрытый текст include …/…/make/platform.mak
TARGET=…/…/…/lib/$(ARCH)/$(INDD)/libz.$(LibSuffix)
OBJS=\ $(ARCH)/$(INDD)/adler32.$(OBJSuffix) \ $(ARCH)/$(INDD)/compress.$(OBJSuffix) \ $(ARCH)/$(INDD)/crc32.$(OBJSuffix) \ $(ARCH)/$(INDD)/deflate.$(OBJSuffix) \ $(ARCH)/$(INDD)/gzio.$(OBJSuffix) \ $(ARCH)/$(INDD)/infback.$(OBJSuffix) \ $(ARCH)/$(INDD)/inffast.$(OBJSuffix) \ $(ARCH)/$(INDD)/inflate.$(OBJSuffix) \ $(ARCH)/$(INDD)/inftrees.$(OBJSuffix) \ $(ARCH)/$(INDD)/trees.$(OBJSuffix) \ $(ARCH)/$(INDD)/uncompr.$(OBJSuffix) \ $(ARCH)/$(INDD)/zutil.$(OBJSuffix) \
all: $(TARGET)
$(TARGET): $(OBJS) $(AR) $(LFLAGS) $(OBJS)
clean: $(RMRF) $(TARGET) $(OBJS) И все это работало на отдельно выделенной виртуалке под WinXP (и такой же под хакинтошем).
Расписывать эти страсти от и до смысла как бы не вижу, приведу только наиболее интересные выдержки. Например, вот так вычисляем project root и платформу, на которой сейчас будет выполняться компиляция:
PR:=$(subst make/,,$(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))) OSA:=$(shell uname -o)
ifeq (Darwin,$(OSA)) OS=mac else OS=win endif А вот так определяется где искать Boost (адобы в каждой версии умудряются переложить, поэтому тут две части — определяем макрос и потом его используем):
ifeq ($(ARCH), x86) BoostARCH=32 else BoostARCH=64 endif BoostLib=$(subst \,/,$(AdobeSDK))/external/dva/third_party/boost_libraries/bin.v2/libs/boost_$1/lib/$(OS)/release/$(BoostARCH)/boost_$1.$(LibSuffix)
BoostFilesystemLib=$(call BoostLib, filesystem) BoostThreadLib=$(call BoostLib, threads) BoostRegexLib=$(call BoostLib, regex) BoostSystemLib=$(call BoostLib, system) Понятно, что набирал я это не руками, а написал пачку батников, да под MinGW:
genmake-for-dll.bat component-name [path-to-sources [target-directory]] @echo off rem genmake component-name [path-to-sources [target-directory]] set CN=%1 set FP=%2 if -%1==- set CN=default if -%2==- set FP=. if -%3==- set TD=. echo> MakeF COMPONENT=%CN% echo>> MakeF include …/make/platform.mak echo>> MakeF TARGET=$(OBJDIR)/$(COMPONENT).$(DLLSuffix) echo>> MakeF OBJS=\ find %FP% -type f -name '*.c*' | grep -v .svn | awk '{ print »\t$(OBJDIR)/» $1 ».$(OBJSuffix)\t\\\»; }' | sed -e 's/\.cpp\./\./' | sed -e 's/\.cxx\./\./' | sed -e 's?/%FP%?' >> MakeF echo>> MakeF # echo>> MakeF HEADERS=\ find %FP% -type f -name '*.h*' | grep -v .svn | awk '{ print »\t» $1 »\t\\\»; }' > headers >> MakeF echo>> MakeF # echo>> MakeF # echo>> MakeF all: $(TARGET) echo>> MakeF # echo>> MakeF $(TARGET): $(OBJS) echo>> MakeF $(LINK) $(LDFLAGS) $(OBJS) $(XLIBS) echo>> MakeF if [ -f $@.manifest ] ; then mt -nologo -manifest $@.manifest »-outputresource:$@;2»; fi echo>> MakeF # echo>> MakeF clean: echo>> MakeF rm -f $(TARGET) $(OBJS) echo>> MakeF # find %FP% -type f -name '*.c*' | grep -v .svn | awk '{ print »$(OBJDIR)/» $1 ».$(OBJSuffix) :» $1 » $(HEADERS)\n\t$(CC) $(CFLAGS) » $1 »\n»; }' | sed -e 's/.cpp././' | sed -e 's/\.cxx\./\./' | sed -e 's?/%FP%/?/?' >> MakeF echo>> MakeF #EOF mv MakeF Makefile genmake-for-lib.dll component-name [path-to-sources [target-directory]] @echo off rem genmake component-name [path-to-sources [target-directory]] set CN=%1 set FP=%2 if -%1==- set CN=default if -%2==- set FP=. if -%3==- set TD=. echo> MakeF COMPONENT=%CN% echo>> MakeF include …/make/platform.mak echo>> MakeF TARGET=$(OBJDIR)/lib$(COMPONENT).$(LibSuffix) echo>> MakeF OBJS=\ find %FP% -type f -name '*.c*' | grep -v .svn | awk '{ print »\t$(OBJDIR)/» $1 ».$(OBJSuffix)\t\\\»; }' | sed -e 's/\.cpp\./\./' | sed -e 's/\.cxx\./\./' | sed -e 's?/%FP%?' >> MakeF echo>> MakeF # echo>> MakeF HEADERS=\ find %FP% -type f -name '*.h*' | grep -v .svn | awk '{ print »\t» $1 »\t\\\»; }' > headers >> MakeF echo>> MakeF # echo>> MakeF # echo>> MakeF $(TARGET): $(OBJS) echo>> MakeF $(AR) $(LFLAGS) $(OBJS) echo>> MakeF # echo>> MakeF clean: echo>> MakeF rm -f $(TARGET) $(OBJS) echo>> MakeF # find %FP% -type f -name '*.c*' | grep -v .svn | awk '{ print »$(OBJDIR)/» $1 ».$(OBJSuffix) :» $1 » $(HEADERS)\n\t$(CC) $(CFLAGS) » $1 »\n»; }' | sed -e 's/.cpp././' | sed -e 's/\.cxx\./\./' | sed -e 's?/%FP%/?/?' >> MakeF echo>> MakeF #EOF mv MakeF Makefile genmake-for-indd.bat plugin-name [path-to-sources [target-directory]] @echo off setlocal rem genmake plugin-name [path-to-sources [target-directory]] set CN=%1 set FP=%2 if -%1==- set CN=plugin if -%2==- set FP=. if -%3==- set TD=. echo> MakeF COMPONENT=%CN% echo>> MakeF include …/make/platform.mak echo>> MakeF PluginName=$(COMPONENT) echo>> MakeF TARGET_DIR=$(OBJDIR)/$(PluginPrefix) echo>> MakeF TARGET=$(TARGET_DIR)/$(PluginName)$(PluginSuffix) echo>> MakeF CFLAGS+=-I …/common echo>> MakeF LIBS+=$(call add_component_ref, vl) $(call add_component_ref, common) echo>> MakeF OBJS=\ find %FP% -type f -name '*.c*' | grep -v .svn | awk '{ print »\t$(OBJDIR)/» $1 ».$(OBJSuffix)\t\\\»; }' | sed -e 's/\.cpp\./\./' | sed -e 's/\.cxx\./\./' | sed -e 's?/%FP%?' >> MakeF echo>> MakeF $(COMMON_PLUGIN_OBJS) echo>> MakeF # echo>> MakeF HEADERS=\ find %FP% -type f -name '*.h*' | grep -v .svn | awk '{ print »\t» $1 »\t\\\»; }' > headers >> MakeF echo>> MakeF # echo>> MakeF # echo>> MakeF ifeq (win,$(OS)) echo>> MakeF OBJS+= $(OBJDIR)/$(COMPONENT).res echo>> MakeF FRES_TGT=$(OBJDIR)/$(COMPONENT)_w.$(FRES) echo>> MakeF else echo>> MakeF FRES_TGT=$(OBJDIR)/R/$(COMPONENT)_w.$(FRES) echo>> MakeF endif echo>> MakeF # echo>> MakeF ifeq (mac,$(OS)) echo>> MakeF ifeq (x64,$(ARCH)) echo>> MakeF TARGET:= echo>> MakeF endif echo>> MakeF endif echo>> MakeF # echo>> MakeF all: $(TARGET) echo>> MakeF # echo>> MakeF clean: echo>> MakeF rm -rf $(TARGET) $(OBJS) $(TARGET_DIR)/* echo>> MakeF # echo>> MakeF # echo>> MakeF $(TARGET): $(OBJS) $(LIBS) $(FRES_TGT) echo>> MakeF $(LINK) $(LDFLAGS) $(LDFLAGS_InDesignPlugin) $(OBJS) $(LIBS) $(AdobeLIBS) $(XLIBS) echo>> MakeF # echo>> MakeF # echo>> MakeF ifeq ($(OS), win) echo>> MakeF $(OBJDIR)/$(COMPONENT)_w.$(FRES): $(COMPONENT).fr echo>> MakeF $(ODFRC) $(ODFRCFLAGS) -o $(call unix_to_dos,$(OBJDIR)/$(COMPONENT)_w.$(FRES)) $(COMPONENT).fr echo>> MakeF # echo>> MakeF $(OBJDIR)/$(COMPONENT).res: $(COMPONENT).rc $(OBJDIR)/$(COMPONENT)_w.$(FRES) echo>> MakeF $(RSC) $(RSCFLAGS) $(CFLAGS_INCLUDE) $(COMPONENT).rc echo>> MakeF $(AdobeSDK)\devtools\bin\merge_res.cmd $(call unix_to_dos,$(OBJDIR)) $(COMPONENT) $(COMPONENT)_w echo>> MakeF mkdir -p »$(TARGET_DIR)/($(PluginName) Resources)» echo>> MakeF cp -r $(OBJDIR)/idrc_* »$(TARGET_DIR)/($(PluginName) Resources)» echo>> MakeF endif echo>> MakeF ifeq ($(OS), mac) echo>> MakeF $(OBJDIR)/R/$(COMPONENT)_w.$(FRES): $(COMPONENT).fr echo>> MakeF mkdir -p $(TARGET_DIR)/Resources echo>> MakeF mkdir -p $(OBJDIR)/R echo>> MakeF $(ODFRC) $(ODFRCFLAGS) $(CFLAGS_IXA_OF) -o $@ $(COMPONENT).fr echo>> MakeF /Developer/Tools/ResMerger -dstIs DF $@ -o $(OBJDIR)/$(PluginName).$(FRES) echo>> MakeF /Developer/Tools/ResMerger $(OBJDIR)/$(PluginName).$(FRES) -dstIs DF -o $(TARGET_DIR)/Resources/$(PluginName).rsrc echo>> MakeF cp -r $(OBJDIR)/R/idrc_* Info.plist $(TARGET_DIR)/Resources echo>> MakeF (cd $(TARGET_DIR)/…; ln -s A Current; cd … ; ln -s Versions/Current/Resources Resources; ln -s Versions/Current/$(PluginName) $(PluginName)) echo>> MakeF endif echo>> MakeF include …/make/commplugs.mak echo>> MakeF # echo>> MakeF # find %FP% -type f -name '*.c*' | grep -v .svn | awk '{ print »$(OBJDIR)/» $1 ».$(OBJSuffix) :» $1 » $(HEADERS)\n\t$(CC) $(CFLAGS) » $1 »\n»; }' | sed -e 's/.cpp././' | sed -e 's/\.cxx\./\./' | sed -e 's?/%FP%/?/?' >> MakeF echo>> MakeF #EOF rem mv MakeF Makefile endlocal
Ничто не предвещало, и ага И все это прекрасно работало до того момента, пока не вышел Adobe InDesign CC 2014. И захотел вижуалстудию 2012. Вот тут-то белый пушной зверь каак выпрыгнул! Нет, я конечно в теории знал, что вижуалстудия давным-давно не работает на ХРюше. Но вот то что cl.exe внезапно оказался not valid Win32 image — это был удар.
Немного поясню — еще со времен двух вижуалстудий 2005 с сервиспаком и без сервиспака одновременно, на билд машинку я ничего честно не ставлю. Для этого есть чистая виртуалка, в которую ставлю вижуалстудию express edition, накатываю правильный platform sdk, и то что получилось (лицензионно чистое и так далее) копирую в соответствующую папочку. А виртиуалку откатываю обратно до состояния «ничего не было».
И раз инсталлятор 2012 студии захотел поновее — не вопрос, вот вам Windows 8.1. Любой каприз — для микрософт ;-)
Ставлю, копирую — опаньки.
Месье знает толк в извращениях И тут встал суровой силы вопрос — что делать?
Вариантов немного.
Поставить и обжить новую виртуалку под Windows 8.1, начиная от MinGW и заканчивая индезигнами. И лицензии найти надо — они конечно у меня все есть, но лежат в совершенно разных местах. Долго и нудно. Перебраться в амазоново облако — на w2k12, хватит надолго и работать будет быстро. Но снова та же проблема — долго и нудно. И все эти накопившиеся версии и копии — 25 гигов перебрасывать. Лениво. Извернуться так чтобы не пришлось ничего менять. Почесал я маковку, и подумал —, а пуркуа бы не па? Ведь хостом у меня опенсусь стоит.Набираю
wine где-там-скопировал-2012-студию\vc\bin\cl.exe /help и оно таки работает.— Ага! — сказали суровые сибирские мужики.
Не без граблей конечно прошло — выяснилось что некоторые библиотеки от вижуалстудийного рантайма в вайне не совсем такие. Это не проблема — у вижуалстудии есть эталонные, скопировал по папочкам вижуалстудии.
А вот что удивило, совершенно простой mt.exe не только сам падал, но и вызывал SIGSEGV у wine. Шаманство с библиотеками решения не дало, пришлось по-быстрому написать свой заменитель с поэтессами.
Скрытый текст
#include
#include
#include
static void usage () { printf («Usage: mt.exe -manifest foo.dll.manifest -outputresource: foo.dll[;2]\n»); }
static void alert (char* fn, char* msg, int code) { static char* lpstrError; FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError (), LANG_USER_DEFAULT, &lpstrError, 1, NULL); fprintf (stderr,»%s: %s: %s, %d\n», fn, msg, lpstrError, code); LocalFree (lpstrError); }
static int update_res (char* ou, int resk, char* mf) { HANDLE hUpdateRes; LPVOID buf; BOOL result; FILE* fp; struct _stat st; int ressz = 0, outk;
fp = fopen (mf, «rb»); if (! fp) { alert (mf, «could not open manifest file», errno); return 2; }
if (_fstat (fileno (fp), &st) != 0) { fclose (fp); alert (mf, «could not determine manifest file size», errno); return 2; }
ressz = st.st_size; buf = (void*)malloc (ressz); if (! buf) { fclose (fp); free (buf); alert (mf, «could not allocate buffer for resource», ressz); return 2; } outk = fread (buf, 1, ressz, fp); if (outk!= ressz) { fclose (fp); free (buf); alert (mf, «could not read manifest», ressz — outk); return 2; } fclose (fp);
hUpdateRes = BeginUpdateResourceA (ou, FALSE); if (hUpdateRes == NULL) { free (buf); alert (ou, «Could not open file for writing.», 0); return 3; }
result = UpdateResourceA (hUpdateRes, RT_MANIFEST, MAKEINTRESOURCE (resk), MAKELANGID (LANG_NEUTRAL, SUBLANG_NEUTRAL), buf, ressz);
if (result == FALSE) { alert (ou, «could not add resource», 0); free (buf); return 4; }
if (! EndUpdateResource (hUpdateRes, FALSE)) { alert (ou, «could not write changes to file», 0); free (buf); return 4; } free (buf); return 0; }
int main (int argc, char** argv) { char* mf = NULL; char* ou = NULL; char* v; int resk = 1; int k;
if (argc < 2) { usage(); return 2; }
for (k=1; k < argc; ++k) { if(argv[k][0] == '-') { if(argv[k][1] =='m') { // manifest mf = argv[k+1]; ++k; continue; } else if(argv[k][1] == 'o' ) { // outputresource if(argv[k+1]) ou = argv[k+1]; else { ou = strchr(argv[k], ':'); if(!ou) { usage(); return 3; } ++ou; } ++k; v = strchr(ou, ';'); if(v) { resk = atoi(v + 1); *v = '\0'; } else resk = 1; continue; } } usage(); return 2; }
if (! mf || ! ou) { usage (); return 2; }
return update_res (ou, resk, mf); } А вот InnoSetup против ожиданий — сюрпризов не принес, собирает со свистом.
Пока — так, а далее поеду в облако, момент назрел.
Но все же решил поделиться с сообществом — вдруг я чего упускаю, вдруг кому пригодится…