Как MSBuild выполняет сборку проектов

Как работает MSBuild? В этой статье вы узнаете, как MSBuild обрабатывает файлы проекта, вызываемые из Visual Studio, командной строки или скрипта. Знание работы MSBuild поможет вам лучше диагностировать проблемы и улучшить настройку процесса сборки. В этой статье описывается процесс сборки, который в основном применим ко всем типам проектов.

Полный процесс сборки состоит из начального запуска, оценки и выполнения целей и задач, реализующих сборку проекта. Помимо этих входных данных на особенности процесса сборки влияют внешние импорты, включая стандартные импорты, такие как Microsoft.Common.targets и настраиваемые пользователем импорты на уровне решения или проекта.

Запуск

MSBuild можно вызвать из Visual Studio через объектную модель MSBuild в Microsoft.Build.dll или вызвать исполняемый файлMSBuild.exe (или dotnet build) непосредственно в командной строке или в скрипте, например в системах CI. В любом случае входные данные, влияющие на процесс сборки, включают файл проекта (или объект проекта, внутренний для Visual Studio), возможно, файл решения, переменные среды и параметры командной строки или их эквиваленты объектной модели. На этапе запуска параметры командной строки или эквиваленты объектной модели используются для настройки параметров MSBuild, таких как параметры средств ведения журнала. Свойства, заданные в командной строке с помощью параметра -property или -p, задаются как глобальные свойства, которые переопределяют все значения, которые будут установлены в файлах проекта, даже если файлы проекта считываются позже.

В следующих разделах содержатся сведения о входных файлах, таких как файлы решений или файлы проектов.

Проекты и решения

Экземпляры MSBuild могут состоять из одного проекта или нескольких проектов в составе решения. Файл решения не является XML-файлом MSBuild, но MSBuild интерпретирует его для получения сведений о проектах, которые должны быть созданы для заданной конфигурации и параметров платформы. Когда MSBuild обрабатывает эти входные данные XML, это называется сборкой решения. Он имеет несколько расширяемых точек, которые позволяют выполнять какие-либо действия во всех сборках решения, но поскольку эта сборка является отдельным запуском из сборок отдельных проектов, никакие настройки свойств или целевых определений из сборки решения не важны для каждой отдельной сборки проекта.

Дополнительные сведения о том, как расширить сборку решения, можно найти в разделе Настройка сборки решения.

Сборки Visual Studio и сборки MSBuild.exe

Имеется ряд существенных различий между сборкой проектов в Visual Studio и при вызове MSBuild напрямую либо с помощью исполняемого файла MSBuild, либо при использовании объектной модели MSBuild для запуска сборки. Visual Studio управляет порядком сборки проекта для сборок Visual Studio. Он вызывает только MSBuild на уровне отдельного проекта. Когда это происходит, задаются несколько логических свойств (BuildingInsideVisualStudio и BuildProjectReferences), которые значительно влияют на то, что делает MSBuild. Внутри каждого проекта выполнение происходит так же, как при вызове с помощью MSBuild, но отличается для проектов, на которые имеются ссылки. В MSBuild, когда требуются проекты, на которые имеются ссылки, возникает фактическая сборка (то есть она запускает задачи и средства и создает выходные данные). Когда сборка Visual Studio находит проект, на который указывает ссылка, MSBuild возвращает ожидаемые выходные данные из упоминаемого проекта. Это позволяет Visual Studio управлять сборкой этих проектов. Visual Studio определяет порядок сборки и вызывает MSBuild отдельно (по мере необходимости), все это полностью находится под контролем Visual Studio.

Другое различие возникает при вызове MSBuild файлом решения, MSBuild анализирует файл решения, создает стандартный входной XML-файл, оценивает его и выполняет в качестве проекта. Сборка решения выполняется перед любым проектом. При сборке из Visual Studio ничего этого не происходит; MSBuild не видит файл решения. В результате настройка сборки решения (используется раньше. SolutionName.sln.targets и после. SolutionName.sln.targets) применяется только к MSBuild.exe или dotnet buildобъектно-управляемым сборкам, а не к сборкам Visual Studio.

Пакеты SDK для проектов

Функция пакета SDK для файлов проекта MSBuild относительно нова. До этого изменения файлы проекта явным образом импортировали файлы TARGETS и PROPS, в которых определен процесс сборки для конкретного типа проекта.

Проекты .NET Core импортируют соответствующую версию пакета SDK для .NET. См. статью Пакеты SDK для проектов .NET Core и ссылку на сведения о свойствах.

Этап оценки

В этом разделе описывается обработка и анализ этих входных файлов для создания объектов в памяти, определяющих, что будет построено.

Целью этапа оценки является создание в памяти структур объектов, основанных на входных XML-файлах и локальной среде. Фаза оценки состоит из шести этапов обработки входных файлов, таких как XML-файлы проекта и (или) импортированные XML-файлы, которые обычно называются файлами PROPS или TARGETS в зависимости от того, задают ли они свойства или определяют целевые объекты сборки. Каждый проход создает часть объектов в памяти, которые позже используются на этапе выполнения для построения проектов, но на этапе оценки фактические действия сборки не выполняются. Внутри каждого прохода элементы обрабатываются в том порядке, в котором они отображаются.

На этапе оценки передаются следующие этапы.

  • Оценка переменных среды
  • Оценка импорта и свойств
  • Оценка определений элементов.
  • Оценка элементов
  • Оценка элементов UsingTask.
  • Оценка целевых объектов

Импорт и свойства вычисляются в той же последовательности внешнего вида, как если бы импорт был развернут на месте. Таким образом, параметры свойств в ранее импортированных файлах доступны в последующих импортированных файлах.

Порядок этих проходов имеет значительные последствия. Это важно помнить при настройке файла проекта. См. раздел Порядок оценки свойств и элементов.

Оценка переменных среды

На этом этапе переменные среды используются для установки эквивалентных свойств. Например, переменная среды PATH становится доступной как свойство $(PATH). При запуске из командной строки или скрипта используется среда команд в обычном режиме. При запуске из Visual Studio используется среда, которая запускается с Visual Studio.

Оценка импорта и свойств

На этом этапе считывается весь входной XML-файл, включая файлы проекта и всю цепочку импорта. MSBuild создает XML-структуру в памяти, которая представляет XML-код проекта и все импортированные файлы. В настоящее время свойства, которые не находятся в целевых объектах, оцениваются и настраиваются.

Как следствие того, что MSBuild считывает все входные XML-файлы на ранних этапах процесса, любые изменения этих входных данных в процессе сборки не влияют на текущую сборку.

Свойства за пределами любого целевого объекта обрабатываются не так, как свойства внутри целевых объектов. На этом этапе оцениваются только свойства, определенные за пределами любого целевого объекта.

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

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

Оценка определений элементов.

На этом этапе интерпретируются определения элементов и создается представление этих определений в памяти.

Оценка элементов

Элементы, определенные внутри целевого объекта, обрабатываются не так, как элементы за пределами целевого объекта. На этом этапе обрабатываются элементы за пределами целевого объекта и связанные с ними метаданные. Метаданные, заданные определениями элементов, переопределяются метаданными, заданными для элементов. Так как элементы обрабатываются в том порядке, в котором они отображаются, можно ссылаться на элементы, которые были определены ранее, но не на элементы, которые появятся в дальнейшем. По причине того, что элементы передаются после передачи свойств, элементы могут получить доступ к любому свойству, если оно определено за пределами целевых объектов, независимо от того, появляется ли определение свойства позже.

Оценка элементов UsingTask

На этом этапе элементы UsingTask считываются, а задачи объявляются для последующего использования на этапе выполнения.

Оценка целевых объектов

На этом этапе все структуры целевых объектов создаются в памяти в процессе подготовки к выполнению. Фактическое выполнение не происходит.

Этап выполнения

На этапе выполнения целевые объекты упорядочиваются и выполняются вместе с задачами. Но сначала свойства и элементы, определенные в рамках целевых объектов, оцениваются в отношении друг друга в порядке их расположения в рамках одного этапа. Порядок обработки существенно отличается от обработки свойств и элементов, которые не находятся в целевом объекте: сначала все свойства, а затем все элементы в отдельных процессах передачи. Изменения свойств и элементов в целевом объекте можно наблюдать после целевого объекта, в котором они были изменены.

Порядок сборки целевого объекта

В одном проекте целевые объекты выполняются последовательно. Главный вопрос заключается в том, как определить порядок сборки всех элементов, чтобы зависимости использовались для построения целевых объектов в правильном порядке.

Порядок сборки целевого объекта определяется использованием атрибутов BeforeTargets, DependsOnTargets и AfterTargets для каждого целевого объекта. Порядок последующих целевых объектов может зависеть от выполнения более раннего целевого объекта, если предыдущий объект изменяет свойство, на которое имеются ссылки в этих атрибутах.

Правила упорядочивания описаны в разделе Определение порядка сборки целевых объектов. Процесс определяется структурой стека, содержащей целевые объекты для сборки. Целевой объект в верхней части этой задачи начинает выполнение, и если он зависит от какого-либо другого, эти целевые объекты помещаются в начало стека и начинают выполнение. При наличии целевого объекта без зависимостей он выполняется до завершения и его родительский целевой объект возобновляется.

Ссылки на проект

Существует два пути кода, которые MSBuild может использовать: обычный, описанный здесь, и параметр graph, описанный в следующем разделе.

Отдельные проекты задают свои зависимости от других проектов с помощью элементов ProjectReference. Когда проект в верхней части стека начинает сборку, он достигает точки, в которой выполняется целевой объект ResolveProjectReferences, стандартный целевой объект, определенный в общих целевых файлах.

ResolveProjectReferences вызывает задачу MSBuild с входными данными элементов ProjectReference, чтобы получить выходные данные. Элементы ProjectReference преобразуются в локальные элементы, такие как Reference. Этапы выполнения MSBuild для текущего проекта приостанавливаются, когда на этапе выполнения начинается обработка проекта, на который указывает ссылка (этап оценки выполняется в первую очередь по мере необходимости). Проект, на который указывает ссылка, создается только после начала выполнения сборки зависимого проекта и поэтому создает дерево построения проектов.

Visual Studio позволяет создавать зависимости проектов в файлах решения (SLN). Зависимости указываются в файле решения и учитываются только при создании решения или при построении в Visual Studio. При построении одного проекта зависимость этого типа игнорируется. Ссылки на решения преобразуются в элементы ProjectReference, а затем обрабатываются аналогичным образом.

Параметр Graph

Если указать параметр построения Graph (-graphBuild или -graph), ProjectReference станет концепцией первого класса, используемой MSBuild. MSBuild будет анализировать все проекты и создавать диаграмму порядка сборки, фактический граф зависимостей проектов, который затем обрабатывается для определения порядка сборки. Как и в случае с целевыми объектами в отдельных проектах, MSBuild гарантирует, что проекты со ссылками создаются после проектов, от которых они зависят.

Параллельное выполнение

Если используется многопроцессорная поддержка (параметры -maxCpuCount или -m), MSBuild создает узлы, которые являются процессами MSBuild, которые используют доступные ядра ЦП. Каждый проект отправляется на доступный узел. В пределах узла отдельные сборки проекта выполняются последовательно.

Задачи можно выполнять параллельно, указав логическую переменную BuildInParallel, которая задается в соответствии со значением свойства $(BuildInParallel) в MSBuild. В случае задач, для которых разрешено параллельное выполнение, планировщик работы управляет узлами и назначает им работу.

См. статью Параллельное построение нескольких проектов с помощью MSBuild.

Стандартные импорты

Microsoft. Common.props и Microsoft. Common.targets импортируются файлами проекта .NET (явно или неявно в проектах в стиле SDK) и находятся в папке MSBuild\Current\bin в установке Visual Studio. Проекты C++ имеют свою собственную иерархию импорта. См. статью Внутренние компоненты MSBuild для проектов C++.

Файл Microsoft.Common.props задает значения по умолчанию, которые можно переопределить. Он импортируется (явно или неявно) в начале файла проекта. Таким образом, параметры проекта будут отображаться после значений по умолчанию, чтобы переопределять их.

Файл Microsoft.Common.targets и целевые файлы, которые он импортирует, определяют стандартный процесс сборки для проектов .NET. Он также предоставляет точки расширения, которые можно использовать для настройки сборки.

В реализации Microsoft.Common.targets является тонкой оболочкой, которая импортирует Microsoft.Common.CurrentVersion.targets. Этот файл содержит параметры стандартных свойств и указывает фактические целевые объекты, определяющие процесс сборки. Здесь определяется целевой объект Build, но фактически он пуст. Однако целевой объект Build содержит атрибут DependsOnTargets, указывающий отдельные целевые объекты, которые составляют фактические этапы сборки, такие как BeforeBuild, CoreBuild и AfterBuild. Целевой объект Build определяется следующим образом:

  <PropertyGroup>
    <BuildDependsOn>
      BeforeBuild;
      CoreBuild;
      AfterBuild
    </BuildDependsOn>
  </PropertyGroup>

  <Target
      Name="Build"
      Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
      DependsOnTargets="$(BuildDependsOn)"
      Returns="@(TargetPathWithTargetPlatformMoniker)" />

BeforeBuild и AfterBuild являются точками расширения. В файле Microsoft.Common.CurrentVersion.targets они пусты, но проекты могут предоставлять собственные целевые объекты BeforeBuild и AfterBuild с задачами, которые должны быть выполнены до или после главного процесса сборки. AfterBuild выполняется перед целевым объектом, Build, так как AfterBuild отображается в атрибуте DependsOnTargets целевого объекта Build, но выполняется после CoreBuild.

Целевой объект CoreBuild содержит вызовы средств сборки следующим образом:

  <PropertyGroup>
    <CoreBuildDependsOn>
      BuildOnlySettings;
      PrepareForBuild;
      PreBuildEvent;
      ResolveReferences;
      PrepareResources;
      ResolveKeySource;
      Compile;
      ExportWindowsMDFile;
      UnmanagedUnregistration;
      GenerateSerializationAssemblies;
      CreateSatelliteAssemblies;
      GenerateManifests;
      GetTargetPath;
      PrepareForRun;
      UnmanagedRegistration;
      IncrementalClean;
      PostBuildEvent
    </CoreBuildDependsOn>
  </PropertyGroup>
  <Target
      Name="CoreBuild"
      DependsOnTargets="$(CoreBuildDependsOn)">

    <OnError ExecuteTargets="_TimeStampAfterCompile;PostBuildEvent" Condition="'$(RunPostBuildEvent)'=='Always' or '$(RunPostBuildEvent)'=='OnOutputUpdated'"/>
    <OnError ExecuteTargets="_CleanRecordFileWrites"/>

  </Target>

Эти целевые объекты описаны в следующей таблице. Некоторые целевые объекты применимы только к определенным типам проектов.

Назначение Description
BuildOnlySettings Параметры только для реальных сборок, а не для тех случаев, когда MSBuild вызывается при загрузке проекта Visual Studio.
PrepareForBuild Подготовка необходимых компонентов для сборки.
PreBuildEvent Точка расширения проектов для определения задач, выполняемых перед сборкой.
ResolveProjectReferences Анализ зависимостей проекта и сборка проектов, на которые имеются ссылки.
ResolveAssemblyReferences Нахождение сборок, на которые указывают ссылки.
ResolveReferences Состоит из ResolveProjectReferences и ResolveAssemblyReferences для поиска всех зависимостей.
PrepareResources Файлы ресурсов процесса.
ResolveKeySource Разрешает ключ строгого имени, используемый для подписания сборки, и сертификат, используемый для подписи манифестов ClickOnce.
Компилировать Вызывает компилятор.
ExportWindowsMDFile Создает файл WinMD из файлов WinMDModule, сгенерированных компилятором.
UnmanagedUnregistration Удаляет или очищает записи реестра СOM-взаимодействия из предыдущей сборки.
GenerateSerializationAssemblies Создает сборку XML-сериализации с помощью SGen.exe.
CreateSatelliteAssemblies Создает одну вспомогательную сборку для каждого уникального языка и региональных параметров в ресурсах.
GenerateManifests Создает манифест приложения и развертывания ClickOnce или собственный манифест.
GetTargetPath Возвращает элемент, содержащий продукт сборки (исполняемый файл или сборку) для этого проекта с метаданными.
PrepareForRun Копирует выходные данные сборки в окончательный каталог, если они были изменены.
UnmanagedRegistration Настраивает записи реестра для COM-взаимодействия.
IncrementalClean Удаляет файлы, созданные в предыдущей сборке, но не созданные в текущей. Это необходимо для того, чтобы Clean работал в инкрементных сборках.
PostBuildEvent Точка расширения проектов для определения задач, выполняемых после сборки.

Многие из целевых объектов в предыдущей таблице можно найти в импортах для конкретных языков, например Microsoft.CSharp.targets. Этот файл определяет шаги в стандартном процессе сборки, характерном для проектов C# .NET. Например, он содержит целевой объект Compile, который фактически вызывает компилятор C#.

Настраиваемые пользователем импорты

В дополнение к стандартным операциям импорта существует несколько импортов, которые можно добавить для настройки процесса сборки.

  • Directory.Build.Props
  • Directory.Build.targets

Эти файлы считываются стандартными импортами для всех проектов в любой вложенной папке. Это обычно происходит на уровне решения для настройки управления всеми проектами в решении, но также может располагаться выше в файловой системе, вплоть до корневого каталога диска.

Файл Directory.Build.props импортируется с помощью Microsoft.Common.props, поэтому в файле проекта доступны определенные свойства. Их можно переопределить в файле проекта для настройки значений на уровне каждого проекта. Файл Directory.Build.targets считывается в файле после файла проекта. Обычно он содержит целевые объекты, но здесь также можно определить свойства, которые не требуется переопределять для отдельных проектов.

Настройки в файле проекта

Visual Studio обновляет файлы проекта при внесении изменений в обозревателе решений, окнах Свойства или Свойства проекта, но вносить собственные изменения можно также, непосредственно редактируя файл проекта.

Многие модели поведения сборки можно настроить, установив свойства MSBuild либо в файле проекта для локальных настроек проекта, либо, как упоминалось в предыдущем разделе, создав файл Directory.Build.props для глобального задания свойств целых папок проектов и решений. Для нерегламентированных сборок в командной строке или скриптах можно также использовать параметр /p в командной строке, чтобы задать свойства для конкретного вызова MSBuild. Сведения о свойствах, которые можно задать, см. в статье Общие свойства проектов MSBuild.