Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Sign Up
Dashboard
Maven User
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<h2>Preamble</h2> <p>Disclaimer: I'm not a .NET programmer. The company I'm currently working for has large number of tools written in various languages (C/C++, C#, Perl, Java, etc.). We are in process of unifying builds of these various tools under Maven. This article describes how to add Maven build to your .NET project(s).</p> <table class="wysiwyg-macro" data-macro-name="info" data-macro-parameters="title=NPanday" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm86dGl0bGU9TlBhbmRheX0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"> <p>You may also consider using <a href="http://incubator.apache.org/npanday/">NPanday</a>, which provides direct support for building .NET projects using Maven.</p></td></tr></table> <h2>Prerequisites</h2> <table class="wysiwyg-macro" data-macro-name="warning" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3dhcm5pbmd9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>This tutorial will not teach you basics of Maven. You should be aware of Maven lifecycle, standard directory layout, plugin (mojo) mechanism and profiles</p></td></tr></table> <ul> <li>Before you can add Maven build to your project it has to confirm to few simple rules: <ul> <li>Code for a single project should be under single root. (No you don't have to confirm to Maven standard directory layout)</li> <li>Your sub-projects should appear under one common root that contains parent POM</li> <li>You project must use msbuild.exe utility, and the tutorial assumes you can build your code from command line</li> </ul> </li> <li>The machine running the build must have .NET 3.5 framework and MS SDK installed</li> <li>Tutorial refers to FOO as "umbrella" project that contains number of child projects some of which may have dependency on each other</li> </ul> <h2>Process</h2> <ul> <li>This tutorial assumes that you have parent-child type of project, so it frequently refers to <strong>parent POM</strong> vs. <strong>child POM</strong>.</li> <li>The parent POM is pom.xml file that resigns in the root directory hosting your projects. For example for FOO it is in <code>FOO/trunk/Projects/pom.xml</code></li> <li>The child POM is pom.xml in the root directory of each subprojects nested under parent project. For example <code>FOO/trunk/Projects/CalendarView/pom.xml</code></li> <li>The "Mavenizing" of .NET project is done in least-invasive fashion. The only convention that .NET project needs to follow is to expect all project dependency files to appear in <code>PROJECT_ROOT/lib</code> directory. (More about that in the following sections)</li> <li>Maven not really "building" your .NET artifacts in terms of compiling and assembling. That is done by MSBuild which is consuming .NET-specific XML descriptors. The job of Maven is to: <ol> <li><strong>Clean</strong> - Execute <code>MSBuild /t:Clean</code> in addition to built-in clean task</li> <li><strong>Prepare</strong> - Identify dependencies and copy/extract artifacts into specified location under project root (currently{{PROJECT_ROOT/lib}}</li> <li><strong>Build</strong> - Execute <code>MSBuild /t:Build</code> task which does the actual job of compiling and assembling</li> <li><strong>Install</strong> - Collect build artifacts (DLL, EXE), create a ZIP and install into Maven repo so it can be used for deployment or as dependency</li> <li><strong>Publish</strong> - Copy artifacts and dependencies to IIS or file server</li> </ol> </li> </ul> <h3>Build phases in details</h3> <h4>Command line</h4> <p>You can build each individual sub-project by executing maven from the directory that contains child POM. The tutorial covers building multiple projects at once from parent level. The main difference in syntax is - when you build child you mustn't use "-rf ModuleName" command line option.</p> <table class="wysiwyg-macro" data-macro-name="note" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e25vdGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>The following examples assume that you are building multiple projects from parent level</p></td></tr></table> <p>Build follows Maven <a href="http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html">build lifecycle</a> so if you want your project to be packaged, use <em>package</em> goal, but if you want to use that as dependency you need <em>install</em> or <em>deploy</em>. You can chain goals as in the following example<br /> Build is executed with this special syntax <strong>(note: subject to change)</strong></p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> mvn clean install </pre></td></tr></table> <p>Profile <strong>build</strong> is enabled by default so you don't have to enforce it with "-P" switch. By default - the <strong>Release</strong> configuration is build. You can change it by defining <code>foo.config</code> property ether in settings.xml or simply by adding switch to command line</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> mvn clean install -Dfoo.config=Debug </pre></td></tr></table> <h4>Clean</h4> <p>Clean goal runs regular Maven clean-up (deletes <code>PROJECT_ROOT/target</code> directory and then executes <code>MSBuild /t:Clean</code> task. This and any other tasks that involve MSBuild utility are handled by <code>exec-maven-plugin</code></p> <h4>Prepare</h4> <p>Optionally, before .NET build is run, Maven pulls any dependencies from the Maven repo and unpacks these to <code>PROJECT_ROOT/lib</code> directory. The .NET dependency artifacts are stored in the maven repo as ZIP archives. Each version is clearly marked with group ID, artifact ID and version number. For example <code>REPO/mycompany/dept/foo/CalendarView/1.0/CalendarView-1.0.zip</code>. ZIP contains all the artifacts (dll, exe, etc.) in the top directory of the archive. This task is handled by <code>maven-dependency-plugin</code></p> <h4>Build</h4> <p>Build is handled by <code>exec-maven-plugin</code> which simply executes MSBuild.exe and provides runtime parameters. See follow-up configuration section for details</p> <h4>Install</h4> <p>Install/deploy creates ZIP archive of project artifacts and <em>installs</em> ZIP into local M2 repo or <em>deploys</em> ZIP to the SVN-backed repo. The process is done in two steps:</p> <ul> <li>First the project directory is scanned by <code>maven-antrun-plugin</code> during <code>prepare-package</code> phase and collected artifacts (DLL, EXE, PDB) are placed (flattened) into <code>PROJECT_ROOT/target/dll-staging</code> directory</li> <li>Next <code>maven-assembly-plugin</code> creates a ZIP archive during <em>package</em> phase based on reusable assembly descriptor (assembly.xml) which is located in the parent project root. Then the created archive is handled by standard maven <em>install</em> or <em>deploy</em> phase and placed into local M2 repo and (if deploying) into Maven repository designated in the parent POM</li> </ul> <h4>Publish</h4> <p>To publish artifacts to IIS you need to execute <strong>publish</strong> profile. Please note that <strong>publish</strong> will not run MSBuild's <strong>clean</strong> or <strong>build</strong> so before you execute <strong>publish</strong> you need run <strong>build</strong> profile with at least <strong>compile</strong> task, e.g. <code>mvn compile -rf ReadCalendar</code></p> <p>The main assumption of publishing a particular project is that MSBuild configuration for the project contains custom tasks <code>t:/FOOPublish</code>. Note that FOOPublish is a custom publish task for FOO project, you will have to define your own and you can call it anything as far as it is reflected correctly in the POM. Not all sub-projects under parent are publishable so profile section in the parent POM defines it's own module list (see Configuration section for details)</p> <p>The command to publish. Note addition "-P publish" switch</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> mvn verify \-P publish \-rf CalendarView </pre></td></tr></table> <h3>Configuration</h3> <p>The complete configuration is split between the parent POM and at least 2 flavors of child POM depending if the child project is self-sufficient or specifies some dependencies</p> <p>Please note that all plugin configurations described in the follow-up sections are placed into <strong><profiles/></strong> section of POM</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=pom.xml/profiles" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPXBvbS54bWwvcHJvZmlsZXN9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <project> ... <profiles> <profile> <id>build</id> <build> <plugins> <!-- Plugins configuration here --> </plugins> </build> </profile> </profiles> </project> </pre></td></tr></table> <h4>Dependencies</h4> <p>The dependencies are configured in child POM only. Since dependencies stored as ZIP files we cannot use common dependency mechanism provided by Maven. Instead we are using <code>maven-dependency-plugin</code> <a href="http://is.gd/3SLU3">unpack artifact</a> feature. Here's complete configuration from <code>FOO/CalendarView</code> project that defines dependency on <code>FOO/ReadCalendar</code>. Note that we have ability to specify a particular version</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=CalendarView/pom.xml" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPUNhbGVuZGFyVmlldy9wb20ueG1sfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack</id> <phase>process-resources</phase> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>mycompany.dept.foo</groupId> <artifactId>ReadCalendar</artifactId> <version>1.0-SNAPSHOT</version> <type>zip</type> <outputDirectory>${project.basedir}/lib</outputDirectory> <overWrite>true</overWrite> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </pre></td></tr></table> <p>In this case - complete configuration is contained within child POM. The way it reads from top to bottom:</p> <ul> <li>Define plugin <code>maven-dependency-plugin</code></li> <li>Define plugin's execution with ID = unpack</li> <li>Execution will happen in Maven's <code>process-resources</code> lifecycle phase</li> <li>Plugin's <em>unpack</em> goal will be executed</li> <li>The goal will get specified <em>artifactItem</em> from Maven repo (the ZIP file) and exctract it into <code>PROJECT_ROOT/lib</code> directory</li> <li>To make your .NET code aware of extracted artifact(s) update <code><HintPath/></code> for each dependency in you <em>Foo.csproj</em> file <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=foo.csproj" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPWZvby5jc3Byb2p9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <Reference Include="ReadCalendar, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\lib\ReadCalendar.dll</HintPath> </Reference> </pre></td></tr></table></li> </ul> <h4>Cleanup and Build</h4> <p>This is "the meat" of the whole process. The configuration is split between the parent POM and child(ren) in such way that parent POM contains (in majority of cases) complete definition of profile(s) which keeps children POMs clean from repeatable code.<br /> To suppress execution of profile in the parent you <span style="color: rgb(153,0,51);"><strong>always</strong></span> need to execute parent-based build with <code>--resume-from</code> or <code>-rf</code> switch</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=Parent pom.xml" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPVBhcmVudCBwb20ueG1sfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <configuration> <executable>${dotnet.path}/msbuild</executable> </configuration> <executions> <execution> <id>clean</id> <phase>clean</phase> <configuration> <arguments> <argument>/p:Configuration=${foo.config}</argument> <argument>/p:Platform=${foo.cpu}</argument> <argument>/t:Clean</argument> </arguments> </configuration> <goals> <goal>exec</goal> </goals> </execution> <execution> <id>build</id> <phase>compile</phase> <configuration> <arguments> <argument>/p:Configuration=${foo.config}</argument> <argument>/p:Platform=${foo.cpu}</argument> <argument>/t:Build</argument> </arguments> </configuration> <goals> <goal>exec</goal> </goals> </execution> </executions> </plugin> </pre></td></tr></table> <ul> <li>Two execution targets are defined: clean and build</li> <li>Note how dynamic parameters <em>${foo.config}</em> and <em>${foo.cpu}</em> are used to be able to change configuration at run time</li> </ul> <h4>Packaging (for Install or Deploy)</h4> <p>The packaging of artifacts requires two plugins and one assembly descriptor. The configuration is split between 2 files: parent POM and assembly descriptor.</p> <h5>Pre-packaging</h5> <p>Step 1 is to scan project for build artifacts and collecting these to lib directory. It's done by <code>maven-antrun-plugin</code>. Here's configuration</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=Parent pom.xml" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPVBhcmVudCBwb20ueG1sfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.3</version> <configuration> <tasks> <copy todir="${project.build.directory}/dll-staging"> <fileset dir="${basedir}"> <include name="**/*in/${foo.config}/*.dll" /> <include name="**/*in/${foo.config}/*.pdb" /> <include name="**/*in/${foo.config}/*.exe" /> <exclude name="**/Test*" /> <exclude name="**/test*" /> </fileset> <flattenmapper /> </copy> </tasks> </configuration> <executions> <execution> <id>copy</id> <phase>prepare-package</phase> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </pre></td></tr></table> <p>As you can see DLL, EXE and PDB files are collected to child's <code>PROJECT_ROOT/target/dll-staging</code> directory</p> <p>Notice how this task is run at <em>prepare-package</em> phase</p> <h5>ZIPping up</h5> <p>This is done by <code>maven-assembly-plugin</code></p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=Parent pom.xml" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPVBhcmVudCBwb20ueG1sfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>../assembly.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>zip</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </pre></td></tr></table> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="title=assembly.xml" data-macro-default-parameter="xml" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6eG1sfHRpdGxlPWFzc2VtYmx5LnhtbH0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> <assembly> <formats> <format>zip</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <fileSets> <fileSet> <directory>${project.build.directory}/dll-staging</directory> <outputDirectory>/</outputDirectory> <includes> <include>*.dll</include> <include>*.exe</include> <include>*.pdb</include> </includes> <excludes> <exclude>Test*</exclude> <exclude>test*</exclude> </excludes> </fileSet> </fileSets> </assembly> </pre></td></tr></table> <p>Note that <include> and <exclude> configuration here is redundant since files are already filtered by pre-packaging step.<br /> <code><includeBaseDirectory>false</includeBaseDirectory></code> is used to ensure that zipped files will go to the top directory of archive</p> <h3>References</h3> <p>There are few discussions on stackoverfow.com that I started during writing of this tutorial that you may find useful. </p> <ul> <li><a href="http://stackoverflow.com/questions/1504850/maven-activate-child-profile-based-on-property">Maven - activate child profile based on property</a></li> <li><a href="http://stackoverflow.com/questions/1501294/maven-skip-parent-project-build">Maven - skip parent project build</a></li> <li><a href="http://stackoverflow.com/questions/1500758/maven-creating-flat-zip-assembly">Maven creating flat zip assembly</a></li> <li><a href="http://stackoverflow.com/questions/1477977/streamline-net-projects-with-msbuild">Streamline .NET projects with Msbuild</a></li> </ul> <h3>About author</h3> <p>I run <a href="http://dev.bostone.us/">Mea Cup O' Jo</a> and <a href="http://droidin.net">DroidIn - Android app for LinkedIn</a> blogs.</p>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced