Continuous Integration http://rickgaribay.net/category/16.aspx Continuous Integration en-US Rick G. Garibay rickgaribay@hotmail.com Subtext Version 1.9.5.176 Take Me with You! Ensuring Team Build Brings Dependencies Along for the Ride http://rickgaribay.net/archive/2009/08/27/take-me-with-you-ensuring-team-build-brings-dependencies-along.aspx <p>As part of an automated build at a client, we currently have over 150 unit tests that run as part of each automated build. Some of the tests are also integration tests which serve to automatically smoke test the freshly compiled application code.</p> <p>One of our apps makes use of an XML file that allows the ability to dynamically change behavior of a component without requiring an app domain recycle. A static method uses a FileWatcher to detect a change and applies the configuration changes at runtime accordingly. But I digress…</p> <p>When Team Build executes your unit tests, it creates a special directory on the build agent called TestResults.The TestResults folder includes a .trx file that captures the build results in XML along with a folder for each build/run which isolates all required test assemblies and other dependant assemblies for execution. The App.config file will automatically be copied over and renamed to AssemblyName.dll.config. This is fine and dandy, but what about other dependencies like the XML file?</p> <p>The answer is different depending on whether you are running your tests locally from Visual Studio or if Team Build is running your tests as part of an automated build, but you must be familiar with both techniques to accomplish a consistent and repeatable means for taking the XML file along for the ride under both scenarios which you will undoubtedly need to understand because chances are you are (or at least should be) running all unit tests locally before checking into source control, right? Right?</p> <p><strong>Local Dependencies</strong></p> <p>When running and debugging unit tests using Visual Studio and a just-in-time test runners like TestDriven.NET that have an external dependency such as an XML file, you must ensure the dependency is in the binary root relative to the executing assembly. This is also true when using other unit testing frameworks like NUnit, which I talk about <a href="http://rickgaribay.net/archive/2007/03/21/Making-Configuration-Files-Available-to-NUnit.aspx">here</a>.</p> <p>Right click the resource in the test project, click properties and set “Copy to Output Directory” to “Copy Always”:</p> <p><a href="http://rickgaribay.net/Images/CustomContent/TakeMewithYouEnsuringMSBuildBringsDepend_E003/image.png"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title="image" border="0" alt="image" width="240" height="94" src="http://rickgaribay.net/Images/CustomContent/TakeMewithYouEnsuringMSBuildBringsDepend_E003/image_thumb.png" /></a> </p> <p>This will ensure that the dependency is copied to the bin directory just as other dependencies like DLLs and App.Config/Web.Config are automatically.</p> <p>At this point, the XML file can be referenced in the root of the bin which is relative to the executing assembly.</p> <p><strong></strong></p> <p><strong>Team Build Dependencies</strong></p> <p>As I mentioned earlier, when Team Build executes unit tests, it moves all required assemblies and assembly dependencies to a TestResults folder. While this includes App.config and Web.config files, other dependencies are not copied regardless of whether you specify “Copy to Output Directory” to “Copy Always” as shown above. This makes sense, because when Team Build runs, the unit test project is no longer located with the rest of the source- instead it is copied to the TestResults folder and prepped for execution.</p> <p>There are two ways to address this.</p> <p>The first option is to mark each test method with a <a target="_blank" href="http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.deploymentitemattribute(VS.80).aspx">DeploymentItem</a> attribute. This is documented <a target="_blank" href="http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.deploymentitemattribute(VS.80).aspx">here</a>, so I won’t go into much into it. The basic usage is to mark you test method with the DeploymentItem attribute, providing a parameter with the path and filename of the dependency:</p> <!-- code formatted by http://manoli.net/csharpformat/ --><style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span>[TestMethod]</pre> <pre><span class="lnum"> 2: </span> [DeploymentItem(<span class="str">"../../MyExternalDependency.xml"</span>)]</pre> <pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> FooShouldReturnBar()</pre> <pre><span class="lnum"> 4: </span> { </pre> <pre class="alt"><span class="lnum"> 5: </span> </pre> <pre><span class="lnum"> 6: </span> <span class="rem">//...</span></pre> <pre class="alt"><span class="lnum"> 7: </span> </pre> <pre><span class="lnum"> 8: </span> }</pre> </div> <p>When the assembly that includes the test class loads, the attribute is reflected and the dependent file is copied to the TestResults folder. You can also add the attribute at the test class level and it will have the same effect.</p> <p>The draw back of this approach is that it requires that you apply this attribute to the test class and it is really quite noisy because it is only used when running tests with Team Build or the Test List Editor.</p> <p>To keep things cleaner, I prefer to make these dependencies configuration driven. So, similarly to setting the “Copy to Output Directory” to “Copy Always” for just-in-time testing, you can determine dependencies that should be copied to the TestResults folder using a global configuration setting that will apply to all tests: Test Run Configuration.</p> <p>Go to Test, “Edit Test Run Configuration” and click on the configuration file as shown below:</p> <p> </p> <p><a href="http://rickgaribay.net/Images/CustomContent/TakeMewithYouEnsuringMSBuildBringsDepend_E003/image_3.png"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title="image" border="0" alt="image" width="240" height="113" src="http://rickgaribay.net/Images/CustomContent/TakeMewithYouEnsuringMSBuildBringsDepend_E003/image_thumb_3.png" /></a> </p> <p>Now, click on the Deployment option on the left, ensure “Enable Deployment” is checked, and either click “Add File” to add a specific file, or “Add Directory” to include the entire contents of a directory:</p> <p> </p> <p><a href="http://rickgaribay.net/Images/CustomContent/TakeMewithYouEnsuringMSBuildBringsDepend_E003/image_4.png"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title="image" border="0" alt="image" width="240" height="170" src="http://rickgaribay.net/Images/CustomContent/TakeMewithYouEnsuringMSBuildBringsDepend_E003/image_thumb_4.png" /></a> </p> <p> </p> <p>The localtestrun.testrunconfig file will be configured with the dependencies specified and this file will be consulted just prior to kicking off a test run to ensure that all dependencies are copied to the TestResults folder.</p> <p>That’s really all there is to it. Thanks to <a target="_blank" href="http://blogs.neudesic.com/blogs/brendon_birdoes/default.aspx">Brendon Birdoes</a> for turning <a target="_blank" href="http://www.rickgaribay.net/contact.aspx">me</a> on to the DeploymentItem attribute which I was not familiar with.</p><img src="http://rickgaribay.net/aggbug/258.aspx" width="1" height="1" /> Rick G. Garibay http://rickgaribay.net/archive/2009/08/27/take-me-with-you-ensuring-team-build-brings-dependencies-along.aspx Fri, 28 Aug 2009 03:05:14 GMT http://rickgaribay.net/archive/2009/08/27/take-me-with-you-ensuring-team-build-brings-dependencies-along.aspx#feedback http://rickgaribay.net/comments/commentRss/258.aspx http://rickgaribay.net/services/trackbacks/258.aspx Unit Testing Microsoft Sync Framework 2.0 &amp; WCF http://rickgaribay.net/archive/2009/08/17/unit-testing-microsoft-sync-framework-amp-wcf.aspx <p>I just wrapped up some work for a client that entails exposing the Sync Framework 2.0 via WCF.</p> <p>We are using Sync Framework 2.0 CTP 2 to support occasionally connected client scenarios between applications deployed out in the edge and core services hosted in the client’s cloud. I am using WCF to wrap the Sync Fx APIs to support interoperable synchronization support via SOAP 1.1. This way, any client can on-ramp with the core services and essentially become self-provisioned by initiating and initial synchronization to download required data necessary to come on line. In addition, when events happen out on the edge, the client can synchronize each event back to the cloud using an upload semantic. </p> <p>There are many benefits to using the Sync Fx as opposed to rolling your own, and one benefit is the extremely simple provider model which makes basic synchronization scenarios a breeze, particularly between relational replicas such as SQL Server. Note however, that Sync Fx is fully extensible and capable of synchronizing any two memory streams. </p> <p>So, the basic idea is that given a SyncProvider for the local and remote replica, any two data sources/applications can be synchronized:</p> <p><a href="http://rickgaribay.net/Images/CustomContent/UnitTestingMicrosoftSyncFrameworkWCF_13226/image_5.png"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="image" border="0" alt="image" src="http://rickgaribay.net/Images/CustomContent/UnitTestingMicrosoftSyncFrameworkWCF_13226/image_thumb_5.png" width="640" height="394" /></a> </p> <p>In my scenario, the local client out in the edge utilizes the out of the box SqlSyncProvider. The goal was to expose the cloud over HTTP using SOAP, which required some understanding of the underlying APIs. From there, it was simply a matter of wrapping the Sync Fx APIs in a service layer, so that I would arrive at something like this:</p> <p><a href="http://rickgaribay.net/Images/CustomContent/UnitTestingMicrosoftSyncFrameworkWCF_13226/image_6.png"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="image" border="0" alt="image" src="http://rickgaribay.net/Images/CustomContent/UnitTestingMicrosoftSyncFrameworkWCF_13226/image_thumb_6.png" width="640" height="371" /></a> </p> <p>By providing a proxy to the orchestrator, I was able to implement a proxy which is polymorphic with RelationalSyncProvider. </p> <p>Immediately it should be obvious that this is not your typical integration scenario. Essentially, I am extending a framework by exposing it over WCF so that any orchestrator can leverage the WebSyncProvider. </p> <p>I started the implementation by writing some tests. It was simple enough to extract an interface for the RelationalSyncProvider type because it is abstract. I used RhinoMocks to stub it, which worked great but where I ran into problems was in recording expectations on the return types because there are not a lot of test seams in the framework. With the absence of setters on main properties that I needed to assert, I needed a way to reach into the framework types and manipulate them so that I could develop the CUT  (in this case the ServiceContract implementation) independent of the framework.<a href="http://rickgaribay.net/Images/CustomContent/UnitTestingMicrosoftSyncFrameworkWCF_13226/clip_image002.jpg"><img style="border-right-width: 0px; margin: 5px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image002" border="0" alt="clip_image002" align="right" src="http://rickgaribay.net/Images/CustomContent/UnitTestingMicrosoftSyncFrameworkWCF_13226/clip_image002_thumb.jpg" width="240" height="227" /></a></p> <p><a href="http://msdn.microsoft.com/en-us/library/bb514191.aspx" target="_blank">Publicize.exe</a> works pretty nicely for what I was looking for. </p> <p>For example, let’s say I want to stub a closed/framework type so that I can actually write my test without having to resort to integrating with the framework (which wouldn’t be a unit test at all), in this case, a couple of types in the Microsoft.Syncronization.dll assembly. </p> <p>I run Publicize.exe Microsoft.Syncronization.dll and it generates a shadow assembly called Microsoft.Syncronization_Accessor.dll, which takes all types, adds _Accessor and exposes all non-public members as public as shown in Reflector on the right. </p> <p>This pretty powerful. </p> <p>I can now stub the return type of any closed/framework class and test as long as I am asserting *properties*, which is pretty common:</p> <!-- code formatted by http://manoli.net/csharpformat/ --><style type="text/css"><![CDATA[ .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }]]></style> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span> </pre> <pre><span class="lnum"> 2: </span> [TestMethod]</pre> <pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> GetChangesShouldReturnNumberOfChangesApplied()</pre> <pre><span class="lnum"> 4: </span> {</pre> <pre class="alt"><span class="lnum"> 5: </span> MockRepository mockRepository = <span class="kwrd">new</span> MockRepository();</pre> <pre><span class="lnum"> 6: </span> </pre> <pre class="alt"><span class="lnum"> 7: </span> RelationalSyncProvider provider = mockRepository.Stub&lt;RelationalSyncProvider&gt;();</pre> <pre><span class="lnum"> 8: </span> </pre> <pre class="alt"><span class="lnum"> 9: </span> </pre> <pre><span class="lnum"> 10: </span> <span class="kwrd">using</span> (mockRepository.Record())</pre> <pre class="alt"><span class="lnum"> 11: </span> {</pre> <pre><span class="lnum"> 12: </span> </pre> <pre class="alt"><span class="lnum"> 13: </span> <span class="rem">// Set up the out parameter on RelationalSyncProvider Stub</span></pre> <pre><span class="lnum"> 14: </span> DbSyncContext changeDataRetriever = <span class="kwrd">new</span> DbSyncContext();</pre> <pre class="alt"><span class="lnum"> 15: </span> </pre> <pre><span class="lnum"> 16: </span> <span class="rem">// Set up ScopeProgress property on DbySyncContext instance</span></pre> <pre class="alt"><span class="lnum"> 17: </span> <span class="rem">/*</span></pre> <pre><span class="lnum"> 18: </span><span class="rem"> * Note that in order to test for number of changes applied, we need to reach into the</span></pre> <pre class="alt"><span class="lnum"> 19: </span><span class="rem"> * DbSyncScopeProgress and access the _totalChanges private member </span></pre> <pre><span class="lnum"> 20: </span><span class="rem"> * so that public properties can be asserted. This is because the TotalChanges property only has a </span></pre> <pre class="alt"><span class="lnum"> 21: </span><span class="rem"> * getter, so the only way to influence the value is through it's backing field.</span></pre> <pre><span class="lnum"> 22: </span><span class="rem"> */</span> </pre> <pre class="alt"><span class="lnum"> 23: </span> </pre> <pre><span class="lnum"> 24: </span> DbSyncScopeProgress dbSyncScopeProgress = <span class="kwrd">new</span> DbSyncScopeProgress();</pre> <pre class="alt"><span class="lnum"> 25: </span> </pre> <pre><span class="lnum"> 26: </span> <span class="rem">// Reference the actual object which will be accessed by the generated accessor</span></pre> <pre class="alt"><span class="lnum"> 27: </span> PrivateObject obj = <span class="kwrd">new</span> PrivateObject(dbSyncScopeProgress);</pre> <pre><span class="lnum"> 28: </span> </pre> <pre class="alt"><span class="lnum"> 29: </span> <span class="rem">// Generated accessor which wraps the type to be exposed using reflection.</span></pre> <pre><span class="lnum"> 30: </span> <span class="rem">// Instead of writing the reflection code manually, Publicize.exe was run against the real assembly,</span></pre> <pre class="alt"><span class="lnum"> 31: </span> <span class="rem">// which in turn generated reflection wrappers for *all* types.</span></pre> <pre><span class="lnum"> 32: </span> </pre> <pre class="alt"><span class="lnum"> 33: </span> DbSyncScopeProgress_Accessor dbSyncScopeProgressAccessor = <span class="kwrd">new</span> DbSyncScopeProgress_Accessor(obj);</pre> <pre><span class="lnum"> 34: </span> </pre> <pre class="alt"><span class="lnum"> 35: </span> <span class="rem">// Look ma, I am accessing a private field</span></pre> <pre><span class="lnum"> 36: </span> dbSyncScopeProgressAccessor._tablesProgress = <span class="kwrd">new</span> List&lt;DbSyncTableProgress&gt;();</pre> <pre class="alt"><span class="lnum"> 37: </span> </pre> <pre><span class="lnum"> 38: </span> </pre> <pre class="alt"><span class="lnum"> 39: </span> <span class="rem">// Now we need to repeat the same process again for this type, which is at the heart of what we need to</span></pre> <pre><span class="lnum"> 40: </span> <span class="rem">// gain access to.</span></pre> <pre class="alt"><span class="lnum"> 41: </span> DbSyncTableProgress dbSyncTableProgress = <span class="kwrd">new</span> DbSyncTableProgress();</pre> <pre><span class="lnum"> 42: </span> </pre> <pre class="alt"><span class="lnum"> 43: </span> <span class="rem">// Provides the accessor with a reference to the actual object</span></pre> <pre><span class="lnum"> 44: </span> obj = <span class="kwrd">new</span> PrivateObject(dbSyncTableProgress);</pre> <pre class="alt"><span class="lnum"> 45: </span> </pre> <pre><span class="lnum"> 46: </span> <span class="rem">// Generated accessor which wraps the type to be exposed using reflection.</span></pre> <pre class="alt"><span class="lnum"> 47: </span> <span class="rem">// Instead of writing the reflection code manually, Publicize.exe was run against the real assembly,</span></pre> <pre><span class="lnum"> 48: </span> <span class="rem">// which in turn generated reflection wrappers for *all* types.</span></pre> <pre class="alt"><span class="lnum"> 49: </span> DbSyncTableProgress_Accessor dbSyncTableProgressAccessor = <span class="kwrd">new</span> DbSyncTableProgress_Accessor(obj);</pre> <pre><span class="lnum"> 50: </span> </pre> <pre class="alt"><span class="lnum"> 51: </span> <span class="rem">// Set the _rowsApplied field so that the stubbed return instance of DbSyncScopeProgress can be asserted.</span></pre> <pre><span class="lnum"> 52: </span> dbSyncTableProgressAccessor._rowsApplied = 42;</pre> <pre class="alt"><span class="lnum"> 53: </span> </pre> <pre><span class="lnum"> 54: </span> <span class="rem">// Add the dbSyncTableProgress to the private collection</span></pre> <pre class="alt"><span class="lnum"> 55: </span> dbSyncScopeProgressAccessor._tablesProgress.Add(dbSyncTableProgress);</pre> <pre><span class="lnum"> 56: </span> </pre> <pre class="alt"><span class="lnum"> 57: </span> </pre> <pre><span class="lnum"> 58: </span> <span class="rem">// Now, set the property to our used and abused instance of DbSyncScopeProgress</span></pre> <pre class="alt"><span class="lnum"> 59: </span> changeDataRetriever.ScopeProgress = dbSyncScopeProgress;</pre> <pre><span class="lnum"> 60: </span> </pre> <pre class="alt"><span class="lnum"> 61: </span> <span class="rem">// Provider is implemented in COM so out params are prevelant (yuk)</span></pre> <pre><span class="lnum"> 62: </span> <span class="kwrd">object</span> changeDataRetrieverObj = changeDataRetriever;</pre> <pre class="alt"><span class="lnum"> 63: </span> </pre> <pre><span class="lnum"> 64: </span> <span class="rem">// Set the expectation</span></pre> <pre class="alt"><span class="lnum"> 65: </span> provider.GetChangeBatch(0, <span class="kwrd">null</span>, <span class="kwrd">out</span> changeDataRetrieverObj);</pre> <pre><span class="lnum"> 66: </span> </pre> <pre class="alt"><span class="lnum"> 67: </span> <span class="rem">// Initialize params</span></pre> <pre><span class="lnum"> 68: </span> SyncIdFormatGroup idFormats = <span class="kwrd">new</span> SyncIdFormatGroup();</pre> <pre class="alt"><span class="lnum"> 69: </span> SyncKnowledge destinationKnowledge = <span class="kwrd">new</span> SyncKnowledge();</pre> <pre><span class="lnum"> 70: </span> ForgottenKnowledge sourceForgottenKnowledge = <span class="kwrd">new</span> ForgottenKnowledge();</pre> <pre class="alt"><span class="lnum"> 71: </span> </pre> <pre><span class="lnum"> 72: </span> <span class="rem">// Stub the return, including the object reference to DbSyncContext</span></pre> <pre class="alt"><span class="lnum"> 73: </span> LastCall.Return(<span class="kwrd">new</span> ChangeBatch(idFormats, destinationKnowledge, sourceForgottenKnowledge))</pre> <pre><span class="lnum"> 74: </span> .OutRef(changeDataRetrieverObj).IgnoreArguments();</pre> <pre class="alt"><span class="lnum"> 75: </span> }</pre> <pre><span class="lnum"> 76: </span> </pre> <pre class="alt"><span class="lnum"> 77: </span> IRelationalSyncManager manager = <span class="kwrd">new</span> RelationalSyncManager();</pre> <pre><span class="lnum"> 78: </span> </pre> <pre class="alt"><span class="lnum"> 79: </span> <span class="rem">// Inject the stub</span></pre> <pre><span class="lnum"> 80: </span> manager.Provider = provider;</pre> <pre class="alt"><span class="lnum"> 81: </span> </pre> <pre><span class="lnum"> 82: </span> SyncKnowledge localKnowledge = <span class="kwrd">new</span> SyncKnowledge();</pre> <pre class="alt"><span class="lnum"> 83: </span> DbSyncContext returnedContext = <span class="kwrd">new</span> DbSyncContext();</pre> <pre><span class="lnum"> 84: </span> returnedContext.ScopeProgress = <span class="kwrd">new</span> DbSyncScopeProgress();</pre> <pre class="alt"><span class="lnum"> 85: </span> </pre> <pre><span class="lnum"> 86: </span> </pre> <pre class="alt"><span class="lnum"> 87: </span> var changeBatch = manager.GetChanges(0, localKnowledge, <span class="kwrd">out</span> returnedContext);</pre> <pre><span class="lnum"> 88: </span> </pre> <pre class="alt"><span class="lnum"> 89: </span> DbSyncContext context = returnedContext;</pre> <pre><span class="lnum"> 90: </span> </pre> <pre class="alt"><span class="lnum"> 91: </span> Assert.IsTrue(context.ScopeProgress.TotalChangesApplied ==42);</pre> <pre><span class="lnum"> 92: </span> </pre> <pre class="alt"><span class="lnum"> 93: </span> }</pre> </div> <p> </p> <p>There is a bit of work here, but with a bit of elbow grease thanks to Visual Studio Test Tools, RhinoMocks, reflection and a little code generation the framework is testable.</p><img src="http://rickgaribay.net/aggbug/256.aspx" width="1" height="1" /> Rick G. Garibay http://rickgaribay.net/archive/2009/08/17/unit-testing-microsoft-sync-framework-amp-wcf.aspx Tue, 18 Aug 2009 04:46:33 GMT http://rickgaribay.net/archive/2009/08/17/unit-testing-microsoft-sync-framework-amp-wcf.aspx#feedback 2 http://rickgaribay.net/comments/commentRss/256.aspx http://rickgaribay.net/services/trackbacks/256.aspx MSBuild Reference Paths http://rickgaribay.net/archive/2007/08/25/msbuild-reference-paths.aspx <p>One of the nice things about MSBuild is that with Visual Studio 2005, the project and solution files <strong><em>are </em></strong>the build files. So, in theory, MSBUILD will start at the sln file and walk all references during a build process.</p> <p>But, what happens when you have an automated build process, or are doing continous integration and want to standardize project references to assemblies and other vendor components? Trying to get an entire development on board to use specific paths, etc is an option, but since no one is perfect (and enforcing these rules can be a nightmare), we need a better way to standardize the management of how dependancies are accessed.</p> <p>With a build process in place, the build can facilitate this standardization. Assemblies, both internal and from external vendors can be catalogued on a file share and the build process itself can reference this shared location in a standardized manner, without trying to micro-manage development environment settings.</p> <p>The solution is to use the ReferencePath property at the MSBUIILD command line.</p> <p>For example:</p> <p><font face="Courier New">MSBUILD.EXE &lt;solution name&gt;  /p:"ReferencePath=&lt;Path1;Path2;Path3&gt;"</font></p> <p><font face="Arial">The <font face="Courier New">ReferencePath</font> property allows you to add a series of paths that MSBUILD will check, overriding any project-based references.</font></p> <p><font face="Arial">This is extremely helpful when references are all over the place or when it is undesirable to recreate development-time configuration at build time.</font></p><img src="http://rickgaribay.net/aggbug/153.aspx" width="1" height="1" /> Rick G. Garibay - The more I learn, the less I kno http://rickgaribay.net/archive/2007/08/25/msbuild-reference-paths.aspx Sat, 25 Aug 2007 22:50:22 GMT http://rickgaribay.net/archive/2007/08/25/msbuild-reference-paths.aspx#feedback 4 http://rickgaribay.net/comments/commentRss/153.aspx http://rickgaribay.net/services/trackbacks/153.aspx