Преамбула
Есть у меня несколько старых проектов, писанных на С++, которые все еще развиваю по мере сил. Казалось бы — в чем же дело? Увы, это пачка очередных плагинов под мой любимый 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 INDVER=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:
@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
@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
@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 <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <io.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> 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 против ожиданий — сюрпризов не принес, собирает со свистом.
Пока — так, а далее поеду в облако, момент назрел.
Но все же решил поделиться с сообществом — вдруг я чего упускаю, вдруг кому пригодится…
ссылка на оригинал статьи http://habrahabr.ru/post/232341/
Добавить комментарий