protected override void CompilationAndBuild(IBuildContext context, CancellationTokenSource cts) { if (context.OnlyCheck) return; List<string> msiFiles = new List<string>(); foreach (string wxsFile in wxsFiles) { msiFiles.Add(InternalCompilationAndBuild(context, cts, wxsFile)); } string outDir = string.Format("{0} v{1}.{2}.{3}.{4} Patches v{5}.{6}.{7}.{8}", product.BaseName, product.BaseVersion.Major, product.BaseVersion.Minor, product.BaseVersion.Build, product.BaseVersion.Revision, product.TargetVersion.Major, product.TargetVersion.Minor, product.TargetVersion.Build, product.TargetVersion.Revision); outDir = Path.Combine(Path.GetDirectoryName(context.ProjectFileName), outDir); if (Directory.Exists(outDir)) Directory.Delete(outDir, true); Directory.CreateDirectory(outDir); context.BuildMessageWriteLine("Построенные MSP-файлы:", BuildMessageTypes.Information); foreach (string msiFile in msiFiles) { string outFile = Path.Combine(outDir, Path.GetFileName(msiFile)); File.Copy(msiFile, outFile); context.BuildMessageWriteLine(outFile, BuildMessageTypes.Information); } }
public void Build(IBuildContext context) { string prefix = context.OnlyCheck ? "Проверка" : "Сборка"; context.ClearBuildMessage(); context.BuildMessageWriteLine(prefix + " начата.", BuildMessageTypes.Notification); // Общее действие для вызова методов с отменой выполнения задач. // Параметры: // a - метод для вызова; // c - токен для отмены и других операций; // m - сообщение о начале действия. Action<Action<IBuildContext, CancellationTokenSource>, IBuildContext, CancellationTokenSource, string> actionWithErrorHandling = (Action<IBuildContext, CancellationTokenSource> a, IBuildContext ctx, CancellationTokenSource c, string m) => { // Не будем выводить названия действия при проверке, так как некоторые могут не выполнятся. if (!context.OnlyCheck) context.BuildMessageWriteLine(m, BuildMessageTypes.Notification); try { a(ctx, c); } catch (Exception e) { context.BuildMessageWriteLine(e.Message, BuildMessageTypes.Error); c.Cancel(); c.Token.ThrowIfCancellationRequested(); } }; Stopwatch stopwatch = Stopwatch.StartNew(); CancellationTokenSource cts = new CancellationTokenSource(); Task.Factory. StartNew(delegate { actionWithErrorHandling(CheckProduct, context, cts, ""); }, cts.Token). ContinueWith(delegate { // Загружаем шаблоны во временную папку. actionWithErrorHandling(LoadingTemplates, context, cts, "Загрузка шаблонов."); }, cts.Token). ContinueWith(delegate { actionWithErrorHandling(ProcessingTemplates, context, cts, "Обработка шаблонов."); }, cts.Token). ContinueWith(delegate { actionWithErrorHandling(CompilationAndBuild, context, cts, "Компиляция и сборка."); }, cts.Token). ContinueWith(delegate { stopwatch.Stop(); // Сюда токен не передаем. // Здесь должен быть код, выполняющийся в любом случае, завершились задачи или нет. context.BuildMessageWriteLine( string.Format(prefix + " завершена {0} за {1}.", cts.IsCancellationRequested ? "с ошибками" : "успешно", stopwatch.Elapsed.ToString("mm\\:ss")), cts.IsCancellationRequested ? BuildMessageTypes.Error : BuildMessageTypes.Information); if (context.BuildIsFinished != null) context.BuildIsFinished(); }); }
protected void RunCommand(IBuildContext context, string fileName, string command) { context.BuildMessageWriteLine(">" + fileName + " " + command, BuildMessageTypes.ConsoleSend); IProcessRunner runner = CreateProcessRunner(); runner.OutputMessageReceived += (s, e) => { context.BuildMessageWriteLine(e.Message, BuildMessageTypes.ConsoleReceive); }; runner.Start(fileName, command); if (runner.HasError) throw new Exception(string.Format("Запуск {0} завершился с ошибкой.", Path.GetFileName(fileName))); }
private void RunningLight(IBuildContext context, CancellationTokenSource cts) { context.BuildMessageWriteLine(LightDescription, BuildMessageTypes.Notification); // Путь к light. string lightFileName = Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.LightFileName); // Конечное имя. Меняем пробелы на подчеркивание. string outFileName = string.Format("{0}_v{1}.{2}", product.Name.Replace(' ', '_'), product.Version.Major, product.Version.Minor); // Добавим к имени файла директорию. string outDirectory = string.Format("{0} v{1}", product.Name, product.Version); outFileName = Path.Combine(Path.GetDirectoryName(context.ProjectFileName), outDirectory, outFileName); // Выполняем два раза. // Первый раз строим wixout, второй msi и wixpdb. for (int mode = 0; mode < 2; mode++) { // Команда для выполнения. StringBuilder command = new StringBuilder(); string targetFileName; if (mode == 0) { targetFileName = string.Format("{0}.{1}", outFileName, "wixout"); command.Append(string.Format("-bf -xo -out \"{0}\"", targetFileName)); } else { targetFileName = string.Format("{0}.{1}", outFileName, "msi"); command.Append(string.Format("-out \"{0}\"", targetFileName)); command.Append(string.Format(" -pdbout \"{0}.{1}\"", outFileName, "wixpdb")); } command.Append(" -cultures:null"); // Подавление ICE: // warning LGHT1076 : ICE17: ListView: 'SELECTEDDATABASE' for Control: 'DatabasesListView' of Dialog: 'DatabasesListDlg' not found in ListView table. // error LGHT0204 : ICE38: Component ... installs to user profile. It's KeyPath registry key must fall under HKCU. // error LGHT0204 : ICE43: Component ... has non-advertised shortcuts. It's KeyPath registry key should fall under HKCU. // error LGHT0204 : ICE57: Component ... has both per-user and per-machine data with a per-machine KeyPath. string[] ices = context.ApplicationSettings.SuppressIce.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); foreach (string ice in ices) command.Append(" -sice:" + ice); command.Append(string.Format(" -ext \"{0}\"", Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.UIExtensionFileName))); command.Append(string.Format(" -ext \"{0}\"", Path.Combine(context.ApplicationInfo.ApplicationDirectory, "WixSTExtension.dll"))); command.Append(" -cultures:ru-RU;en-US"); // Берем файлы с расширением .wxs и меняем на .wixobj. foreach (var name in templates.Where(v => v.Value.EndsWith(".wxs")).Select(v => Path.ChangeExtension(v.Value, ".wixobj"))) { command.Append(@" " + Path.Combine(StoreDirectory, name)); } RunCommand(context, lightFileName, command.ToString()); context.BuildMessageWriteLine(string.Format("Создан файл: {0}", targetFileName), BuildMessageTypes.Information); // Если создаем wixout, то зархивируем его вместе с дополнительной информацией. if (mode == 0) { WixProductUpdateInfo updateInfo = WixProductUpdateInfo.Create(product, targetFileName, context.SourceStoreDirectory); updateInfo.Save(string.Format("{0}.{1}", outFileName, WixProductUpdateInfo.FilenameExtension), true); } } }
private void RunningCandle(IBuildContext context, CancellationTokenSource cts) { context.BuildMessageWriteLine(CandleDescription, BuildMessageTypes.Notification); // Путь к candle. string candleFileName = Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.CandleFileName); // Команда для выполнения. StringBuilder command = new StringBuilder(); // Если указываем путь в кавычках, то надо указать дополнительно обратный слеш, // это требование WIX. command.Append(string.Format("-out \"{0}\\\"", StoreDirectory.IncludeTrailingPathDelimiter())); command.Append(" -arch x86"); command.Append(string.Format(" -ext \"{0}\"", Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.UIExtensionFileName))); command.Append(string.Format(" -ext \"{0}\"", Path.Combine(context.ApplicationInfo.ApplicationDirectory, "WixSTExtension.dll"))); // Берем файлы с расширением .wxs. foreach (var name in templates.Where(v => v.Value.EndsWith(".wxs"))) { command.Append(string.Format(" \"{0}\"", Path.Combine(StoreDirectory, name.Value))); } RunCommand(context, candleFileName, command.ToString()); }
protected override void ProcessingTemplates(IBuildContext context, CancellationTokenSource cts) { if (context.OnlyCheck) return; wxsFiles.Clear(); string pathToTemplate = Path.Combine(StoreDirectory, mspTemplate); DeleteDevelopmentInfo(pathToTemplate); foreach (IWixElement element in product.RootElement.Items) { // Создадим имя файла: Имя_Продукта_v1.2.3.4_Имя_Патча_v1.2.3.5.wxs. string name = string.Format("{0}_v{1}.{2}.{3}.{4}_{5}_v{6}.{7}.{8}.{9}.wxs", product.TargetName.Replace(' ', '_'), product.BaseVersion.Major, product.BaseVersion.Minor, product.BaseVersion.Build, product.BaseVersion.Revision, element.Id, product.TargetVersion.Major, product.TargetVersion.Minor, product.TargetVersion.Build, product.TargetVersion.Revision); // Создаем в отдельной директории файл wxs для каждого Patch. string path = Path.Combine(StoreDirectory, element.Id); Directory.CreateDirectory(path); path = Path.Combine(path, name); File.Copy(pathToTemplate, path); // Дочерние элементы WixPatchComponentElement; var children = element.Items.OfType<WixPatchComponentElement>(); // Заполним содержимым. XElement xmlWix = XElement.Load(path); XElement xmlPatch = xmlWix.GetXElement("Patch"); // Сформируем список компонент. string componentsDesc = ""; foreach (WixPatchComponentElement c in children) componentsDesc += (componentsDesc == "" ? "" : ", ") + c.Id; string description = string.Format(@"Патч ""{0}"" версии {1}. Обновление до версии {2} компонент: {3}", product.BaseName, product.BaseVersion, product.TargetVersion, componentsDesc); xmlPatch.Attribute("Comments").Value = description; xmlPatch.Attribute("Description").Value = description; xmlPatch.Attribute("DisplayName").Value = string.Format(@"Патч ""{0}""", product.TargetName); xmlPatch.Attribute("Manufacturer").Value = product.TargetManufacturer; xmlPatch.Attribute("TargetProductName").Value = product.TargetName; XElement xmlPatchFamily = xmlPatch.GetXElement("PatchFamily"); // Важно, указываем новую версию. Эта версия ни как не связана с версией MSI, // только для msp. Но она должна быть больше чем у ранних MSP. Будем использовать // версию нового продукта. xmlPatchFamily.Attribute("Version").Value = product.TargetVersion; xmlPatchFamily.Attribute("ProductCode").Value = product.TargetId.ToString(); // Добавляем компоненты. // Если указаны все компоненты, то ничего не добавляем. Патч сформируется полным. // Если компоненты указаны не все, то добавим их. bool isFull = children.Select(v => v.Id).OrderBy(v => v). SequenceEqual(product.UpdateComponents.Select(v => v.Id).OrderBy(v => v)); if (!isFull) { foreach (WixPatchComponentElement component in children) { XElement xmlComponentRef = new XElement(XmlNameSpaceWIX + "ComponentRef", new XAttribute("Id", component.Id)); xmlPatchFamily.Add(xmlComponentRef); } } xmlWix.Save(path); // Добавим в список wxsFiles.Add(path); context.BuildMessageWriteLine(string.Format("Сформирован {0}.", path), BuildMessageTypes.Notification); } }
private string InternalCompilationAndBuild(IBuildContext context, CancellationTokenSource cts, string wxsFile) { // Команда для выполнения. StringBuilder command = new StringBuilder(); context.BuildMessageWriteLine("Компиляция и сборка файла: " + wxsFile, BuildMessageTypes.Notification); context.BuildMessageWriteLine(TorchDescription, BuildMessageTypes.Notification); // torch -p -xi "Сервер АСПО 1.0.1\Сервер АСПО.wixout" "Сервер АСПО 1.0.2\Сервер АСПО.wixout" -out Patch\Differences.wixmst string wixmstFileName = Path.ChangeExtension(wxsFile, ".wixmst"); command.Append("-p -xi"); command.Append(string.Format(" \"{0}\"", Path.Combine(context.SourceStoreDirectory, product.BasePath))); command.Append(string.Format(" \"{0}\"", Path.Combine(context.SourceStoreDirectory, product.TargetPath))); command.Append(string.Format(" -out \"{0}\"", wixmstFileName)); string torchFileName = Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.TorchFileName); RunCommand(context, torchFileName, command.ToString()); command.Clear(); context.BuildMessageWriteLine(CandleDescription, BuildMessageTypes.Notification); // candle Patch\Patch.wxs -out Patch\ command.Append(string.Format("\"{0}\"", wxsFile)); command.Append(string.Format(" -out \"{0}\\\"", Path.GetDirectoryName(wxsFile).IncludeTrailingPathDelimiter())); string candleFileName = Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.CandleFileName); RunCommand(context, candleFileName, command.ToString()); string wixmspFileName; command.Clear(); context.BuildMessageWriteLine(LightDescription, BuildMessageTypes.Notification); // light Patch\Patch.wixobj -out Patch\Patch.wixmsp command.Append(string.Format("\"{0}\"", Path.ChangeExtension(wxsFile, ".wixobj"))); command.Append(string.Format(" -out \"{0}\"", wixmspFileName = Path.ChangeExtension(wxsFile, ".wixmsp"))); string lightFileName = Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.LightFileName); RunCommand(context, lightFileName, command.ToString()); string mspFileName; command.Clear(); context.BuildMessageWriteLine(PyroDescription, BuildMessageTypes.Notification); // pyro Patch\Patch.wixmsp -t MyPatch Patch\Differences.wixmst -out Patch\Patch.msp command.Append(string.Format("\"{0}\"", wixmspFileName)); #warning AspoPatch Вставить в продукт. command.Append(string.Format(" -t AspoPatch \"{0}\"", wixmstFileName)); command.Append(string.Format(" -out \"{0}\"", mspFileName = Path.ChangeExtension(wxsFile, ".msp"))); string pyroFileName = Path.Combine(context.ApplicationSettings.WixToolsetPath, context.ApplicationSettings.PyroFileName); RunCommand(context, pyroFileName, command.ToString()); context.BuildMessageWriteLine("Файл " + mspFileName + " построен.", BuildMessageTypes.Notification); return mspFileName; }