Grundlegendes zum Buildprozess
von Jason Lee
Dieses Thema enthält eine exemplarische Vorgehensweise für einen Build- und Bereitstellungsprozess auf Unternehmensniveau. Der in diesem Thema beschriebene Ansatz verwendet benutzerdefinierte Microsoft-Build-Engine -Projektdateien (MSBuild), um eine differenzierte Kontrolle über jeden Aspekt des Prozesses zu ermöglichen. In den Projektdateien werden benutzerdefinierte MSBuild-Ziele verwendet, um Bereitstellungsprogramme wie das Internetinformationsdienste -Webbereitstellungstool (IIS) (MSDeploy.exe) und das Datenbankbereitstellungsprogramm VSDBCMD.exe auszuführen.
Hinweis
Im vorherigen Thema Grundlegendes zur Projektdatei wurden die wichtigsten Komponenten einer MSBuild-Projektdatei beschrieben und das Konzept geteilter Projektdateien eingeführt, um die Bereitstellung in mehreren Zielumgebungen zu unterstützen. Wenn Sie mit diesen Konzepten noch nicht vertraut sind, sollten Sie grundlegende Informationen zur Projektdatei lesen, bevor Sie dieses Thema durcharbeiten.
Dieses Thema ist Teil einer Reihe von Tutorials, die sich auf die Unternehmensbereitstellungsanforderungen eines fiktiven Unternehmens namens Fabrikam, Inc. beziehen. In dieser Tutorialreihe wird eine Beispiellösung – die Contact Manager-Lösung – verwendet, um eine Webanwendung mit einem realistischen Maß an Komplexität darzustellen, einschließlich einer ASP.NET MVC 3-Anwendung, einem WCF-Dienst (Windows Communication Foundation) und einem Datenbankprojekt.
Die Bereitstellungsmethode im Mittelpunkt dieser Tutorials basiert auf dem unter Grundlegendes zur Projektdatei beschriebenen Ansatz für geteilte Projektdateien, bei dem der Buildprozess von zwei Projektdateien gesteuert wird– eine mit Buildanweisungen, die für jede Zielumgebung gelten, und eine mit umgebungsspezifischen Build- und Bereitstellungseinstellungen. Zur Buildzeit wird die umgebungsspezifische Projektdatei in die umgebungsunabhängige Projektdatei zusammengeführt, um einen vollständigen Satz von Buildanweisungen zu bilden.
Übersicht über Build und Bereitstellung
In der Contact Manager-Lösung steuern drei Dateien den Build- und Bereitstellungsprozess:
- Eine universelle Projektdatei (Publish.proj). Dies enthält Build- und Bereitstellungsanweisungen, die sich zwischen Zielumgebungen nicht ändern.
- Eine umgebungsspezifische Projektdatei (Env-Dev.proj). Dies enthält Build- und Bereitstellungseinstellungen, die für eine bestimmte Zielumgebung spezifisch sind. Beispielsweise können Sie die Datei Env-Dev.proj verwenden, um Einstellungen für eine Entwickler- oder Testumgebung bereitzustellen und eine alternative Datei mit dem Namen Env-Stage.proj zu erstellen, um Einstellungen für eine Stagingumgebung bereitzustellen.
- Eine Befehlsdatei (Publish-Dev.cmd). Dies enthält einen MSBuild.exe-Befehl, der angibt, welche Projektdateien sie ausführen möchten. Sie können eine Befehlsdatei für jede Zielumgebung erstellen, in der jede Datei einen MSBuild.exe-Befehl enthält, der eine andere umgebungsspezifische Projektdatei angibt. Dadurch kann der Entwickler die Bereitstellung in verschiedenen Umgebungen einfach durch Ausführen der entsprechenden Befehlsdatei durchführen.
In der Beispiellösung finden Sie diese drei Dateien im Projektmappenordner Veröffentlichen.
Bevor Sie sich diese Dateien genauer ansehen, sehen wir uns an, wie der gesamte Buildprozess funktioniert, wenn Sie diesen Ansatz verwenden. Auf hoher Ebene sieht der Build- und Bereitstellungsprozess wie folgt aus:
Zunächst werden die beiden Projektdateien – eine mit universellen Build- und Bereitstellungsanweisungen und eine mit umgebungsspezifischen Einstellungen – in einer einzelnen Projektdatei zusammengeführt. MSBuild verarbeitet dann die Anweisungen in der Projektdatei. Es erstellt jedes der Projekte in der Projektmappe unter Verwendung der Projektdatei für jedes Projekt. Anschließend werden andere Tools wie Web Deploy (MSDeploy.exe) und das VSDBCMD-Hilfsprogramm aufgerufen, um Ihre Webinhalte und Datenbanken in der Zielumgebung bereitzustellen.
Von Anfang bis Ende führt der Build- und Bereitstellungsprozess die folgenden Aufgaben aus:
Es löscht den Inhalt des Ausgabeverzeichnisses, um einen neuen Build vorzubereiten.
Jedes Projekt in der Projektmappe wird erstellt:
- Für Webprojekte – in diesem Fall eine ASP.NET MVC-Webanwendung und einen WCF-Webdienst – erstellt der Buildprozess ein Webbereitstellungspaket für jedes Projekt.
- Bei Datenbankprojekten erstellt der Buildprozess ein Bereitstellungsmanifest (DEPLOYMANIFEST-Datei) für jedes Projekt.
Es verwendet das Hilfsprogramm VSDBCMD.exe, um jedes Datenbankprojekt in der Projektmappe bereitzustellen, wobei verschiedene Eigenschaften aus den Projektdateien – eine Zielverbindungszeichenfolge und ein Datenbankname – zusammen mit der Deploymanifest-Datei verwendet werden.
Es verwendet das Hilfsprogramm MSDeploy.exe, um jedes Webprojekt in der Projektmappe bereitzustellen, wobei verschiedene Eigenschaften aus den Projektdateien verwendet werden, um den Bereitstellungsprozess zu steuern.
Sie können die Beispiellösung verwenden, um diesen Prozess ausführlicher nachzuverfolgen.
Hinweis
Anleitungen zum Anpassen der umgebungsspezifischen Projektdateien für Ihre eigenen Serverumgebungen finden Sie unter Konfigurieren von Bereitstellungseigenschaften für eine Zielumgebung.
Aufrufen des Build- und Bereitstellungsprozesses
Um die Contact Manager-Lösung in einer Entwicklertestumgebung bereitzustellen, führt der Entwickler die Befehlsdatei Publish-Dev.cmd aus. Dadurch wird MSBuild.exe aufgerufen, wobei Publish.proj als auszuführende Projektdatei und Env-Dev.proj als Parameterwert angegeben wird.
msbuild.exe Publish.proj /fl /p:TargetEnvPropsFile=EnvConfig\Env-Dev.proj
Hinweis
Der Schalter /fl (kurz für /fileLogger) protokolliert die Buildausgabe in einer Datei namens msbuild.log im aktuellen Verzeichnis. Weitere Informationen finden Sie in der MSBuild-Befehlszeilenreferenz.
An diesem Punkt beginnt MSBuild mit der Ausführung, lädt die Datei Publish.proj und beginnt mit der Verarbeitung der darin enthaltenen Anweisungen. Die erste Anweisung weist MSBuild an, die Projektdatei zu importieren, die der TargetEnvPropsFile-Parameter angibt.
<Import Project="$(TargetEnvPropsFile)" />
Der Parameter TargetEnvPropsFile gibt die Datei Env-Dev.proj an, sodass MSBuild den Inhalt der Datei Env-Dev.proj in der Datei Publish.proj zusammenführt.
Die nächsten Elemente, auf die MSBuild in der zusammengeführten Projektdatei stößt, sind Eigenschaftengruppen. Eigenschaften werden in der Reihenfolge verarbeitet, in der sie in der Datei angezeigt werden. MSBuild erstellt ein Schlüssel-Wert-Paar für jede Eigenschaft, vorausgesetzt, dass alle angegebenen Bedingungen erfüllt sind. Die später in der Datei definierten Eigenschaften überschreiben alle Eigenschaften mit demselben Namen, der zuvor in der Datei definiert wurde. Betrachten Sie beispielsweise die OutputRoot-Eigenschaften .
<OutputRoot Condition=" '$(OutputRoot)'=='' ">..\Publish\Out\</OutputRoot>
<OutputRoot Condition=" '$(BuildingInTeamBuild)'=='true' ">$(OutDir)</OutputRoot>
Wenn MSBuild das erste OutputRoot-Element verarbeitet und kein ähnlich benannter Parameter bereitgestellt wurde, wird der Wert der OutputRoot-Eigenschaft auf festgelegt. \Publish\Out. Wenn das zweite OutputRoot-Element gefunden wird, wird der Wert der OutputRoot-Eigenschaft mit dem Wert des OutDir-Parameters überschrieben, wenn die Bedingung auf true ausgewertet wird.
Das nächste Element, auf das MSBuild stößt, ist eine einzelne Elementgruppe, die ein Element mit dem Namen ProjectsToBuild enthält.
<ItemGroup>
<ProjectsToBuild Include="$(SourceRoot)ContactManager-WCF.sln"/>
</ItemGroup>
MSBuild verarbeitet diese Anweisung, indem eine Elementliste mit dem Namen ProjectsToBuild erstellt wird. In diesem Fall enthält die Elementliste einen einzelnen Wert – den Pfad und den Dateinamen der Projektmappendatei.
An diesem Punkt sind die verbleibenden Elemente Ziele. Ziele werden anders verarbeitet als Eigenschaften und Elemente– Im Wesentlichen werden Ziele nicht verarbeitet, es sei denn, sie werden entweder explizit vom Benutzer angegeben oder von einem anderen Konstrukt in der Projektdatei aufgerufen. Denken Sie daran, dass das öffnende Project-Tag ein DefaultTargets-Attribut enthält.
<Project ToolsVersion="4.0"
DefaultTargets="FullPublish"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Dadurch wird MSBuild angewiesen, das FullPublish-Ziel aufzurufen, wenn beim Aufrufen MSBuild.exe keine Ziele angegeben werden. Das FullPublish-Ziel enthält keine Aufgaben. Stattdessen wird lediglich eine Liste von Abhängigkeiten angegeben.
<PropertyGroup>
<FullPublishDependsOn>
Clean;
BuildProjects;
GatherPackagesForPublishing;
PublishDbPackages;
PublishWebPackages;
</FullPublishDependsOn>
</PropertyGroup>
<Target Name="FullPublish" DependsOnTargets="$(FullPublishDependsOn)" />
Diese Abhängigkeit teilt MSBuild mit, dass zum Ausführen des FullPublish-Ziels diese Liste der Ziele in der angegebenen Reihenfolge aufgerufen werden muss:
- Es muss das Clean-Ziel aufrufen.
- Sie muss das BuildProjects-Ziel aufrufen.
- Es muss das GatherPackagesForPublishing-Ziel aufrufen.
- Es muss das PublishDbPackages-Ziel aufrufen.
- Sie muss das PublishWebPackages-Ziel aufrufen.
Das saubere Ziel
Das Clean-Ziel löscht im Grunde das Ausgabeverzeichnis und seinen gesamten Inhalt als Vorbereitung für einen neuen Build.
<Target Name="Clean" Condition=" '$(BuildingInTeamBuild)'!='true' ">
<Message Text="Cleaning up the output directory [$(OutputRoot)]"/>
<ItemGroup>
<_FilesToDelete Include="$(OutputRoot)**\*"/>
</ItemGroup>
<Delete Files="@(_FilesToDelete)"/>
<RemoveDir Directories="$(OutputRoot)"/>
</Target>
Beachten Sie, dass das Ziel ein ItemGroup-Element enthält. Wenn Sie Eigenschaften oder Elemente innerhalb eines Target-Elements definieren, erstellen Sie dynamische Eigenschaften und Elemente. Anders ausgedrückt: Die Eigenschaften oder Elemente werden erst verarbeitet, wenn das Ziel ausgeführt wird. Das Ausgabeverzeichnis ist möglicherweise nicht vorhanden oder enthält keine Dateien, bis der Buildprozess beginnt, sodass Sie die _FilesToDelete Liste nicht als statisches Element erstellen können. Sie müssen warten, bis die Ausführung ausgeführt wird. Daher erstellen Sie die Liste als dynamisches Element innerhalb des Ziels.
Hinweis
In diesem Fall ist es nicht erforderlich, eine dynamische Elementgruppe zu verwenden, da das Ziel "Clean " als erstes ausgeführt wird. Es empfiehlt sich jedoch, dynamische Eigenschaften und Elemente in diesem Szenariotyp zu verwenden, da Sie Ziele möglicherweise irgendwann in einer anderen Reihenfolge ausführen möchten.
Außerdem sollten Sie vermeiden, Elemente zu deklarieren, die nie verwendet werden. Wenn Sie Über Elemente verfügen, die nur von einem bestimmten Ziel verwendet werden, sollten Sie sie im Ziel platzieren, um unnötigen Mehraufwand für den Buildprozess zu entfernen.
Abgesehen von dynamischen Elementen ist das Ziel "Bereinigen" recht einfach und nutzt die integrierten Aufgaben Message, Delete und RemoveDir für Folgendes:
- Senden Sie eine Nachricht an die Protokollierung.
- Erstellen Sie eine Liste der zu löschenden Dateien.
- Dateien werden gelöscht.
- Entfernen Sie das Ausgabeverzeichnis.
Das BuildProjects-Ziel
Das BuildProjects-Ziel erstellt im Grunde alle Projekte in der Beispiellösung.
<Target Name="BuildProjects" Condition=" '$(BuildingInTeamBuild)'!='true' ">
<MSBuild Projects="@(ProjectsToBuild)"
Properties="OutDir=$(OutputRoot);
Configuration=$(Configuration);
DeployOnBuild=true;
DeployTarget=Package"
Targets="Build" />
</Target>
Dieses Ziel wurde im vorherigen Thema Grundlegendes zur Projektdatei ausführlich beschrieben, um zu veranschaulichen, wie Aufgaben und Ziele auf Eigenschaften und Elemente verweisen. An diesem Punkt interessieren Sie sich hauptsächlich für die MSBuild-Aufgabe . Mit dieser Aufgabe können Sie mehrere Projekte erstellen. Die Aufgabe erstellt keine neue instance von MSBuild.exe. Sie verwendet die aktuell ausgeführte instance, um jedes Projekt zu erstellen. Die wichtigsten Punkte in diesem Beispiel sind die Bereitstellungseigenschaften:
- Die DeployOnBuild-Eigenschaft weist MSBuild an, alle Bereitstellungsanweisungen in den Projekteinstellungen auszuführen, wenn der Build jedes Projekts abgeschlossen ist.
- Die DeployTarget-Eigenschaft identifiziert das Ziel, das Sie aufrufen möchten, nachdem das Projekt erstellt wurde. In diesem Fall erstellt das Paketziel die Projektausgabe in ein bereitstellbares Webpaket.
Hinweis
Das Paketziel ruft die Web Publishing Pipeline (WPP) auf, die die Integration zwischen MSBuild und Web Deploy ermöglicht. Wenn Sie einen Blick auf die integrierten Ziele werfen möchten, die vom WPP bereitgestellt werden, überprüfen Sie die Datei Microsoft.Web.Publishing.targets im Ordner %PROGRAMFILES(x86)%\MSBuild\Microsoft\VisualStudio\v10.0\Web.
Das GatherPackagesForPublishing-Ziel
Wenn Sie das GatherPackagesForPublishing-Ziel untersuchen, werden Sie feststellen, dass es eigentlich keine Aufgaben enthält. Stattdessen enthält sie eine einzelne Elementgruppe, die drei dynamische Elemente definiert.
<Target Name="GatherPackagesForPublishing">
<ItemGroup>
<PublishPackages
Include="$(_ContactManagerDest)ContactManager.Mvc.deploy.cmd">
<WebPackage>true</WebPackage>
<!-- More item metadata -->
</PublishPackages>
<PublishPackages
Include="$(_ContactManagerSvcDest)ContactManager.Service.deploy.cmd">
<WebPackage>true</WebPackage>
<!-- More item metadata -->
</PublishPackages>
<DbPublishPackages Include="$(_DbDeployManifestPath)">
<DbPackage>true</DbPackage>
<!-- More item metadata -->
</DbPublishPackages>
</ItemGroup>
</Target>
Diese Elemente beziehen sich auf die Bereitstellungspakete, die erstellt wurden, als das BuildProjects-Ziel ausgeführt wurde. Sie konnten diese Elemente nicht statisch in der Projektdatei definieren, da die Dateien, auf die die Elemente verweisen, erst vorhanden sind, wenn das BuildProjects-Ziel ausgeführt wird. Stattdessen müssen die Elemente dynamisch innerhalb eines Ziels definiert werden, das erst aufgerufen wird, nachdem das BuildProjects-Ziel ausgeführt wurde.
Die Elemente werden innerhalb dieses Ziels nicht verwendet. Dieses Ziel erstellt einfach die Elemente und die Metadaten, die jedem Elementwert zugeordnet sind. Nachdem diese Elemente verarbeitet wurden, enthält das PublishPackages-Element zwei Werte: den Pfad zur ContactManager.Mvc.deploy.cmd-Datei und den Pfad zur ContactManager.Service.deploy.cmd-Datei . Web Deploy erstellt diese Dateien als Teil des Webpakets für jedes Projekt. Dies sind die Dateien, die Sie auf dem Zielserver aufrufen müssen, um die Pakete bereitzustellen. Wenn Sie eine dieser Dateien öffnen, wird im Grunde ein MSDeploy.exe-Befehl mit verschiedenen buildspezifischen Parameterwerten angezeigt.
Das DbPublishPackages-Element enthält einen einzelnen Wert, den Pfad zur ContactManager.Database.deploymanifest-Datei .
Hinweis
Eine DEPLOYMANIFEST-Datei wird generiert, wenn Sie ein Datenbankprojekt erstellen, und sie verwendet das gleiche Schema wie eine MSBuild-Projektdatei. Sie enthält alle informationen, die zum Bereitstellen einer Datenbank erforderlich sind, einschließlich des Speicherorts des Datenbankschemas (.dbschema) und Details zu allen Skripts vor und nach der Bereitstellung. Weitere Informationen finden Sie unter Übersicht über Datenbankbuild und -bereitstellung.
Sie erfahren mehr darüber, wie Bereitstellungspakete und Datenbankbereitstellungsmanifeste erstellt und verwendet werden, um Webanwendungsprojekte zu erstellen und zu packen und Datenbankprojekte bereitzustellen.
Das PublishDbPackages-Ziel
Kurz gesagt ruft das PublishDbPackages-Ziel das VSDBCMD-Hilfsprogramm auf, um die ContactManager-Datenbank in einer Zielumgebung bereitzustellen. Das Konfigurieren der Datenbankbereitstellung erfordert viele Entscheidungen und Nuancen. Weitere Informationen hierzu finden Sie unter Bereitstellen von Datenbankprojekten und Anpassen von Datenbankbereitstellungen für mehrere Umgebungen. In diesem Thema konzentrieren wir uns darauf, wie dieses Ziel tatsächlich funktioniert.
Beachten Sie zunächst, dass das öffnende Tag ein Outputs-Attribut enthält.
<Target Name="PublishDbPackages" Outputs="%(DbPublishPackages.Identity)">
Dies ist ein Beispiel für die Zielbatchverarbeitung. In MSBuild-Projektdateien ist batching eine Technik zum Durchlaufen von Sammlungen. Der Wert des Outputs-Attributs, "%(DbPublishPackages.Identity)", bezieht sich auf die Identity-Metadateneigenschaft der DbPublishPackages-Elementliste. Diese Notation, Outputs=%(ItemList.ItemMetadataName), wird wie folgt übersetzt:
- Teilen Sie die Elemente in DbPublishPackages in Batches von Elementen auf, die denselben Identitätsmetadatenwert enthalten.
- Führen Sie das Ziel einmal pro Batch aus.
Hinweis
Identität ist einer der integrierten Metadatenwerte , der jedem Element bei der Erstellung zugewiesen wird. Er bezieht sich auf den Wert des Include-Attributs im Item-Element , d. h. auf den Pfad und dateinamen des Elements.
Da in diesem Fall nie mehr als ein Element mit demselben Pfad und Dateinamen vorhanden sein sollte, arbeiten wir im Wesentlichen mit Batchgrößen von 1. Das Ziel wird für jedes Datenbankpaket einmal ausgeführt.
Eine ähnliche Notation wird in der eigenschaft _Cmd angezeigt, die einen VSDBCMD-Befehl mit den entsprechenden Switches erstellt.
<_Cmd>"$(VsdbCmdExe)"
/a:Deploy
/cs:"%(DbPublishPackages.DatabaseConnectionString)"
/p:TargetDatabase=%(DbPublishPackages.TargetDatabase)
/manifest:"%(DbPublishPackages.FullPath)"
/script:"$(_CmDbScriptPath)"
$(_DbDeployOrScript)
</_Cmd>
In diesem Fall beziehen sich %(DbPublishPackages.DatabaseConnectionString), %(DbPublishPackages.TargetDatabase) und %(DbPublishPackages.FullPath) auf Metadatenwerte der DbPublishPackages-Elementsammlung . Die _Cmd-Eigenschaft wird von der Exec-Aufgabe verwendet, die den Befehl aufruft.
<Exec Command="$(_Cmd)"/>
Als Ergebnis dieser Notation erstellt der Task Exec Batches basierend auf eindeutigen Kombinationen der Metadatenwerte DatabaseConnectionString, TargetDatabase und FullPath , und der Task wird für jeden Batch einmal ausgeführt. Dies ist ein Beispiel für die Batchverarbeitung von Aufgaben. Da die Batchverarbeitung auf Zielebene unsere Elementsammlung jedoch bereits in Einzelelementbatches unterteilt hat, wird der Task Exec für jede Iteration des Ziels nur einmal ausgeführt. Anders ausgedrückt: Dieser Task ruft das Hilfsprogramm VSDBCMD einmal für jedes Datenbankpaket in der Projektmappe auf.
Hinweis
Weitere Informationen zur Ziel- und Aufgabenbatchverarbeitung finden Sie unter MSBuild-Batchverarbeitung, Elementmetadaten in der Zielbatchverarbeitung und Elementmetadaten in Der Aufgabenbatchverarbeitung.
Das PublishWebPackages-Ziel
Zu diesem Zeitpunkt haben Sie das BuildProjects-Ziel aufgerufen, das ein Webbereitstellungspaket für jedes Projekt in der Beispiellösung generiert. Jedes Paket wird von einer deploy.cmd-Datei begleitet, die die MSDeploy.exe Befehle enthält, die zum Bereitstellen des Pakets in der Zielumgebung erforderlich sind, sowie eine SetParameters.xml-Datei , die die erforderlichen Details der Zielumgebung angibt. Sie haben auch das Ziel GatherPackagesForPublishing aufgerufen, das eine Elementsammlung generiert, die die dateien deploy.cmd enthält, an denen Sie interessiert sind. Im Wesentlichen führt das PublishWebPackages-Ziel folgende Funktionen aus:
- Die SetParameters.xml-Datei für jedes Paket wird so bearbeitet, dass sie die richtigen Details für die Zielumgebung enthält, indem der XmlPoke-Task verwendet wird .
- Die Datei deploy.cmd wird für jedes Paket mithilfe der entsprechenden Switches aufgerufen.
Genau wie das PublishDbPackages-Ziel verwendet das PublishWebPackages-Ziel die Zielbatchverarbeitung, um sicherzustellen, dass das Ziel einmal für jedes Webpaket ausgeführt wird.
<Target Name="PublishWebPackages" Outputs="%(PublishPackages.Identity)">
Innerhalb des Ziels wird der Task Exec verwendet, um die Datei deploy.cmd für jedes Webpaket auszuführen.
<PropertyGroup>
<_Cmd>
%(PublishPackages.FullPath)
$(_WhatifSwitch)
/M:$(MSDeployComputerName)
%(PublishPackages.AdditionalMSDeployParameters)
</_Cmd>
</PropertyGroup>
<Exec Command="$(_Cmd)"/>
Weitere Informationen zum Konfigurieren der Bereitstellung von Webpaketen finden Sie unter Erstellen und Verpacken von Webanwendungsprojekten.
Zusammenfassung
Dieses Thema enthält eine exemplarische Vorgehensweise, wie geteilte Projektdateien verwendet werden, um den Build- und Bereitstellungsprozess für die Contact Manager-Beispiellösung von Anfang bis Ende zu steuern. Mit diesem Ansatz können Sie komplexe Bereitstellungen auf Unternehmensebene in einem einzigen, wiederholbaren Schritt ausführen, indem Sie einfach eine umgebungsspezifische Befehlsdatei ausführen.
Weitere Informationen
Eine ausführlichere Einführung in Projektdateien und das WPP finden Sie unter Inside the Microsoft-Build-Engine: Using MSBuild and Team Foundation Build by Sayed Ibrahim Hashimi and William Bartholomew, ISBN: 978-0-7356-4524-0.