Использование оболочки Visual Studio 2010 для компиляции проектов с помощью gcc в Linux

Источник: habrahabr
NWOcs

Ни для кого не секрет, что Microsoft Visual Studio 2010 представляет собой мощную IDE, которая, помимо всего, позволяет заменять команды сборки проекта проектов путем внесения изменений в .vcxproj файлах. Как выяснилось, можно использовать эту возможность, чтобы заставить Visual Studio собирать проекты с помощью gcc, работающего на другом компьютере под управлением Linux. При этом обращение к gcc на Linux должно происходить по сети, например по ssh. В этой статье мы расскажем вам о проделанном нами эксперименте по такой необычной настройке Visual Studio. 

Предположим, у нас есть программа:


#include <stdio.h>

int main() 
{
    printf("Hello world!");
    fflush(stdout);
    getchar();
    return 0;
}


Эта программа должна собираться в среде Linux и при помощи gcc. Конечно, это просто пример, на самом деле речь может идти о большом проекте для Linux с сотнями тысяч файлов и налаженной системой сборки на makefile, что не меняет сути предлагаемого решения. Наша задача - обеспечить возможность редактирования кода программы в Visual Studio и использования входящих в ее состав инструментов по анализу кода и других средств.
Для начала сделаем простенький makefile для этой программы:

NAME= test
OBJS= main.o

.SUFFIXES: .cpp
.SUFFIXES: .o

all: $(NAME)

clean:
rm -rf *.o

cleanall: clean
rm -rf *.d map dep *~ $(NAME)

rebuild: cleanall all
@eсho Rebuild done.

$(NAME): $(OBJS)
@echo Compiling $(NAME).
g++ -o $(NAME) $(OBJS)

.cpp.o:
gcc -c $*.cpp

Теперь нужно решить следующую проблему: код должен редактироваться на платформе Windows (Visual Studio), а компилироваться в Linux. Для этого можно использовать виртуальные машины и разделяемыепапки. Например, в хостовой системе Windows можно установить любое средство виртуализации (Oracle VirtualBox или VMware Workstation), затем создать виртуальную машину и установить в ней Linux. В результате появляется возможность одновременно работать как с Windows, так и с Linux. Функция Shared Folders позволяет получить доступ к файлам хостовой ОС Windows из виртуальной машины Linux. 
Для VMware Workstation можно настроить shared folders, пробросив, например, D:\proj\ в Linux как папку proj. Тогда из Windows можно редактировать файл программы main.c, расположенный на диске Windows D:\proj\main.c и, при этом, компилировать его, используя gcc, в Linux в папке /mnt/hgfs/proj/.

В Visual Studio можно заменить команды сборки проекта:
• Build - сборка.
• Rebuild - очистка и сборка проекта заново.
• Clean - очистка файлов проекта (удаление всех бинарных файлов).
плюс команда для запуска проекта. 
Для среды Linux они будут соответствовать следующим:
• Build: make all
• Rebuild: make rebuild
• Clean: make clean
• Запуск: ./test

Наша задача - запускать в Windows так, будто бы они запускаются в обычном cmd, при этом ввод/вывод команд должен перенаправляться обратно в Windows, если мы хотим видеть ошибки компиляции прямо в среде из Visual Studio. Для решения этой задачи можно использовать утилитку plink.exe (скачивается на официальном сайте www.chiark.greenend.org.uk/~sgtatham/putty/download.html) из пакета Putty. Эта утилитка может выполнить одну команду по ssh, при этом корректно перенаправляя ввод/вывод на текущий терминал cmd.
Допустим, Linux в виртуальной машине настроен так, что из Windows к нему можно обращаться по ssh используя IP адрес 192.168.1.8, имя пользователя - user, а пароль - 123456. Тогда, запустив cmd, можно в Windows успешно выполнить команду:
D:\proj\tools>plink -batch -pw 123456 user@192.168.1.8 pwd 
/home/user
Результат работы программы говорит нам о том, что "pwd" выполнилось в домашнем каталоге пользователя user. Это значит, что прямо в cmd можно скомпилировать программку test следующим образом:

D:\proj\tools>plink -batch -pw 123456 user@192.168.1.8 cd /mnt/hgfs/proj/; make rebuild
rm -rf *.o
rm -rf *.d map dep *~ test
gcc -c main.cpp
Compiling test.
g++ -o test main.o
Rebuild done.


Теперь нам остается интегрировать указанный метод в Visual Studio. Для этого, создадим пустой Solution с названием vs_test в каталоге proj. Добавим проект "vs_test" в созданный Solution. Проект должен иметь тип Makefile (все остальные настройки по умолчанию).







В результате получится следующее дерево файлов:

D:\proj\main.c
D:\proj\makefile
D:\proj\tools\plink.exe
D:\proj\vs_test\vs_test.sln
D:\proj\vs_test\vs_test.suo
D:\proj\vs_test\vs_test.sdf
D:\proj\vs_test\vs_test\vs_test.vcxproj
D:\proj\vs_test\vs_test\vs_test.vcxproj.filters
D:\proj\vs_test\vs_test\vs_test.vcxproj.user 


Далее нужно добавить в проект "vs_test" наши makefile и main.c. Для этого следует воспользоваться опцией проекта "Add->Existing Item…". Таким образом получим следующую картину в Solution Explorer:



Далее, при помощи опции "Unload project" выгружаем проект из solution.



Теперь открываем на редактирование файл проекта при помощи опции "Edit vs_test.vcxproj"



Теперь при помощи "File->New->File…"создаем текстовый файл, и называем его make_vs.props, размещая его в D:\proj\make_vs.props.
Далее, при помощи тэга "Import" включим текст файла make_vs.props в vs_test.vcxproj. Для этого в файле vs_test.vcxproj добавим строку, импортирующую дополнительные настройки проекта из make_vs.props:

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <Import Project="$(SolutionDir)..\make_vs.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>


В файле make_vs.props мы можем переопределить любые настройки проекта или дописать свои собственные. У нас получился такой файл make_vs.props:


<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup Label="RemoteBuildLocals">
    <RblFolder>proj</RblFolder>
    <RblIncludePath>$(SolutionDir)\..\inc\</RblIncludePath>
    <RblExecute>./test</RblExecute>
  </PropertyGroup>

  <PropertyGroup Label="RemoteBuildSettings">
    <RbHost>192.168.1.8</RbHost>
    <RbUser>user</RbUser>
    <RbPassword>123456</RbPassword>
    <RbRoot>test_src</RbRoot>
  </PropertyGroup>

  <PropertyGroup Label="RemoteBuild">
    <RbToolArgs> -pw $(RbPassword) $(RbUser)%40$(RbHost) cd $(RbRoot); cd $(RblFolder);</RbToolArgs>
    <RbToolExe>$(SolutionDir)..\tools\plink -batch $(RbToolArgs)</RbToolExe>
    <RbBuildCmd>$(RbToolExe) make all</RbBuildCmd>
    <RbRebuildAllCmd>$(RbToolExe) make rebuild</RbRebuildAllCmd>
    <RbCleanCmd>$(RbToolExe) make cleanall</RbCleanCmd>
    <RbExecuteCmd>$(RbToolArgs) $(RblExecute)</RbExecuteCmd>
    <RbIncludePath>$(RblIncludePath)</RbIncludePath>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)/$(Platform)'=='Debug/Win32'">
    <NMakeBuildCommandLine>$(RbBuildCmd)</NMakeBuildCommandLine>
    <NMakeReBuildCommandLine>$(RbRebuildAllCmd)</NMakeReBuildCommandLine>
    <NMakeCleanCommandLine>$(RbCleanCmd)</NMakeCleanCommandLine>
    <IncludePath>$(RbIncludePath)</IncludePath>
    <LocalDebuggerCommand>$(SolutionDir)..\tools\plink</LocalDebuggerCommand>
    <LocalDebuggerCommandArguments>$(RbExecuteCmd)</LocalDebuggerCommandArguments>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)/$(Platform)'=='Release/Win32'">
    <NMakeBuildCommandLine>$(RbBuildCmd)</NMakeBuildCommandLine>
    <NMakeReBuildCommandLine>$(RbRebuildAllCmd)</NMakeReBuildCommandLine>
    <NMakeCleanCommandLine>$(RbCleanCmd)</NMakeCleanCommandLine>
    <IncludePath>$(RbIncludePath)</IncludePath>
    <LocalDebuggerCommand>$(SolutionDir)..\tools\plink</LocalDebuggerCommand>
    <LocalDebuggerCommandArguments>$(RbExecuteCmd)</LocalDebuggerCommandArguments>
  </PropertyGroup>

</Project>


Перезагружаем проект при помощи "Reload project". И просто нажимаем F5. Выглядеть все должно после этого следующим образом:




Ура! Visual Studio для компиляции сама обратилась к make и gcc из Linux, и мы получили в окне IDE вывод от gcc и запустили нашу программу test, с которой так же можно работать из Windows.
Теперь кратко разберем основной файл make_vs.props (начнем с конца..). Файл разбит на группы настроек, для того чтобы избежать лишнего копирования текста из одного проекта в другой (методика опробована на практике для Solution более чем из сотни проектов такого вида).
Первый(на самом деле последний) блок - это блок настроек, которые Visual Studio использует для осуществления сборки проекта, состоит ихдвух дублирующихся групп для конфигураций Debug и Release.

<PropertyGroup Condition="'$(Configuration)/$(Platform)'=='Debug/Win32'">
    <NMakeBuildCommandLine>$(RbBuildCmd)</NMakeBuildCommandLine>
    <NMakeReBuildCommandLine>$(RbRebuildAllCmd)</NMakeReBuildCommandLine>
    <NMakeCleanCommandLine>$(RbCleanCmd)</NMakeCleanCommandLine>
    <IncludePath>$(RbIncludePath)</IncludePath>
    <LocalDebuggerCommand>$(SolutionDir)..\tools\plink</LocalDebuggerCommand>
    <LocalDebuggerCommandArguments>$(RbExecuteCmd)</LocalDebuggerCommandArguments>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)/$(Platform)'=='Release/Win32'">
    <NMakeBuildCommandLine>$(RbBuildCmd)</NMakeBuildCommandLine>
    <NMakeReBuildCommandLine>$(RbRebuildAllCmd)</NMakeReBuildCommandLine>
    <NMakeCleanCommandLine>$(RbCleanCmd)</NMakeCleanCommandLine>
    <IncludePath>$(RbIncludePath)</IncludePath>
    <LocalDebuggerCommand>$(SolutionDir)..\tools\plink</LocalDebuggerCommand>
    <LocalDebuggerCommandArguments>$(RbExecuteCmd)</LocalDebuggerCommandArguments>
  </PropertyGroup>


Как не трудно догадаться, значения тэгов следующие:
• NMakeBuildCommandLine - команда Build (make all).
• NMakeReBuildCommandLine - команда Rebuild (make rebuild).
• NMakeCleanCommandLine - команда Clean (make clean).
• IncludePath - список Include директорий. Без корректного списка VS не сможет нормально обработать и распарсить Ваш код.
• LocalDebuggerCommand - команда запуска программы после компиляции.
• LocalDebuggerCommandArguments - аргументы команды при запуске программы после компилляции.

На данном этапе все значения указаны ссылками для других настроек. Эту группу настроек удобно выделить в Common.props и включать всегда во все проекты такого вида.

Следующая группа настроек соответствует заданию команд, которые должны выполняться при сборке.
.
 <PropertyGroup Label="RemoteBuild">
    <RbToolArgs> -pw $(RbPassword) $(RbUser)%40$(RbHost) cd $(RbRoot); cd $(RblFolder);</RbToolArgs>
    <RbToolExe>$(SolutionDir)..\tools\plink -batch $(RbToolArgs)</RbToolExe>
    <RbBuildCmd>$(RbToolExe) make all</RbBuildCmd>
    <RbRebuildAllCmd>$(RbToolExe) make rebuild</RbRebuildAllCmd>
    <RbCleanCmd>$(RbToolExe) make cleanall</RbCleanCmd>
    <RbExecuteCmd>$(RbToolArgs) $(RblExecute)</RbExecuteCmd>
    <RbIncludePath>$(RblIncludePath)</RbIncludePath>
  </PropertyGroup> 

Значения тэгов следующие:
• RbToolArgs - стандартные аргументы утилиты plink которые будут использоваться всегда.
• RbToolExe - общее значение начала всех команд, которые будут использоваться далее.
• RbBuildCmd - простая команда Build.
• RbRebuildAllCmd - простая команда Rebuild.
• RbCleanCmd - простая команда Clean.
• RbExecuteCmd - для запуска программы test после сборки все делится на команду и аргументы - эта часть отвечает за аргументы.
• RbIncludePath - переобозначенный список Include директорий.

Описанную группу настроек удобно выделить в тот же Common.props.

Следующая группа настроек, общая для всех проектов, но некоторые параметры будут различаться в зависимости от настроек стенда.

 <PropertyGroup Label="RemoteBuildSettings">
    <RbHost>192.168.1.8</RbHost>
    <RbUser>user</RbUser>
    <RbPassword>123456</RbPassword>
    <RbRoot>test_src</RbRoot>
  </PropertyGroup> 


Как можно видеть, указаны имя хоста, идентификатор пользователя и его пароль, а так же путь к каталогу с файлами проектов для Linux. Эти настройки удобно выделить в специальный user.props и включать его в Common.props при помощи тэга Import.
Последняя группа настроек касается только конкретного проекта.

 <PropertyGroup Label="RemoteBuildLocals">
    <RblFolder>proj</RblFolder>
    <RblIncludePath>$(SolutionDir)\..\inc\</RblIncludePath>
    <RblExecute>./test</RblExecute>
  </PropertyGroup>


Значения тэгов следующие:
• RblFolder - папка, где находятся файлы проекта (для Linux).
• RblIncludePath - список Include директорий (для Windows).
• RblExecute - команда для запуска.



Учтите, что при каждой команде Build происходит установка ssh соединения, что требует некоторого времени (например, у меня выходило порядка 2-5 секунд). 
В итоге у нас получилось заставить Visual Studio оcуществлять сборку проекта при помощи makefile и gcc из Linux.

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=30427