Как я стенд для сборки с windows на wine мигрировал

от автора

image

Преамбула

Есть у меня несколько старых проектов, писанных на С++, которые все еще развиваю по мере сил. Казалось бы — в чем же дело? Увы, это пачка очередных плагинов под мой любимый Adobe InDesign.

И каждый раз, когда выходит новый Creative Suite, приходится портировать это дело. Что интересно, основные усилия уходят на то, чтобы собрать новую версию по новым правилам, и подогнуть инсталлятор. Потому как уж если дошел до стадии «оно компилируется», то как правило — работает. Хотя конечно есть нюансы — например в один прекрасный момент PlaceGun перестал раскладывать несколько выбранных изображений, только первое. Но об этом — в следующий раз.

И разумеется — хотелось бы это собирать под все версии и все платформы за раз, а не «открыл вижлу — собрал — закрып — повторил».

Итак, для сборки, нам нужны одновременно

  • MS VS 2005
  • MS VS 2005 sp1
  • MS VS 2008
  • MS VS 2010
  • MS VS 2012

image

Переходим на gmake — профит

Почему так? Ах, я не сказал — еще есть MacOSX. Так что прощай nmake без вариантов.

И в итоге получаем вот такой кошмар:

Скрытый текст

  1. cl-cs3.mak
  2. cl-cs4.mak
  3. cl-cs5.mak
  4. cl-cs55.mak
  5. cl-cs6.mak
  6. cl-cc.mak
  7. cl-cc2014.mak
  8. cl.mak
  9. commplugs.mak
  10. gcc-cs3.mak
  11. gcc-cs4.mak
  12. gcc-cs5.mak
  13. gcc-cs55.mak
  14. gcc-cs6.mak
  15. gcc-cc.mak
  16. gcc-cc2014.mak
  17. gcc.mak
  18. mac-defs.mak
  19. platform-impl.mak
  20. platform-targets.mak
  21. platform.mak
  22. 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:

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 

image

Ничто не предвещало, и ага

И все это прекрасно работало до того момента, пока не вышел Adobe InDesign CC 2014. И захотел вижуалстудию 2012. Вот тут-то белый пушной зверь каак выпрыгнул!

Нет, я конечно в теории знал, что вижуалстудия давным-давно не работает на ХРюше. Но вот то что cl.exe внезапно оказался not valid Win32 image — это был удар.

Немного поясню — еще со времен двух вижуалстудий 2005 с сервиспаком и без сервиспака одновременно, на билд машинку я ничего честно не ставлю. Для этого есть чистая виртуалка, в которую ставлю вижуалстудию express edition, накатываю правильный platform sdk, и то что получилось (лицензионно чистое и так далее) копирую в соответствующую папочку. А виртиуалку откатываю обратно до состояния «ничего не было».

И раз инсталлятор 2012 студии захотел поновее — не вопрос, вот вам Windows 8.1. Любой каприз — для микрософт 😉

Ставлю, копирую — опаньки.

image

Месье знает толк в извращениях

И тут встал суровой силы вопрос — что делать?

Вариантов немного.

  1. Поставить и обжить новую виртуалку под Windows 8.1, начиная от MinGW и заканчивая индезигнами. И лицензии найти надо — они конечно у меня все есть, но лежат в совершенно разных местах. Долго и нудно.
  2. Перебраться в амазоново облако — на w2k12, хватит надолго и работать будет быстро. Но снова та же проблема — долго и нудно. И все эти накопившиеся версии и копии — 25 гигов перебрасывать. Лениво.
  3. Извернуться так чтобы не пришлось ничего менять.

Почесал я маковку, и подумал — а пуркуа бы не па? Ведь хостом у меня опенсусь стоит.

Набираю

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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *