Using Nant 0.92 (and previously 0.85 with same consequence)
I'm trying to call the delete task in NAnt to remove all files except the .dll file after calling msbuild (see the script below .. I'm referring to the delete in the "build" target). The delete task does not seem to execute.
The initial delete task works fine, and behaves as expected, removing all files from the specified build directory. The second delete task, after the compile however doesn't work.
I've tried just deleting everything (not using the exclude tag), tried explicitly setting failonerror and verbose to true. None of these make a difference. I've also tried using sleep to stop the process prior to the delete task, in case something in msbuild was not releasing the files in time for a delete. I've tried putting the delete into a separate target, still no luck.
The command obviously works prior to calling msbuild, and it works after msbuild if trying to delete from a directory other than the msbuild output target (i.e. copy the output files, then delete relevant files).
I'm sure that this is too fundamental a problem to be a bug, but I thought I'd ask anyway. Of course I'll use the workaround in the mean time of just copying the files to a different location delete what I don't need then move appropriately, but I'm curious about this.
I suspect, that unless this behaviour is by design (although I can find nothing in the NAnt documentation to suggest it is), then perhaps the msbuild process is locking the output files until NAnt process completes? This is my best guess. Further insights would be appreciated.
EDIT: also, if I explicitly set the \OutputPath switch of msbuild, then I don't have the same problem. It only appears that when the default OutputPath is used is the problem created.
NAnt build file:
<?xml version="1.0" encoding="utf-8" ?>
<project name="Reports.TestBench.PreBuild" default="postbuild.cleanup" basedir="." xmlns="http://nant.sourceforge.net/release/0.86-beta1/nant.xsd">
<property name="nant.settings.currentframework" value="net-4.0" />
<property name="project.StandardReports" value="${project::get-base-directory()}\Reports.StandardReports\Palladium.Reports.StandardReports.csproj" />
<property name="output.Dir" value="${project::get-base-directory()}\bin\debug\"/>
<property name="build.Type" value="debug"/>
<!--Deletes the pre-existing build files-->
<target name="clean">
<delete>
<fileset basedir="${output.Dir}">
<include name="*.*" />
</fileset>
</delete>
</target>
<!--Builds the projects to the specified build directory-->
<target name="build" depends="clean" description="Build the Palladium Reports Standard Reports application">
<msbuild project="${project.StandardReports}">
<arg value="/p:Configuration=${build.Type}" />
<!--arg value="/p:OutputPath=${path::get-full-path(buildDir.Client)}\Reports" /-->
<arg value="/t:Rebuild" />
</msbuild>
<delete failonerror="true" verbose="true">
<fileset basedir="${output.Dir}">
<include name="*.*" />
<exclude name="Palladium.Reports.StandardReports.dll" />
</fileset>
</delete>
</target>
</project>
Summary of NAnt output showing build success with no further messages:
[msbuild] Build succeeded.
[msbuild] 0 Warning(s)
[msbuild] 0 Error(s)
[msbuild]
[msbuild] Time Elapsed 00:00:03.19
BUILD SUCCEEDED
Total time: 3.5 seconds.
Try poking into NAnt source code - most likely the msbuild task creates an MSBuild engine and does not force it to close the files afterwards. Although looking at the MSDN docs, I don't really see a way to do it - MSBuild constructs lack a Dispose() function. There's a finalizer on classes such as ProjectInstance, so one could force it release the file handles by running in an application domain and closing the whole domain afterwards - that's quite a bit of work.
Related
I'm invoking an MSBuild script that isn't a csproj from a bat script. I would like that script to be able to use the MSBuild Community Tasks, and I don't want to have to install it on every machine, nor do I want to include its binaries in my repo.
By adding these nodes to the script and calling the restore target, the package restores.
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<PackageReference Include="MSBuildTasks">
<Version>1.*</Version>
</PackageReference>
</ItemGroup>
To use the tasks it contains, I only need to use them. I don't need to import any other targets files:
<Target Name="MyTarget" DependsOnTargets="Restore">
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(VersionInfoFile)"
AssemblyVersion="1.2.3.5"
/>
</Target>
However, the first time I run my script, the package restores, but then the script fails because it can't find the AssemblyInfo task. The second time, it succeeds. Is there any way to get this to work without calling the MSBuild script twice (the first time, specifically running the Restore target)?
You can force a re-evaluation of the imports generated by NuGet by calling the msbuild file from itself using the <MSBuild> task with a different set of global properties (!).
<Target Name="MyTarget" DependsOnTargets="Restore">
<MSBuild Projects="$(MSBuildProject)" Targets="MyTargetCore" Properties="Foo=Bar" />
</Target>
<Target Name="MyTargetCore">
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(VersionInfoFile)"
AssemblyVersion="1.2.3.5"
/>
</Target>
Depending on the circumstances (solution build, project references), it may or may not work without the Properties="Foo=Bar" part.
However, note that this is a bit risky since not all msbuild caches can even be cleared using the arguments on the MSBuild task. MSBuild 15.5 is going to add a /restore switch that will execute the Restore target, clear all necessary caches and then do the other requested work. So in 15.5 you should be able to call msbuild /restore /t:MyTarget without any difficulties.
I am having issues understanding Cruise Control.
I would like to create a build automation in order to perform the build in my project. To do that I created the following entry in the ccnet.config file
<project name="My Web Release " description="Web config">
<workingDirectory>d:\GIT</workingDirectory>
<triggers/>
<sourcecontrol type="git">
<repository>GIT REPO</repository>
<branch>release-name</branch>
<autoGetSource>true</autoGetSource>
<fetchSubmodules>true</fetchSubmodules>
<executable>C:\Program Files (x86)\Git\cmd\git.exe</executable>
<tagOnSuccess>false</tagOnSuccess>
<commitBuildModifications>false</commitBuildModifications>
<commitUntrackedFiles>false</commitUntrackedFiles>
<tagCommitMessage> Build {0}</tagCommitMessage>
<tagNameFormat>Build-{0}</tagNameFormat>
<committerName>Build</committerName>
<committerEMail>build#build.com</committerEMail>
<workingDirectory>$(workingDirectory)\Sources\WEB</workingDirectory>
<timeout>600000</timeout>
</sourcecontrol>
<tasks>
<msbuild>
<executable>c:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
<buildFile>BuildScript.xml</buildFile>
<targets>NewBuild</targets>
<logger>C:\Program Files (x86)\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</tasks>
<publishers>
<xmllogger />
<artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="50" />
</publishers>
</project>
And I do have a BuildScript.xml file.
My question is:
Is this a nAnt or MSBUILD script?
I am asking because I am trying to follow the documentation but I get a lot of issues regarding unknown tasks and so on.
For instance, this:
<property name="configuration" value="CLOSED" />
Would generate a unknown "property" task.
I am looking at MSBuild documentation to use a Move task.
and I got to this line:
<move file="originPath" tofile="TargetPath"/>
But I get:
BuildScript.xml(18,3): error MSB4036: The "Move" task was not
found. C heck the following: 1.) The name of the task in the project
file is the same as the name of the task class. 2.) The task class is
"public" and implements the Microsoft.Build.Framework.ITask interface.
3.) The task is correctly declared w ith in the project file, or in the *.tasks files located in the "C:
\Windows\Microsoft.NET\Framework\v2.0.50727" directory.
What is driving me crazy is that it was working before we migrated to Cruise Control.
Is this being interpreted as nAnt or MSBuild? Any ideas on why I am getting these errors?
It looks like your mixing nant and msbuild, if it was msbuild it would look like
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Tools="4.0">
<Target Name="Move">
<PropertyGroup>
<configuration>CLOSED</configuration>
</PropertyGroup>
<Move SourceFiles="Somefilefile" DestinationFolder="c:\temp"/>
</Target>
</Project>
So casing was an issue and that you need to specify the tools version as move is available from 4.0.
I am using MSBuild.Community.Tasks to help with two things, namely adjusting the version and zipping up a file. I am not married to this, so an alternate approach is welcome provided it produces what I'm looking for. The goal is to increment the build number before the build, then ZIP up a new DLL (with a couple of other files) after the build. The ZIP file should be named according to the build.
I am almost there, however, my version number in my DLL is always one step behind my version.txt file (auto-gen'd from the Version task). Here is what I have in the BeforeBuild target:
<Target Name="BeforeBuild" BeforeTargets="PrepareForBuild">
<Message Text=" --=== Before Build ===--"></Message>
<ItemGroup>
<PreviousFiles Include="$(MSBuildProjectDirectory)\BuildPackage\$(AssemblyName).*.zip">
<InProject>false</InProject>
</PreviousFiles>
</ItemGroup>
<Delete Files="#(PreviousFiles)"></Delete>
<Delete Files="$(MSBuildProjectDirectory)\BuildPackage\$(AssemblyName).dll"></Delete>
<Version VersionFile="version.txt" RevisionType="Increment">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
</Target>
This deletes any files from the previous build, then increments the version.txt file correctly.
Next, in my AfterBuild target I have put this together:
<Target Name="AfterBuild" AfterTargets="Build">
<Message Text=" --=== After Build ==--"></Message>
<AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyVersion.cs" AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />
<ItemGroup>
<ProjectOutputFiles Include="bin\$(AssemblyName).dll">
<InProject>false</InProject>
</ProjectOutputFiles>
<ZipFiles Include="$(MSBuildProjectDirectory)\BuildPackage\*.*" Exclude="$(MSBuildProjectDirectory)\BuildPackage\*.zip">
<InProject>false</InProject>
</ZipFiles>
</ItemGroup>
<Copy SourceFiles="#(ProjectOutputFiles)" DestinationFolder="$(MSBuildProjectDirectory)\StorePackage" />
<Zip Files="#(ZipFiles)" WorkingDirectory="$(MSBuildProjectDirectory)\BuildPackage" ZipFileName="$(MSBuildProjectDirectory)\BuildPackage\$(AssemblyName).$(Major)-$(Minor)-$(Build)-$(Revision).zip" ZipLevel="9" />
</Target>
Basically, I'm updating AssemblyInfo.cs and specifying some file groups. I then copy the project output over and finally ZIP up the required files.
This all works great, except my DLL version is always 1 revision number behind my actual revision number - i.e., what is stored in version.txt and what the name of the .ZIP file is saved out as.
Am I missing something obvious here? It's like the version is captured before the build process even starts or something.
Thanks in advance.
GOSH, yes, I am missing something obvious. As soon as I re-read the question, right before posting, it became clear what I had done incorrectly. I even spelled it out.
The fix was to move the AssemblyInfo task up to the BeforeBuild target. This is the task that outputs the .cs file containing the attributes required to inject the correct versioning information into the DLL. Leaving the update of this file until after the build has completed meant that my DLL was going to be losing the race in perpetuity.
I'm going to go ahead and post this anyways, in case someone else runs into something similar.
Cheers.
Edit For what it's worth, the final version of these targets required moving the ZIP operation out into a separate target (I called it ZipProjectOutput) because of a race condition that I couldn't resolve: the DLL was never showing up in the ZIP file, and I think that the timing of the OS releasing a lock on the file or something might have been to blame.
I used the AfterTargets="AfterBuild" to have the ZipProjectOutput target execute when the other was complete. I'm not entirely happy about this as I'm not certain I'm just "winning" the race here, rather than solving the problem, but this is working for me now rather slickly. #WFM
I'm trying to make a batch file to publish the few ClickOnce application we have in one click. I'm using msbuild for that, and as an example the below command line shows how I'm doing it:
msbuild
MyApp.sln
/t:Publish
/p:Configuration=Release
/p:PublishUrl="C:\Apps\"
/v:normal > Log.txt
(wrapped for easier reading)
when I run the above command it builds and publish the application in the release directory, i.e. bin\release! Any idea why msbuild doesn't respect PublishUrl property in my example above?
PS: I tried also different combinations including remove 'Configuration', use 'Rebuild' and 'PublishOnly' as targets, and remove the the quotation marks but without any success.
You are setting the wrong property. Try PublishDir instead.
You can pass it into MSBuild as you are or you can set it in the project file (or maybe the sln file too, not sure I always use the project file.) like this
<PropertyGroup>
<PublishDir>C:\Dev\Release\$(BuildEnvironment)\</PublishDir>
</PropertyGroup>
I've just done a few blog posts on MsBuild and ClickOnce stuff, check it out you 'should' find them useful...
Some features are done by Visual-Studio and not by the MSBuild-script. So the click-once-deployment behaves differently when it's executed from the command-line.
The ApplicationRevision isn't increased with every build. This works only when is exectued from Visual Studio
In in somecases, the PublishUrl isn't used. Quote from MSDN:
For example, you could set the PublishURL to an FTP path and set the InstallURL to a Web URL. In this case, the PublishURL is only used in the IDE to transfer the files, but not used in the command-line builds. Finally, you can use UpdateUrl if you want to publish a ClickOnce application that updates itself from a separate location from which it is installed.
I've created a special MSBuild-file which does this things. It runs the publish-target and copies then the files to the right location.
An example of the build-file, as requested by alhambraeidos. It basically runs the regular VisualStudio-build and then copies the click-once data to the real release folder. Note that removed some project-specific stuff, so it's maybe broken. Furthermore it doesn't increase the build-number. Thats done by our Continues-Build-Server:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- the folder of the project to build -->
<ProjLocation>..\YourProjectFolder</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Release</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<!-- This is the web-folder, which provides the artefacts for click-once. After this
build the project is actually deployed on the server -->
<DeploymentFolder>D:\server\releases\</DeploymentFolder>
</PropertyGroup>
<Target Name="Publish" DependsOnTargets="Clean">
<Message Text="Publish-Build started for build no $(ApplicationRevision)" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Publish"/>
<ItemGroup>
<SchoolPlannerSetupFiles Include="$(ProjPublishLocation)\*.*"/>
<SchoolPlannerUpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy
SourceFiles="#(SchoolPlannerSetupFiles)"
DestinationFolder="$(DeploymentFolder)\"
/>
<Copy
SourceFiles="#(SchoolPlannerUpdateFiles)"
DestinationFolder="$(DeploymentFolder)\Application Files\%(RecursiveDir)"
/>
<CallTarget Targets="RestoreLog"/>
</Target>
<Target Name="Clean">
<Message Text="Clean project:" />
<MSBuild Projects="$(ProjLocation)/YourProject.csproj" Properties="Configuration=Release" Targets="Clean"/>
</Target>
</Project>
I'll put in my 2 cents, this syntax seems to work (right or wrong):
/p:publishUrl="C:\\_\\Projects\\Samples\\artifacts\\Web\\"
For me, the soultion was to escape the path.
Instead of:
/p:PublishUrl="C:\Apps\"
Put:
/p:PublishUrl="C:\\Apps\\"
I'm new to NAnt and have been able to create a <target> which
1) Deletes any code from the current folder
2) Exports fresh code from SVN
3) Builds the code in the default directory which is the PrecompiledWeb folder (its a web app)
Here it is:
<target name="export" description="export code from svn">
<delete dir="${Delete.Dir}"></delete>
<exec program="svn" commandline="export ${MySVN.Repos} ${MySVN.Dest}" />
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
</msbuild>
</target>
I want to specify a custom output directory (other than "PrecompiledWeb"). Is this possible and could you please show me the necessary tag/property?
Thank you!
EDIT
Thanks to Scott and Si, I'm getting closer to a solution, but I still don't have anything that works. There comments led me to this article on MSBuild's Output Path Property. Using their code:
<?xml version="1.0"?>
<project name="test" default="build" basedir="." xmlns="http://nant.sf.net/schemas/nant-0.84.win32.net-1.0.xsd">
<target name="build">
<exec program="${framework::get-framework-directory('net-3.5')}/msbuild.exe">
<arg value="${Full.Path}\Code\MySolution.sln" />
<arg value="/p:OutputPath=${Full.Path}\Output\" />
</exec>
</target>
</project>
This will sucessfully run; however, in my solution which contains a web site and a class library, it publishes the class library dll to the specified output path, but it still publishes the actual web site to the default PrecompiledWeb folder.
Does anyone have any suggestions for how to change the PrecompiledWeb folder path from NAnt?
Thanks again to everyone who's helped!
Edit 2 Final Solution
Here is what I finally was able to get working (updated for .net 4):
<exec program="${framework::get-framework-directory('net-4.0')}/msbuild.exe">
<arg value="${Full.Path}\Code\MySolution.sln" />
<arg value="/t:Rebuild" />
<arg value="/t:ResolveReferences" />
<arg value="/t:_CopyWebApplication" />
<arg value="/p:OutDir=${Build.Output}bin\" />
<arg value="/p:WebProjectOutputDir=${Build.Output}" />
<arg value="/p:Configuration=Release" />
</exec>
One can specify and override some of properties for msbuild. In order to specify the output directory, override the OutputDir property.
<target name="export" description="export code from svn">
<delete dir="${Delete.Dir}" />
<exec program="svn" commandline="export ${MySVN.Repos} ${MySVN.Dest}" />
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
<property name="OutputDir" value="${Output.Dir}"/>
</msbuild>
</target>
Just had a quick peek at a project, does OutputPath instead of OutputDir help?
Another option might be a web deployment project, which I like because it calls aspnet_compiler as well as the C# compiler, so it picks up issues which you may otherwise miss until deployment.
A build script for one of our projects uses this command to publish a web application:
msbuild.exe /t:_CopyWebApplication /p:Configuration=Release /p:OutDir=.\..\published\ /p:WebProjectOutputDir=.\..\published
(The current directory is set to the web app's project directory at this point, which is why no .csproj file is specified. The entire solution has already been rebuilt earlier in the script.)
By the sound of it, WebProjectOutputDir might be the property you need.
/t:_CopyWebApplication may also be important. I've not used NAnt so I don't know if you can pass this parameter with the msbuild task. You may need to use an exec task, like in this example: http://www.netomatix.com/development/wapwithnant.aspx. This example appears to rebuild and copy all in one go.
When using the task, the correct property name is OutDir, not OutputDir:
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
<property name="OutDir" value="${Output.Dir}"/>
</msbuild>
A source of confusion is that you're blending two distinct build systems. Your NAnt target is delegating all the work of figuring out how to publish your web application to the solution file, hence by extension to the csproj files it references.
csproj files are MsBuild files, so you should probably look there for how to direct your project output. This post by dave^2 might be helpful on that issue.
You can publish your web application wherever you want using NAnt, provided it's doing the publishing. You can do the same with MsBuild. The cause of your quandary is that NAnt is not doing the publishing in this case, and you're letting the csproj file determine the location of your web directory. So either bypass the csproj file and have NAnt (or MsBuild) publish the code; or modify the csproj file to publish the web application where you want; or make a second location for your web application and publish it there as well using your build tool.
AFAIK, those options are exhaustive.
Hmm, don't know how to do it with MSBuild in Nant, but using NAnt, I've done it previously like this:
<solution solutionfile="${build.dir}\solution.sln">
<webmap>
<map url="http://localhost/somdir/project.csproj"
path="c:\inetpub\wwwroot\somelocaldir" />
<webmap>
</solution>
But then, you're using the NAnt 'solution' task offcourse instead of using MSBuild directly.
edit:
I'm also having a look at some msbuild options;
If you set OutDir & OutputPath to the same value, what happens ?
Try something like this:
<property name="nant.settings.currentframework" value="net-3.5"/>
<msbuild project="${Solution.Filename}">
<property name="Configuration" value="Release"/>
<property name="OutDir" value="${Full.Path}\Output\\"/>
<property name="WebProjectOutputDir" value="${Full.Path}\Output\Web\\"/>
</msbuild>