Udostępnij za pośrednictwem


Jak program MSBuild kompiluje projekty

Jak działa program MSBuild? W tym artykule dowiesz się, jak program MSBuild przetwarza pliki projektu, niezależnie od tego, czy jest wywoływany z programu Visual Studio, czy z wiersza polecenia lub skryptu. Znajomość sposobu działania programu MSBuild może pomóc w lepszym diagnozowaniu problemów i lepszym dostosowaniu procesu kompilacji. W tym artykule opisano proces kompilacji i jest w dużej mierze stosowany do wszystkich typów projektów.

Kompletny proces kompilacji składa się z początkowego uruchamiania, oceny i wykonywania celów i zadań, które kompilują projekt. Oprócz tych danych wejściowych importy zewnętrzne definiują szczegóły procesu kompilacji, w tym zarówno standardowe importy , jak Microsoft.Common.targets i importy konfigurowalne przez użytkownika na poziomie rozwiązania lub projektu.

Uruchamianie

Program MSBuild może być wywoływany z programu Visual Studio za pośrednictwem modelu obiektów MSBuild w Microsoft.Build.dll lub przez wywołanie pliku wykonywalnego (MSBuild.exe lub dotnet build) bezpośrednio w wierszu polecenia lub w skrycie, takim jak w systemach ciągłej integracji. W obu przypadkach dane wejściowe wpływające na proces kompilacji obejmują plik projektu (lub obiekt projektu wewnętrzny w programie Visual Studio), prawdopodobnie plik rozwiązania, zmienne środowiskowe i przełączniki wiersza polecenia lub ich odpowiedniki modelu obiektów. Podczas fazy uruchamiania opcje wiersza polecenia lub odpowiedniki modelu obiektów są używane do konfigurowania ustawień programu MSBuild, takich jak konfigurowanie rejestratorów. Właściwości ustawione w wierszu polecenia przy użyciu przełącznika -property lub -p są ustawiane jako właściwości globalne, które zastępują wszystkie wartości ustawione w plikach projektu, mimo że pliki projektu są odczytywane później.

Następne sekcje dotyczą plików wejściowych, takich jak pliki rozwiązania lub pliki projektu.

Rozwiązania i projekty

Wystąpienia programu MSBuild mogą składać się z jednego projektu lub wielu projektów w ramach rozwiązania. Plik rozwiązania nie jest plikiem XML programu MSBuild, ale program MSBuild interpretuje go, aby znać wszystkie projekty wymagane do skompilowania dla danej konfiguracji i ustawień platformy. Gdy program MSBuild przetwarza te dane wejściowe XML, jest określany jako kompilacja rozwiązania. Ma on pewne rozszerzalne punkty, które umożliwiają uruchamianie czegoś w każdej kompilacji rozwiązania, ale ponieważ ta kompilacja jest oddzielnym uruchomieniem od poszczególnych kompilacji projektu, żadne ustawienia właściwości lub definicji docelowych z kompilacji rozwiązania nie są istotne dla każdej kompilacji projektu.

Aby dowiedzieć się, jak rozszerzyć kompilację rozwiązania, zobacz Dostosowywanie kompilacji rozwiązania.

Kompilacje programu Visual Studio a kompilacje MSBuild.exe

Istnieją pewne istotne różnice między kompilacją projektów w programie Visual Studio a bezpośrednie wywołanie programu MSBuild za pośrednictwem pliku wykonywalnego MSBuild lub użycie modelu obiektów MSBuild do uruchomienia kompilacji. Program Visual Studio zarządza kolejnością kompilacji projektu dla kompilacji programu Visual Studio; wywołuje program MSBuild tylko na poziomie pojedynczego projektu, a gdy tak, kilka właściwości logicznych (BuildingInsideVisualStudio, BuildProjectReferences) jest ustawianych, które znacząco wpływają na działanie programu MSBuild. W każdym projekcie wykonywanie odbywa się tak samo jak w przypadku wywołania za pomocą programu MSBuild, ale różnica występuje w przypadku przywoływanych projektów. W programie MSBuild, gdy wymagane są przywoływana dokumentacja projektów, faktycznie występuje kompilacja; oznacza to, że uruchamia zadania i narzędzia oraz generuje dane wyjściowe. Po znalezieniu przywoływana kompilacja programu Visual Studio program MSBuild zwraca tylko oczekiwane dane wyjściowe z przywoływana projektu; umożliwia programowi Visual Studio kontrolowanie kompilowania tych innych projektów. Program Visual Studio określa kolejność kompilacji i wywołania do programu MSBuild oddzielnie (zgodnie z potrzebami), wszystkie całkowicie pod kontrolą programu Visual Studio.

Inna różnica występuje, gdy program MSBuild jest wywoływany z plikiem rozwiązania, program MSBuild analizuje plik rozwiązania, tworzy standardowy plik wejściowy XML, ocenia go i wykonuje jako projekt. Kompilacja rozwiązania jest wykonywana przed każdym projektem. Podczas kompilowania z poziomu programu Visual Studio nic z tego nie dzieje się; Program MSBuild nigdy nie widzi pliku rozwiązania. W związku z tym dostosowywanie kompilacji rozwiązania (używane wcześniej. SolutionName.sln.targets i after. SolutionName.sln.targets) dotyczy tylko kompilacji opartych na MSBuild.exe, dotnet buildlub opartych na modelu obiektów, a nie kompilacji programu Visual Studio.

Zestawy SDK projektu

Funkcja zestawu SDK dla plików projektu MSBuild jest stosunkowo nowa. Przed tą zmianą pliki projektu jawnie zaimportowały pliki .targets i .props , które zdefiniowały proces kompilacji dla określonego typu projektu.

Projekty platformy .NET Core importują odpowiednią wersję zestawu .NET SDK. Zapoznaj się z omówieniem, zestawami SDK projektów platformy .NET Core i odwołaniem do właściwości.

Faza oceny

W tej sekcji omówiono sposób przetwarzania i analizowania tych plików wejściowych w celu utworzenia obiektów w pamięci, które określają, co zostanie skompilowane.

Celem fazy oceny jest utworzenie struktur obiektów w pamięci na podstawie wejściowych plików XML i środowiska lokalnego. Faza oceny składa się z sześciu przebiegów, które przetwarzają pliki wejściowe, takie jak pliki XML projektu lub importowane pliki XML, zazwyczaj nazywane plikami .props lub targets, w zależności od tego, czy ustawiają głównie właściwości, czy definiują obiekty docelowe kompilacji. Każda passa tworzy część obiektów w pamięci, które są później używane w fazie wykonywania do kompilowania projektów, ale nie występują rzeczywiste akcje kompilacji w fazie oceny. W ramach każdego przekazywania elementy są przetwarzane w kolejności ich wyświetlania.

Przebiegi w fazie oceny są następujące:

  • Ocena zmiennych środowiskowych
  • Ocena importu i właściwości
  • Ocena definicji elementów
  • Ocena elementów
  • Ocena elementów UsingTask
  • Ocena miejsc docelowych

Importy i właściwości są oceniane w tym samym przebiegu w sekwencji wyglądu, tak jak w przypadku rozszerzenia importu. W związku z tym ustawienia właściwości w wcześniej importowanych plikach są dostępne w późniejszych importowanych plikach.

Kolejność tych przebiegów ma znaczące konsekwencje i ważne jest, aby wiedzieć podczas dostosowywania pliku projektu. Zobacz Kolejność oceny właściwości i elementów.

Ocena zmiennych środowiskowych

W tej fazie zmienne środowiskowe są używane do ustawiania równoważnych właściwości. Na przykład zmienna środowiskowa PATH jest udostępniana jako właściwość $(PATH). Po uruchomieniu z wiersza polecenia lub skryptu środowisko poleceń jest używane normalnie, a po uruchomieniu z programu Visual Studio środowisko działa w czasie uruchamiania programu Visual Studio.

Ocena importu i właściwości

W tej fazie cały wejściowy kod XML jest odczytywany, w tym pliki projektu i cały łańcuch importu. Program MSBuild tworzy strukturę XML w pamięci, która reprezentuje kod XML projektu i wszystkie zaimportowane pliki. Obecnie właściwości, które nie znajdują się w obiektach docelowych, są oceniane i ustawiane.

W wyniku odczytu wszystkich plików wejściowych XML w programie MSBuild na wczesnym etapie procesu wszelkie zmiany tych danych wejściowych w procesie kompilacji nie mają wpływu na bieżącą kompilację.

Właściwości spoza dowolnego obiektu docelowego są obsługiwane inaczej niż właściwości w ramach obiektów docelowych. W tej fazie oceniane są tylko właściwości zdefiniowane poza dowolnym obiektem docelowym.

Ponieważ właściwości są przetwarzane w kolejności przekazywanej właściwości, właściwość w dowolnym momencie w danych wejściowych może uzyskać dostęp do wartości właściwości, które są wyświetlane wcześniej w danych wejściowych, ale nie właściwości, które pojawią się później.

Ponieważ właściwości są przetwarzane przed oceną elementów, nie można uzyskać dostępu do wartości dowolnego elementu podczas jakichkolwiek części przekazywanych właściwości.

Ocena definicji elementów

W tej fazie definicje elementów są interpretowane i tworzona jest reprezentacja w pamięci tych definicji.

Ocena elementów

Elementy zdefiniowane wewnątrz obiektu docelowego są obsługiwane inaczej niż elementy spoza dowolnego obiektu docelowego. W tej fazie przetwarzane są elementy spoza dowolnego obiektu docelowego i skojarzonych z nimi metadanych. Metadane ustawiane według definicji elementów są zastępowane przez metadane ustawione dla elementów. Ponieważ elementy są przetwarzane w kolejności ich wyświetlania, można odwoływać się do elementów, które zostały zdefiniowane wcześniej, ale nie tych, które pojawiają się później. Ponieważ przekazywane elementy są po przekazaniu właściwości, elementy mogą uzyskać dostęp do dowolnej właściwości, jeśli zostały zdefiniowane poza obiektami docelowymi, niezależnie od tego, czy definicja właściwości jest wyświetlana później.

Ocena UsingTask elementów

W tej fazie elementy UsingTask są odczytywane, a zadania są deklarowane do późniejszego użycia w fazie wykonywania.

Ocena miejsc docelowych

W tej fazie wszystkie struktury obiektów docelowych są tworzone w pamięci, w ramach przygotowań do wykonania. Nie ma rzeczywistego wykonania.

Faza wykonywania

W fazie wykonywania obiekty docelowe są uporządkowane i uruchamiane, a wszystkie zadania są wykonywane. Najpierw właściwości i elementy zdefiniowane w ramach elementów docelowych są oceniane razem w jednej fazie w kolejności, w jakiej się pojawiają. Kolejność przetwarzania różni się w szczególności od sposobu przetwarzania właściwości i elementów, które nie znajdują się w obiekcie docelowym: najpierw wszystkie właściwości, a następnie wszystkie elementy w osobnych przejściach. Zmiany właściwości i elementów w obiekcie docelowym można zaobserwować po zmianie obiektu docelowego.

Kolejność kompilowania obiektów docelowych

W jednym projekcie obiekty docelowe są wykonywane szeregowo. Głównym problemem jest określenie kolejności kompilowania wszystkich elementów w taki sposób, aby zależności były używane do kompilowania obiektów docelowych w odpowiedniej kolejności.

Docelowa kolejność kompilacji jest określana przez użycie atrybutów BeforeTargets, DependsOnTargetsi AfterTargets w każdym obiekcie docelowym. Kolejność późniejszych obiektów docelowych może mieć wpływ podczas wykonywania wcześniejszego obiektu docelowego, jeśli wcześniejsza wartość docelowa modyfikuje właściwość, do którego odwołuje się ten atrybut.

Reguły porządkowania opisano w temacie Określanie docelowej kolejności kompilacji. Proces jest określany przez strukturę stosu zawierającą obiekty docelowe do skompilowania. Element docelowy w górnej części tego zadania rozpoczyna wykonywanie, a jeśli zależy od czegokolwiek innego, te obiekty docelowe są wypychane na górę stosu i zaczynają wykonywać operacje. Gdy obiekt docelowy nie ma żadnych zależności, jest wykonywany do ukończenia, a jego nadrzędny element docelowy zostanie wznowiony.

Informacje o projekcie

Istnieją dwie ścieżki kodu, które program MSBuild może wykonać, normalny, opisany tutaj i opcję grafu opisaną w następnej sekcji.

Poszczególne projekty określają zależność od innych projektów za pośrednictwem ProjectReference elementów. Gdy projekt w górnej części stosu rozpoczyna tworzenie, osiąga punkt, w którym ResolveProjectReferences jest wykonywany element docelowy, standardowy element docelowy zdefiniowany w typowych plikach docelowych.

ResolveProjectReferences wywołuje zadanie MSBuild z danymi wejściowymi ProjectReference elementów w celu pobrania danych wyjściowych. ProjectReference Elementy są przekształcane na elementy lokalne, takie jak Reference. Faza wykonywania programu MSBuild dla bieżącego projektu wstrzymuje się, gdy faza wykonywania rozpoczyna przetwarzanie przywoływanego projektu (faza oceny jest wykonywana najpierw zgodnie z potrzebami). Przywołyżony projekt kompiluje się tylko po rozpoczęciu kompilowania projektu zależnego, dlatego tworzy drzewo kompilowania projektów.

Program Visual Studio umożliwia tworzenie zależności projektu w plikach rozwiązania (.sln). Zależności są określone w pliku rozwiązania i są przestrzegane tylko podczas tworzenia rozwiązania lub podczas kompilowania wewnątrz programu Visual Studio. Jeśli tworzysz pojedynczy projekt, ten typ zależności jest ignorowany. Odwołania do rozwiązań są przekształcane przez program MSBuild na ProjectReference elementy, a następnie są traktowane w ten sam sposób.

Opcja grafu

Jeśli określisz przełącznik kompilacji grafu (-graphBuild lub -graph), ProjectReference staje się pierwszą klasą koncepcji używaną przez program MSBuild. Program MSBuild przeanalizuje wszystkie projekty i skonstruuje wykres kolejności kompilacji, rzeczywisty graf zależności projektów, który następnie przechodzi w celu określenia kolejności kompilacji. Podobnie jak w przypadku obiektów docelowych w poszczególnych projektach, program MSBuild zapewnia, że przywołyne projekty są kompilowane po projektach, od których zależą.

Równoległego

W przypadku korzystania z obsługi wieloprocesorowej (-maxCpuCount lub -m przełącznika) program MSBuild tworzy węzły, które są procesami MSBuild korzystającymi z dostępnych rdzeni procesora CPU. Każdy projekt jest przesyłany do dostępnego węzła. W węźle poszczególne kompilacje projektu są wykonywane szeregowo.

Zadania mogą być włączone na potrzeby wykonywania równoległego, ustawiając zmienną BuildInParallellogiczną , która jest ustawiana zgodnie z wartością $(BuildInParallel) właściwości w programie MSBuild. W przypadku zadań, które są włączone do wykonywania równoległego, harmonogram pracy zarządza węzłami i przypisuje pracę do węzłów.

Zobacz Tworzenie wielu projektów równolegle z programem MSBuild

Importy standardowe

Obiekty Microsoft.Common.props i Microsoft.Common.targets są importowane przez pliki projektów .NET (jawnie lub niejawnie w projektach w stylu zestawu SDK) i znajdują się w folderze MSBuild\Current\bin w instalacji programu Visual Studio. Projekty języka C++ mają własną hierarchię importu; Zobacz MsBuild Internals for C++ projects (Wewnętrzne elementy MSBuild dla projektów języka C++).

Plik Microsoft.Common.props ustawia wartości domyślne, które można zastąpić. Jest importowany (jawnie lub niejawnie) na początku pliku projektu. W ten sposób ustawienia projektu są wyświetlane po wartościach domyślnych, aby je zastąpić.

Plik Microsoft.Common.targets i pliki docelowe, które importuje, definiują standardowy proces kompilacji dla projektów .NET. Udostępnia również punkty rozszerzenia, których można użyć do dostosowania kompilacji.

W implementacji obiekty Microsoft.Common.targets to cienka otoka, która importuje obiekty Microsoft.Common.CurrentVersion.targets. Ten plik zawiera ustawienia właściwości standardowych i definiuje rzeczywiste obiekty docelowe definiujące proces kompilacji. Element docelowy Build jest zdefiniowany w tym miejscu, ale faktycznie sam jest pusty. Build Jednak element docelowy zawiera DependsOnTargets atrybut, który określa poszczególne elementy docelowe, które składają się na rzeczywiste kroki kompilacji, które są BeforeBuild, CoreBuildi AfterBuild. Element docelowy Build jest definiowany w następujący sposób:

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

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

BeforeBuild i AfterBuild są punktami rozszerzenia. Są one puste w pliku Microsoft.Common.CurrentVersion.targets , ale projekty mogą udostępniać własne BeforeBuild i AfterBuild docelowe zadania, które należy wykonać przed lub po głównym procesie kompilacji. AfterBuildpolecenie jest uruchamiane przed obiektem docelowym no-op, , ponieważ AfterBuild pojawia się w atrybucie DependsOnTargets Build w obiekcie docelowym, ale występuje po CoreBuild. Build

Element docelowy CoreBuild zawiera wywołania narzędzi kompilacji w następujący sposób:

  <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>

W poniższej tabeli opisano te cele; niektóre cele mają zastosowanie tylko do niektórych typów projektów.

Obiekt docelowy opis
BuildOnly Ustawienia Ustawienia tylko dla rzeczywistych kompilacji, a nie wtedy, gdy program MSBuild jest wywoływany podczas ładowania projektu przez program Visual Studio.
PrepareForBuild Przygotowywanie wymagań wstępnych do kompilowania
PreBuildEvent Punkt rozszerzenia dla projektów do definiowania zadań do wykonania przed kompilacją
ResolveProjectReferences Analizowanie zależności projektu i kompilowanie przywołynych projektów
ResolveAssemblyReferences Znajdź zestawy, do których odwołuje się odwołanie.
ResolveReferences Składa się z elementów ResolveProjectReferences i ResolveAssemblyReferences , aby znaleźć wszystkie zależności
PrepareResources Przetwarzanie plików zasobów
ResolveKeySource Rozwiąż klucz silnej nazwy używany do podpisywania zestawu i certyfikatu używanego do podpisywania manifestów ClickOnce .
Kompiluj Wywołuje kompilator
ExportWindowsMDFile Wygeneruj plik WinMD z plików WinMDModule wygenerowanych przez kompilator.
UnmanagedUnregistration Usuwanie/czyszczenie wpisów rejestru międzyoperacyjności MODELU COM z poprzedniej kompilacji
GenerateSerializationAssemblies Wygeneruj zestaw serializacji XML przy użyciu sgen.exe.
CreateSatelliteAssemblies Utwórz jeden zestaw satelitarny dla każdej unikatowej kultury w zasobach.
Generowanie manifestów Generuje manifesty aplikacji ClickOnce i wdrożenia lub manifestu natywnego.
GetTargetPath Zwróć element zawierający produkt kompilacji (plik wykonywalny lub zestaw) dla tego projektu z metadanymi.
PrepareForRun Skopiuj dane wyjściowe kompilacji do katalogu końcowego, jeśli zostały zmienione.
UnmanagedRegistration Ustawianie wpisów rejestru dla międzyoperacyjności MODELU COM
IncrementalClean Usuń pliki utworzone w poprzedniej kompilacji, ale nie zostały utworzone w bieżącej kompilacji. Jest to niezbędne do pracy Clean w kompilacjach przyrostowych.
PostBuildEvent Punkt rozszerzenia dla projektów do definiowania zadań do uruchomienia po kompilacji

Wiele obiektów docelowych w poprzedniej tabeli znajduje się w importach specyficznych dla języka, takich jak Microsoft.CSharp.targets. Ten plik definiuje kroki w standardowym procesie kompilacji specyficznym dla projektów .NET języka C#. Na przykład zawiera element docelowy Compile , który faktycznie wywołuje kompilator języka C#.

Importowanie konfigurowalne przez użytkownika

Oprócz standardowych importów istnieje kilka importów, które można dodać, aby dostosować proces kompilacji.

  • Directory.Build.props
  • Directory.Build.targets

Te pliki są odczytywane przez standardowe importy dla wszystkich projektów w dowolnym podfolderze. Zazwyczaj jest to na poziomie rozwiązania w celu kontrolowania wszystkich projektów w rozwiązaniu, ale może być również wyższe w systemie plików, aż do katalogu głównego dysku.

Plik Directory.Build.props jest importowany przez plik Microsoft.Common.props, więc właściwości zdefiniowane w pliku projektu są dostępne. Można je ponownie zdefiniować w pliku projektu, aby dostosować wartości dla poszczególnych projektów. Plik Directory.Build.targets jest odczytywany po pliku projektu. Zazwyczaj zawiera ona obiekty docelowe, ale tutaj można również zdefiniować właściwości, których nie chcesz ponownie definiować w poszczególnych projektach.

Dostosowania w pliku projektu

Program Visual Studio aktualizuje pliki projektu podczas wprowadzania zmian w Eksplorator rozwiązań, w oknie Właściwości lub we właściwościach projektu, ale możesz również wprowadzić własne zmiany, bezpośrednio edytując plik projektu.

Wiele zachowań kompilacji można skonfigurować, ustawiając właściwości MSBuild w pliku projektu dla ustawień lokalnych dla projektu lub zgodnie z opisem w poprzedniej sekcji, tworząc plik Directory.Build.props , aby ustawić właściwości globalnie dla całych folderów projektów i rozwiązań. W przypadku kompilacji ad hoc w wierszu polecenia lub skryptów można również użyć /p opcji w wierszu polecenia, aby ustawić właściwości dla określonego wywołania MSBuild. Zobacz Typowe właściwości projektu MSBuild, aby uzyskać informacje o właściwościach, które można ustawić.