Found the answer here:
http://www.digitallycreated.net/Blog/59/locally-publishing-a-vs2010-asp.net-web-application-using-msbuild
Visual Studio 2010 has great new Web Application Project publishing
features that allow you to easy publish your web app project with a
click of a button. Behind the scenes the Web.config transformation and
package building is done by a massive MSBuild script that’s imported
into your project file (found at: C:\Program Files
(x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets).
Unfortunately, the script is hugely complicated, messy and
undocumented (other then some oft-badly spelled and mostly useless
comments in the file). A big flowchart of that file and some
documentation about how to hook into it would be nice, but seems to be
sadly lacking (or at least I can’t find it).
Unfortunately, this means performing publishing via the command line
is much more opaque than it needs to be. I was surprised by the lack
of documentation in this area, because these days many shops use a
continuous integration server and some even do automated deployment
(which the VS2010 publishing features could help a lot with), so I
would have thought that enabling this (easily!) would be have been a
fairly main requirement for the feature.
Anyway, after digging through the Microsoft.Web.Publishing.targets
file for hours and banging my head against the trial and error wall,
I’ve managed to figure out how Visual Studio seems to perform its
magic one click “Publish to File System” and “Build Deployment
Package” features. I’ll be getting into a bit of MSBuild scripting, so
if you’re not familiar with MSBuild I suggest you check out this crash
course MSDN page.
Publish to File System
The VS2010 Publish To File System Dialog Publish to File System took
me a while to nut out because I expected some sensible use of MSBuild
to be occurring. Instead, VS2010 does something quite weird: it calls
on MSBuild to perform a sort of half-deploy that prepares the web
app’s files in your project’s obj folder, then it seems to do a manual
copy of those files (ie. outside of MSBuild) into your target publish
folder. This is really whack behaviour because MSBuild is designed to
copy files around (and other build-related things), so it’d make sense
if the whole process was just one MSBuild target that VS2010 called
on, not a target then a manual copy.
This means that doing this via MSBuild on the command-line isn’t as
simple as invoking your project file with a particular target and
setting some properties. You’ll need to do what VS2010 ought to have
done: create a target yourself that performs the half-deploy then
copies the results to the target folder. To edit your project file,
right click on the project in VS2010 and click Unload Project, then
right click again and click Edit. Scroll down until you find the
Import element that imports the web application targets
(Microsoft.WebApplication.targets; this file itself imports the
Microsoft.Web.Publishing.targets file mentioned earlier). Underneath
this line we’ll add our new target, called PublishToFileSystem:
<Target Name="PublishToFileSystem"
DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''"
Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))"
Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(PublishFiles)"
DestinationFiles="@(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="True" />
</Target>
This target depends on the
PipelinePreDeployCopyAllFilesToOneFolder target, which is what VS2010
calls before it does its manual copy. Some digging around in
Microsoft.Web.Publishing.targets shows that calling this target causes
the project files to be placed into the directory specified by the
property _PackageTempDir.
The first task we call in our target is the Error task, upon which
we’ve placed a condition that ensures that the task only happens if
the PublishDestination property hasn’t been set. This will catch you
and error out the build in case you’ve forgotten to specify the
PublishDestination property. We then call the MakeDir task to create
that PublishDestination directory if it doesn’t already exist.
We then define an Item called PublishFiles that represents all the
files found under the _PackageTempDir folder. The Copy task is then
called which copies all those files to the Publish Destination folder.
The DestinationFiles attribute on the Copy element is a bit complex;
it performs a transform of the items and converts their paths to new
paths rooted at the PublishDestination folder (check out Well-Known
Item Metadata to see what those %()s mean).
To call this target from the command-line we can now simply perform
this command (obviously changing the project file name and properties
to suit you):
msbuild Website.csproj "/p:Platform=AnyCPU;Configuration=Release;PublishDestination=F:\Temp\Publish" /t:PublishToFileSystem