[Перевод] Использование C# и Wix# для создания msi-пакетов
От переводчика: англоязычные it-блоггеры обычно начинают такие статьи со слов I’m so excited. Про Wix# я узнал совершенно случайно и спешу поделиться этим открытием с хабрасообществом, т.к. каждый, кто имел дело с «голым» WiX, знает, насколько неприятным может быть этот процесс. И вот теперь можно сделать msi-дистрибутив, написав всего лишь несколько строчек на C#! По-моему, это круто! А относительно недавно (4 дек 2014) автор Wix# Олег Шило дал интервью изданию InfoQ. Перевод этого интервью я и представляю вашему вниманию. И прошу не судить строго за кальку некоторых слов — тот же «деплоймент» мне как-то ближе, чем «развертывание».InfoQ: Для тех наших читателей, которые не слышали о Wix#: что это?
Oleg: Wix# (WixSharp) — это фреймворк для создания деплоймента (deployment authoring framework) на базе Windows Installer (MSI). Wix# позволяет построить полноценный MSI-дистрибутив по спецификации, написанной на C#. Типичный исходный файл Wix# состоит из обычного C#-кода, который использует C#-классы для задания сущностей WiX.
Wix# решает множество проблем, возникающих при создании MSI. Он в изящной и оригинальной манере обходит типичные ограничения MSI/WiX. Wix# вслед за другими транскомпиляторами (transcompilers) типа Script#, CoffeeScript или GWT использует исходный код на синтаксически более удобном языке (в данном случае C#) для получения исходного кода на менее удобном языке (WiX). «Синтаксически более удобный» в данном случае означает менее избыточный и более читабельный код. Код, в котором больше проверок на этапе компиляции и для которого доступны более продвинутые инструменты.
Также Wix# убирает необходимость в создании подмодулей MSI (Custom Actions) на совершенно другом языке (например C++), позволяя и компоненты, и поведение описывать на одном языке (C#). Также это дает возможность иметь более однородную, простую и согласованную структуру исходного кода.
Но что еще важнее, так это то, что Wix# предоставляет уровень абстракции, который скрывает всю сложность и неинтуитивность MSI/WiX и позволяет выразить алгоритм деплоймента в более естественном виде. Код ниже — это типичный пример Wix#, который демонстрирует скрипт для создания MSI-файла для установки приложения «My App»:
using System; using WixSharp; class Script { static public void Main () { var project = new Project («My App», new Dir (@»%ProgramFiles%\My Company\My App», new File (@»\\BuildServer\LatestRelease\Bin\MyApp.exe»), new File (@»\\BuildServer\LatestRelease\Bin\MyApp.exe.config»), new File (@»\\BuildServer\LatestRelease\Docs\readme.txt»))); project.GUID = new Guid (»6f330b47–2577–43ad-9095–1861ba25889b»); Compiler.BuildMsi (project); } } Разумеется, Wix# может выполнять и более сложные сценарии (Custom Actions или даже свой UI на WPF или WinForms), но код выше — это прекрасный пример того, в чем его суть.Wix# — это часть набора утилит CS-Script: скриптового движка с Открытым Кодом для выполнения C#-скриптов. Читатели InfoQ возможно уже знакомы с ним, так как я уже рассказывал про него в моем предыдущем интервью о CS-Script плагине для Notepad++.
InfoQ: Почему вы выбрали для Wix# формат API, а не формат языка предметной области (domain specific language, DSL)?
Oleg: Цель Wix# — не заменить XML-синтаксис каким-то другим. Это позволило бы решить только одно из множества практических ограничений связки MSI+WiX. С помощью Wix# я хотел свести разработку деплоймента к обычному программированию. Я хотел, чтобы самый обычный разработчик, который создает ПО, мог также быстро и комфортно создать решение для деплоймента. DSL означал бы, что разработчикам нужно учить очередной синтаксис и страдать от отсутствия вспомогательных утилит типа Intellisense. Также DSL скорее всего потребовал бы отдельного языка для создания Custom Actions. Я хотел, чтобы C# и ваша любимая среда разработки (IDE) стали «службой одного окна» (one-stop-shop) для разработки деплоймента. И тогда вашей команде становится не нужен отдельный WiX-специалист, обычных навыков программирования становится достаточно для реализации практически любого сценария деплоймента.
Однако, есть и менее очевидный мотив для отказа от DSL-подхода. Типичная задача DSL — это перевод одного синтаксиса в другой или же преобразование одной программной модели в другую. Задача перевода в контексте WiX имеет крайне мало практического смысла. Несмотря на легкость реализации это только переведет синтаксис WiX в более простой, но сохранит все ограничения MSI. С другой стороны, именно преобразование в новую чистую и более интуитивную модель программирования — это именно то, в чем нуждается WiX. Такое преобразование обычно достигается с использованием высокоуровневых языков программирования общего назначения (C# в контексте Wix#). И после этого новая модель программирования обычно привязывается к новому DSL-синтаксису. Однако в случае Wix# последний шаг (привязка синтаксиса) не принесет пользы. Подавляющее большинство Windows-разработчиков вполне освоились или как минимум знакомы с языками .Net. Так что новый DSL-синтаксис принес бы только лишние накладные расходы. Поэтому я решил остановиться на C#.
Несмотря на вышесказанное, DSL поверх программной модели Wix# может иметь смысл и легко реализуем. Но это уже совсем другая история…
InfoQ: Кроме сложности формата, что по Вашему мнению является самым большим препятствием для принятия WiX?
Oleg: Очень хороший вопрос. Да, WiX действительно очень сложен, но настоящим препятствием является не сложность, а тесная связь с MSI. WiX радикально упрощает доступ к программной модели MSI. Однако сама эта модель, а не сложность доступа к ней, и является корнем проблемы. Так что пользователи WiX все так же имеют дело с неудобством MSI, только теперь с использованием XML.
Здесь я бы хотел прерваться и сказать несколько слов в защиту WiX. Несовершенная программная модель MSI — это не вина WiX. На самом деле, учитывая, что WiX сделал для MSI, практически невозможно ожидать от него исправления ограничений MSI. Чтобы по-настоящему оценить WiX, нам нужно оглянуться на развитие технологий деплоймента Microsoft.
Когда был представлен MSI, он пытался быть «ответом на все вопросы». И, как и многие технологии Microsoft, он был с самого начала чрезмерно усложненным. С тех пор он не изменился. Microsoft реализовали в MSI множество сложных функций, которые сегодня имеют очень мало практического значения (например, компоненты, MSM, рекламная установка…).
И нет ничего удивительного в том, что Microsoft осознали проблему и избавились от всех этих функций в их последней попытке — ClickOnce. ClickOnce — очень простой фреймворк для деплоймента. Фактически Microsoft полностью отбросили концепцию MSI и реализовали ClickOnce с нуля как альтернативу. Он легкий, умный и минималистичный (в хорошем смысле). Правда он более закрытый, не расширяемый и не кастомизируемый.
Из-за внутренних недостатков MSI крайне не дружественен разработке. MSI-файл — это файл базы данных. Логика деплоймента задается через данные в таблицах. Microsoft предполагали, что MSI будет создаваться путем ручного заполнения этих данных, поле за полем, с помощью их редактора MSI-таблиц Orca. Это обеспечило огромный доход InstallShield’у (путем завышения цен за поддержку MSI), но оставило ни с чем разработчиков. А потом случился WiX. И остановил «хаос MSI». WiX абсолютно заслуживает всех похвал за внесение порядка и превращение создания MSI в задачу разработки ПО (а не вбивания данных) — «построение двоичных файлов из исходников». Таким образом, WiX позволил заполнять эти MSI-таблицы с помощью XML-файла, а не вручную. Однако, это не поменяло суть разработки MSI. С WiX разработчики все еще должны были (и должны до сих пор) думать в терминах «MSI-таблиц».
Всего пару недель назад один из пользователей WiX# поделился своим решением для простого MSI-сценария, когда нужно только внести правки в реестр. Проблема в том, что MSI/WiX требует задать папку установки… даже если вы не ставите ни одного файла, а только вносите ключи в реестр. Решение заключается в задании папки-пустышки и затем… операции «удалить папку» для того, чтобы избежать физического создания папки во время инсталляции. Все это нужно не из-за требований деплоймента, но из-за труднообъяснимого ограничения MSI: таблица папок должна иметь хотя бы одну запись.
WiX#, с другой стороны, позволяет автоматически создать запись для папки-заглушки и полностью скрывает сложность MSI/WiX:
var project = new Project («MyProduct», new RegValue (RegistryHive.LocalMachine, «Software\\My Company\\My Product», «Message», «Hello»), new RegValue (RegistryHive.LocalMachine, «Software\\My Company\\My Product», «Count», 777));
project.GUID = new Guid (»6f330b47–2577–43ad-9095–1861ba25889b»);
Compiler.BuildMsi (project); WiX-эквивалент кода выше составит порядка 40 строк очень плотного XML-кода плюс командный файл для запуска компилятора и линкера WiX.
«Папка-пустышка» — это не единичный случай. Есть множество других синтаксических «препятствий» MSI/WiX, которые решены в Wix#.
Многие WiX-гуру считают, что такая близость к архитектуре MSI — это сила WiX, потому что это дает абсолютный доступ к функциональности MSI. Я не согласен. Я уверен, что это препятствует принятию целой технологии, так как игнорирует потребности разработчика. Таким образом, WiX полностью MSI-ориентирован вместо того, чтобы быть ориентированным на разработчика. Но мы не должны обвинять в этом WiX. Я даже не уверен, что WiX вообще должен об этом задумываться. По-моему, здесь нужен еще один слой, который взял бы требования/спецификацию (например, Wix#-файл) и преобразовал бы в MSI-DB спецификацию (WiX-файл).
WiX играет ту же роль в среде MSI, какую играет IL в .NET. IL дает абсолютный доступ к возможностям CLR, но никто же не использует его для разработки. Для этого у нас есть все .NET-языки высокого уровня. Так что если я перефразирую вопрос как «что является самым большим препятствием для принятия IL?», ответ будет прост. Нет никакой необходимости для принятия. Для разработки у нас есть варианты гораздо лучше IL (при том что IL — жизненно важная часть всей .NET-технологии). И Wix# — это попытка привнести хотя бы один из таких «лучших вариантов» в предметную область MSI. Так что Wix# или любое подобное решение — это просто следующий шаг в развитии после WiX.
InfoQ: Кроме облегчения создания базовых пакетов инсталляции, что Вам видится самой интересной особенностью WiX#?
Oleg: Есть несколько очень важных вещей, заслуживающих упоминания.
1. Прежде всего это несокрушимая простота управляемых (managed) Custom Actions, которая проистекает из мощи высокоуровневого языка программирования (C#):
using System; using WixSharp; class Script { static void Main () { var project = new Project («CustomActionTest», new ManagedAction («MyAction», Return.check, When.After, Step.InstallInitialize, Condition.NOT_Installed)); Compiler.BuildMsi (project); } } public class CustomActions { [CustomAction] public static ActionResult MyAction (Session session) { MessageBox.Show («Hello World!», «Embedded Managed CA»); session.Log («Begin MyAction Hello World»); return ActionResult.Success; } } 2. Свой UI. Wix# не только позволяет вставить в последовательность UI обычный WinForms-диалог. Он также дает возможность создать полностью свой GUI и подсунуть его в рантайме MSI-движку. Эта фантастическая возможность, предлагаемая MSI API, полностью упускается из виду практически всеми фреймворками создания MSI при том, что Microsoft использует ее во множестве своих продуктов (в частности, инсталляторы Office, Visual Studio).
Я считаю собственный UI очень спорной техникой деплоймента и не советую (из соображений дизайна) разработчикам идти таким путем. Но я просто не мог игнорировать запросы пользователей и все же реализовал ее. Ниже скриншот WPF-примера (из дистрибутива Wix#) для собственного UI:
3. Последняя особенность, которую я хотел бы упомянуть, — это расширяемость Wix#. Описание деплоймента — это всего лишь объявление классов C#. Оно может быть легко улучшено с использованием своих вспомогательных классов для упрощения или изменения обычных условий установки. Например, вместо перечисления всех файлов вы можете создать класс (например, AllFiles), который пробежит структуру папки и сделает это для вас динамически. Так что скрипт установки не нужно будет менять, когда понадобится включить в дистрибутив новую dll:
var project = new Project («My App», new Dir (@»%ProgramFiles%\My Company\My App», new AllFiles (@»\\BuildServer\LatestRelease\*.*»))); В случае, когда возможностей Wix# недостаточно, вы можете напрямую работать со сгенерированным XML (WiX) контентом с использованием обработчиков событий компилятора Wix# (прямо перед тем как XML передается компилятору MSI). Вы можете добавить или убрать любой элемент или атрибут из дерева XML. В примерах показано, как установить ASP.NET-сайт с использованием встроенной функциональности WiX# и как сделать то же самое с помощью вставки требуемого XML прямо перед компиляцией.
Между прочим, количество примеров в дистрибутиве Wix# столь значительно, что составляет ~95% всего кода Wix#.
Я считаю, что на данном этапе жизненного цикла Wix# его основная функциональность практически завершена. Так что новые возможности будут обеспечивать скорее горизонтальное развитие, нежели вертикальное. Они будут, в основном, посвящены интеграции с инструментами разработки и большему удобству фреймворка для разработчиков. В моих планах публикация Wix# в NuGet (сделано, прим. перев.), предоставление полного эквивалента пользовательского интерфейса MSI, но как внешнего набора WinForm-диалогов, разработка отдельного плагина для Notepad++ и, возможно, задачи MSBuild с шаблоном проекта (на данный момент Visual Studio обрабатывает Wix# как обычный C#-проект). И, конечно, обработка запросов пользователей.
Wix# доступен на CodePlex под лицензией MIT.