Multiple Delphi versions using Jenkins: library paths - delphi

We are looking at migrating our build machine from FinalBuilder to Jenkins to fit in with the rest of our extended company.
One issue that I have noticed is that whereas Finalbuilder is able to extract the current library path from your current Delphi installs on the build machine, Jenkins relies on the information contained within the .dproj files.
Owing to known problems of the paths within the .dproj files being very specific to a users machine, we don't currently commit them to our repository, relying on Delphi to re-create them as required. This obviously doesn't play nice when the build machine is reliant on a full MSBUILD script being there in the first place.
We use a fair few third-party components (DevExpress suite alone having over 100 units), so including and maintaining all the .pas files with full paths in the .dpr isn't really an option for this.
Does anyone have a tried-and-tested solution for this?
My thoughts on options were:
setting the %PATH% for each build - adding the current Delphi library
for the relevant version (will this run into %PATH% length restrictions?)
Using a command-line parameter to pass the correct library path to MSBUILD (is this possible?)
Including the search path somehow in the source files with compiler directives (is this possible?)
Using a pre-compile step to create new .dproj files (something like http://delphi-divining.blogspot.co.uk/2012/10/dprojmaker-tool-to-create-delphi.html but it'll need to be command-line)
Edit: 5th idea:
Could we use dproj.local files for each project, stored in a separate repository (or in a separate path) and copied to the build machine pre-build? This would allow build machine paths to be stored safely away from clutzy commits.

You need to submit your .dproj file to source control.
You have a problem which is that your configuration is not complete. Any build system should be able to build your project using nothing but files in your source control, that is the ONLY way to ensure you are building the correct binary.
You have a number of options to make this work
You can use Environment variables in the Delphi IDE eg %ROOTFOLDER% could be set to C:\Development\MyDelphiProjects on one machine and C:\Dev on another and as long as everything is the same from that route it should be ok. Each dev and your build machine can set the required path. You may need vars for bpl paths also.
Enforce identical structures on client machines. Really how difficult is it to make all devs us C:\Development\Delphi as their root?
Make sure all search paths are relative. This can work, but there are always exceptions that cause problems so I have never managed to get this to work.
We used option 1 in a previous company and it worked very successfully, its a bit of a pain to set up but once setup you can be sure your build is correct.

I had the same problem when i choose Jenkins as a "build" environment. The solution is to use a MSBuild script with a build task inside. So in Jenkins instead of building the project directly, just build this script which gives you a lot more options, including the option to specify the paths for the project (you can to override the default IDE paths).
I'll post such a script tomorrow.
So in Jenkins when you configure MSBuild you have to specify the msbuild file, which will be Build.xml. For command line arguments i use only /v - verbosity and /t - target name.
The build script looks like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Compile" DependsOnTargets="CompileApp" />
<PropertyGroup>
<ExeOutputName>App.exe</ExeOutputName>
<ExeOutputPath>x:\exe</ExeOutputPath>
<DcuOutputPath>x:\dcu</DcuOutputPath>
<ForConfig>Release</ForConfig>
<ForPlatform>Win32</ForPlatform>
</PropertyGroup>
<Target Name="ResolveOutputPath">
<MakeDir Directories="$(ExeOutputPath)" />
<MakeDir Directories="$(DcuOutputPath)" />
<Delete Files="$(ExeOutputPath)\$(ExeOutputName)" />
<Delete Files="$(DcuOutputPath)\*.*" />
</Target>
<ItemGroup>
<AppUnitSearchPathItem Include="$(BDS)\lib\$(ForPlatform)\$(ForConfig)" />
<AppUnitSearchPathItem Include="C:\Users\builder\Documents\tmssoftware\TMS Component Pack" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\RemObjects Software\RemObjects SDK for Delphi\Dcu\$(ForPlatform)" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\RemObjects Software\RemObjects SDK for Delphi\Source" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\RemObjects Software\RemObjects SDK for Delphi\Source\CodeGen" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\RemObjects Software\RemObjects SDK for Delphi\Source\DataSnap" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\RemObjects Software\RemObjects SDK for Delphi\Source\ZLib" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\RemObjects Software\RemObjects SDK for Delphi\Source\Synapse" />
<AppUnitSearchPathItem Include="C:\Program Files (x86)\Embarcadero\RAD Studio\12.0\Components\EhLib\Lib\$(ForPlatform)\$(ForConfig)" />
...
</ItemGroup>
<ItemGroup>
<AppDefinesItem Include="App" />
<!-- AppDefinesItem Include="CompilerDirective" -->
</ItemGroup>
<ItemGroup>
<AppPropertiesItem Include="DCC_ExeOutput=$(ExeOutputPath)" />
<AppPropertiesItem Include="DCC_DcuOutput=$(DcuOutputPath)" />
<AppPropertiesItem Include="DCC_BuildAllUnits=true" />
<AppPropertiesItem Include="DCC_Optimize=true" />
<AppPropertiesItem Include="DCC_DebugInformation=0" />
<AppPropertiesItem Include="DCC_PentiumSafeDivide=true" />
<AppPropertiesItem Include="DCC_RangeChecking=true" />
<AppPropertiesItem Include="DCC_IntegerOverflowCheck=true" />
<AppPropertiesItem Include="DCC_WriteableConstants=true" />
<AppPropertiesItem Include="DCC_IOChecking=true" />
<AppPropertiesItem Include="DCC_AssertionsAtRuntime=false" />
<AppPropertiesItem Include="DCC_Warnings=true" />
<AppPropertiesItem Include="DCC_MapFile=3" />
<AppPropertiesItem Include="DCC_ConsoleTarget=false" />
</ItemGroup>
<Target Name="CompileApp" DependsOnTargets="ResolveOutputPath">
<PropertyGroup>
<AppUnitSearchPath>#(AppUnitSearchPathItem)</AppUnitSearchPath>
<AppDefines>#(AppDefinesItem)</AppDefines>
</PropertyGroup>
<ItemGroup>
<AppProperties Include="Config=$(ForConfig)" />
<AppProperties Include="Platform=$(ForPlatform)" />
<!-- AppProperties Include="LibraryPath=$(AppUnitSearchPath)" -->
<AppProperties Include="DelphiLibraryPath=$(AppUnitSearchPath)" />
<AppProperties Include="UnitSearchPath=$(AppUnitSearchPath)" />
<AppProperties Include="ResourcePath=$(AppUnitSearchPath)" />
<AppProperties Include="IncludePath=$(AppUnitSearchPath)" />
<AppProperties Include="ObjPath=$(AppUnitSearchPath)" />
<AppProperties Include="DCC_Define=$(AppDefines)" />
<AppProperties Include="#(AppPropertiesItem)" />
</ItemGroup>
<MSBuild Projects="App.dproj" Properties="#(AppProperties)" />
</Target>
</Project>
What is missing here is the versioning part which can be done from this script using a resource template...

Related

Project SDK (.Net Standard): Official way to create a nuget package that includes msbuild targets

Visual Studio 2019, .Net Standard 2.0
How do I include a custom msbuild targets file for the consuming project?
What is the official supported way of doing this?
I've already tried:
modifying the csproj file as per (Setting Nuget package target path for item in MSBuild project)
trying to specify a nuspec file as per (https://natemcmaster.com/blog/2017/11/11/build-tools-in-nuget/)
Nuspec:
<?xml version="1.0"?>
<package >
<metadata>
<id>TestingNugetContent</id>
<version>1.0.10</version>
<title>Blah</title>
<authors>Me</authors>
<owners>Me</owners>
<licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
<projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
<iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Blah</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>Copyright 2019</copyright>
<tags>Tag1 Tag2</tags>
</metadata>
<files>
<file src="Immutable\*.*" target="content/Immutable/" />
<file src="Build\*.*" target="build/netstandard2.0/" />
</files>
</package>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<NoPackageAnalysis>true</NoPackageAnalysis>
<NuspecFile>TestingNugetContent.nuspec</NuspecFile>
<IntermediatePackDir>$(MSBuildProjectDirectory)/bin/$(Configuration)/publish/</IntermediatePackDir>
<PublishDir>$(IntermediatePackDir)$(TargetFramework)/</PublishDir>
<NuspecProperties>publishDir=$([MSBuild]::NormalizeDirectory($(IntermediatePackDir)))</NuspecProperties>
<Version>1.0.10</Version>
</PropertyGroup>
<ItemGroup>
<Compile Remove="build\**" />
<EmbeddedResource Remove="build\**" />
<None Remove="build\**" />
</ItemGroup>
<ItemGroup>
<None Include="build\netstandard2.0\TestingNugetContent.targets" />
</ItemGroup>
<Target Name="PublishAll" BeforeTargets="GenerateNuspec">
<ItemGroup>
<_TargetFramework Include="$(TargetFrameworks)" />
</ItemGroup>
<MSBuild Projects="$(MSBuildProjectFullPath)" Targets="Publish" Properties="TargetFramework=%(_TargetFramework.Identity)" />
</Target>
</Project>
Checking the consumer's <***>.csproj.nuget.g.targets file the import project tag for this custom target is missing after installing the Nuget package
As per the docs, the props and targets file names must match the package id exactly. Your nuspec lists the <id> as TestingNugetContent, so the files must be TestingNugetContent.props and TestingNugetContent.targets. They should be either directly in the build/ folder in the package, or the build/<tfm>/ folder (I prefer to be more explicit, so I appriciate you used the netstandard2.0 TFM). Now, your csproj appears to specify a build\netstandard2.0\TestingNugetContent.targets, which looks correct, so I can only guess that it wasn't packed into the correct location somehow.
I don't currently have time to show an example on how to pack it, but you can inspect the contents of your nupkg using NuGet package explorer, or just opening it up as a zip file, see what's "wrong", then adjust your project and try again.
FYI, you shouldn't need to use a nuspec at all, you can use the MSBuild PackagePath metadata on items to specify where MSBuild items are packed. It's unclear to me what the purpose of your PublishAll target is supposed to be. If you added it as part of trying to get your targets file included, you can remove it.

MSBuild (for Delphi) Make-like functionality

I am trying to set up dependencies for a set of Delphi packages, such at if Application A uses Package 1 and Package 2, and Application B uses Package 1 only, then only Application A is rebuilt when Package 2 is changed.
At the moment, I am using a Group File, and each Package and Application is built (re-linked), even if it or one of it's dependencies is not changed.
UPDATE:
Unit1.pas is in Package 1
Unit2.pas is in Package 2
Application A uses Package 1 and Package 2
Application B use Package 2
What I want is that if I change Unit1.pas, then ONLY Package 1 and Application B is rebuilt. make will do this, but I can't work out how MSBUILD could be made to work.
My worked example uses a group file, which might be the underlying problem.
OK, I think I have sorted this out. Groupprojs will always rebuild / relink, so that is not what I want.
I have created a targets file, such that it defines the 4 projects and the dependencies of each. It has to be all the dependencies, both code and binary (i.e. the packages).
Msbuild has an attribute 'DependsOnTarget', but this doesn't achieve what I want, so I have added dependencies on both the code and packages. The final build.targets file looks like this ...
<Project ToolsVersion="3.5"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="Default">
<ItemGroup>
<Package1 Include=".\Package1\Package1.dproj"/>
<Package2 Include=".\Package2\Package2.dproj"/>
<App6 Include=".\App6\Project6.dproj"/>
<App7 Include=".\App7\Project7.dproj"/>
</ItemGroup>
<ItemGroup>
<Package1ExternalSourceFiles Include=".\Package1\*.pas" />
<Package2ExternalSourceFiles Include=".\Package2\*.pas" />
<App6ExternalSourceFiles Include=".\App6\*.pas" />
<App7ExternalSourceFiles Include=".\App7\*.pas" />
<Package1BinaryFiles Include=".\Package1\Win32\Release\Package1.bpl" />
<Package2BinaryFiles Include=".\Package2\Win32\Release\Package2.bpl" />
<App6BinaryFiles Include=".\App6\bin\Project6.exe" />
<App7BinaryFiles Include=".\App7\bin\Project7.exe" />
</ItemGroup>
<Target Name="App6" Inputs="#(App6ExternalSourceFiles);#(Package1BinaryFiles);#(Package2BinaryFiles)" Outputs="#(App6BinaryFiles)" DependsOnTargets="Package1;Package2">
<Message Text="App6" />
<MSBuild Projects="#(App6)"/>
</Target>
<Target Name="App7" Inputs="#(App7ExternalSourceFiles);#(Package1BinaryFiles)" Outputs="#(App7BinaryFiles)" DependsOnTargets="Package1">
<Message Text="App7" />
<MSBuild Projects="#(App7)"/>
</Target>
<Target Name="Package1" Inputs="#(Package1ExternalSourceFiles)" Outputs="#(Package1BinaryFiles)">
<Message Text="Package1" />
<MSBuild Projects="#(Package1)"/>
</Target>
<Target Name="Package2" Inputs="#(Package2ExternalSourceFiles)" Outputs="#(Package2BinaryFiles)">
<Message Text="Package2" />
<MSBuild Projects="#(Package2)"/>
</Target>
<Target Name="Build">
<CallTarget Targets="Package1;Package2;App6;App7"/>
</Target>
</Project>
Whether this is scalable I am not sure, we could potentially have a large number of projects and dependencies.
UPDATE: Actually the binary dependency on the Applications isn't needed, if they are shipped separate from the packages. Recompiling Package 1, without changing it's interface would mean that App6 and App7 are not rebuilt.

TFS Continuous Integration and Deployment

I have an web project Project A and a library Project B.
Project B is being built into Project A\Plugins.
The Problem is when Project A is being built via CI Project B isn't deployed with Project A. MSBuild params in CI are:
/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactstagingdirectory)\\" /t:ProjectA
I added the following piece of code into Project A.csproj to deploy Project B with A:
<ItemGroup>
<ProjectsToBuild Include="..\..\Plugins\**\*ProjectB*proj" />
</ItemGroup>
<Target Name="Plugin" AfterTargets="Build">
<Message Text="Building Plugins" Importance="High" />
<MSBuild Projects="#(ProjectsToBuild)" ContinueOnError="false" Properties="Configuration=$(Configuration)">
<Output ItemName="OutputFiles" TaskParameter="TargetOutputs" />
</MSBuild>
<CallTarget Targets="CopyPlugins"/>
</Target>
<PropertyGroup>
<PluginDirectory>Plugins</PluginDirectory>
</PropertyGroup>
<Target Name="CopyPlugins">
<Message Text="Copying plugins to $(_PackageTempDir)\Plugins\ ($(ProjectDir)$(PluginDirectory))" Importance="high" />
<CreateItem Include="$(ProjectDir)$(PluginDirectory)\**\*.*">
<Output TaskParameter="Include" ItemName="PluginFiles" />
</CreateItem>
<Copy DestinationFolder="$(_PackageTempDir)\Plugins\" SourceFiles="#(PluginFiles)" SkipUnchangedFiles="true" />
</Target>
In the log executing order seems fine (Copy after Build), but after being deployed the Plugin folder doesn't exist on the target server.
Am I missing anything?
The files need to be included in the project. Since the files are generated dynamically, you can add this code to project file (e.g. XX.csproj)
<Content Include="plugin\**\*.*" />
From your description, you would need to check in Project A\Plugins folder into TFS. But it's not suggested.
We suggest you make a project reference in the same solution or create NuGet for package management. It's recommended to build a nuget package for Project B, and publish this Nuget package, then restore the library to Project A when you build it.

MSBuild & TFS2010 Find Dlls

I am trying to create a custom MSBuild task that will run my nUnit tests either locally or during a TFS2010 build. The script works great locally but I can't seem to find the test dlls on the TFS build server. I'm using a task in the MSBuild.ExtensionPack to run the unit tests (again this works fine locally).
The Assemblies list is always empty. TargetDir shows the path is "C:\Builds\2\Product1\ci.product1.acme.com\Binaries\" which looks right to me. I also tried to kick off my target after the CoreCompile target instead within the AfterBuild target with no change.
I've probably made some stupid mistake but I'm in kill me mode at this point. Please help.
<PropertyGroup>
<RunTFSBuild>false</RunTFSBuild>
<SolutionDirectory>$(MSBuildProjectDirectory)\..</SolutionDirectory>
<ExtensionTasksPath>$(SolutionDirectory)\_shared\MSBuild\</ExtensionTasksPath>
<TPath>$(ExtensionTasksPath)MSBuild.ExtensionPack.tasks</TPath>
<NUnitOutputFile>$(SolutionDirectory)\nUnitResults.xml</NUnitOutputFile>
<NUnitOutputFileAsMsTest>$(SolutionDirectory)\nUnitResultsAsMsTestResults.xml</NUnitOutputFileAsMsTest>
<ToolPath>$(SolutionDirectory)\_shared\MSBuild\nUnit_2.5.7</ToolPath>
</PropertyGroup>
<Import Project="$(TPath)" />
<Target Name="AfterBuild">
<CallTarget Condition="$(RunTFSBuild)!='true'" Targets="NUnitTestRunner" />
<CallTarget Condition="$(RunTFSBuild)=='true'" Targets="NUnitTestRunner;TFSNUnitTestRunner" />
</Target>
<Target Name="NUnitTestRunner">
<ItemGroup >
<Assemblies Include="$(SolutionDirectory)\**\bin\$(Configuration)\*.nUnit.Tests.dll" />
</ItemGroup>
<ItemGroup Condition="$(RunTFSBuild)=='true'">
<Assemblies Include="$(TargetDir)\**\*.nUnit.Tests.dll" />
</ItemGroup>
<Message Text="SolutionDirectory=$(SolutionDirectory)" />
<Message Text="ExtensionTasksPath=$(ExtensionTasksPath)" />
<Message Text="TargetDir=$(TargetDir)" />
<Message Text="TPath=$(TPath)" />
<Message Text="NUnitOutputFile=$(NUnitOutputFile)" />
<Message Text="Running nUnit tests from: $(Assemblies)" />
1) Try to change property RunTFSBuild
<RunTFSBuild Condition="'$(RunTFSBuild)'==''">false</RunTFSBuild>
2) Replace sections (it may be just cleaning of project, but it may be very important due to some limitations of CallTarget):
<Target Name="AfterBuild" DependsOnTargets="NUnitTestRunner;TFSNUnitTestRunner" />
<Target Name="TFSNUnitTestRunner"
Condition="$(RunTFSBuild)=='true'">
<!-- TFSNUnitTestRunner Body -->
</Target>
3) If 1) and 2) willn't be helpful try to set verbose of the build to diagnostic (msbuld key /v:diag). Find all calls of TFSNUnitTestRunner in the log and you will see what is actually happened on TFS.
EDIT: *Assemblies* is declared as ItemGroup. Use #(Assemblies) to access items:
<Message Text="Running nUnit tests from: #(Assemblies)" />
Property $(Assemblies) will always be empty in your case.

TFS2008 recursively copying files not always works (compiling vs2003) (AfterCompile target)

I'm having some strange problems copying files in a custom script in TFS2008 without SP1, I have to run the build several times to get the files copied (most of the times its in the second build that i get the files), let me give you the details:
This is happening with ASP sites and VS2003 Web solutions, (vs2008 solutions are OK)
In ASP I have a dummy 2008 solution, the build compiles this dummy, I override AfterCompile and in there I copy all the files to the drop location
In VS 2003 i have also a dummy 2008 solution, the build first compiles the dummy, I override AfterCompile, use "Exec" and "Command" to compile the 2003 solution and then copy the files to the drop location.
As you can see both approaches are similar, I'm not having problems with the builds per se, my problem is reproducible in two ways (and yes, i do check out, update, check in and then test the build):
Create a new build, configure the script, run the build the first time, some DLL's in the bin folder are not copied, run the build for the second time and i get all the files.
Build already configured and running OK, add some file to the project (this mostly happens with the ASP sites), run the build, don't get this new file, run the build again and i get this new file.
Here is my build script for a VS2003 Web solution as an example
<PropertyGroup>
<TasksPath>D:\BuildTools\</TasksPath>
<VS2003Devenv>D:\Archivos de programa\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.com</VS2003Devenv>
<VS2003VirtualFolder>CnbvPifWeb</VS2003VirtualFolder>
<VS2003Suba>Cnbv.Pif.Web</VS2003Suba>
<VS2003Project>Cnbv.Pif.Web</VS2003Project>
<VS2003WebSiteName>Sitio Web predeterminado</VS2003WebSiteName>
<VS2003Configuration>Release</VS2003Configuration>
<VS2003Branch>Desarrollo</VS2003Branch>
<VS2003RelativePath>$(SolutionRoot)\$(VS2003Branch)\$(VS2003Suba)\</VS2003RelativePath>
<VS2003SolutionPath>$(VS2003RelativePath)Cnbv.Pif.Web.sln</VS2003SolutionPath>
<VS2003LocalFolder>$(VS2003RelativePath)Sources\$(VS2003Project)\</VS2003LocalFolder>
<VS2003Output>$(BinariesRoot)\$(VS2003Project)\</VS2003Output>
<VS2003CachePath>C:\Documents and Settings\srvfoundation\VSWebCache\230-2555-CPU015\</VS2003CachePath>
<VS2003ProjectExtension>vbproj</VS2003ProjectExtension>
<VS2003CacheFile>$(VS2003CachePath)$(VS2003VirtualFolder)\_vti_pvt\$(VS2003Project).$(VS2003ProjectExtension).cache</VS2003CacheFile>
</PropertyGroup>
<Import Project="$(TasksPath)Microsoft.Sdc.Common.tasks"/>
<UsingTask TaskName="Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory" AssemblyFile="Microsoft.Sdc.Tasks.dll" />
<UsingTask TaskName="Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory" AssemblyFile="Microsoft.Sdc.Tasks.dll" />
<ItemGroup>
<!--list of ouput files, excluding .DLL outside bin and some other files-->
<VS2003OutputFiles
Include="$(VS2003LocalFolder)**\*.*"
Exclude="$(VS2003LocalFolder)**\*.vb;$(VS2003LocalFolder)**\*.cs;$(VS2003LocalFolder)**\*.resx;$(VS2003LocalFolder)**\*.vspscc;$(VS2003LocalFolder)**\*.csproj;$(VS2003LocalFolder)**\*.vbproj;$(VS2003LocalFolder)**\*.scc;$(VS2003LocalFolder)**\*.webinfo;$(VS2003LocalFolder)**\*.snk;$(VS2003LocalFolder)**\*.dll;$(VS2003LocalFolder)**\*.exe;" />
<!-- copy dll to bin folder -->
<VS2003OutputBinFiles
Include="$(VS2003LocalFolder)bin\*.dll"/>
</ItemGroup>
<Target Name="AfterCompile">
<Message Text="Deleting cache file" />
<Microsoft.Build.Tasks.Delete
Condition="Exists('$(VS2003CacheFile)')"
Files="$(VS2003CacheFile)" />
<Message Text="Creating virtual folder $(VS2003VirtualFolder) in IIS in local path $(VS2003LocalFolder)" />
<Web.WebSite.CreateVirtualDirectory
VirtualDirectoryName="$(VS2003VirtualFolder)"
Path="$(VS2003LocalFolder)"
WebSiteName="$(VS2003WebSiteName)" />
<Message Text="Compiling $(VS2003Project) in $(VS2003Branch)" />
<Exec
Command=""$(VS2003Devenv)" "$(VS2003SolutionPath)" /build $(VS2003Configuration) /out "$(VS2003LocalFolder)$(VS2003Project).log" "/>
<Message Text="Eliminando la carpeta virtual $(VS2003VirtualFolder) en IIS" />
<Web.WebSite.DeleteVirtualDirectory
WebSiteName="$(VS2003WebSiteName)"
VirtualDirectoryName="$(VS2003VirtualFolder)" />
<MakeDir Condition="!Exists('$(VS2003Output)')" Directories="$(VS2003Output)" />
<Message Text="Copying output files #(VS2003OutputFiles)" />
<Copy
SourceFiles="#(VS2003OutputFiles)"
DestinationFiles="#(VS2003OutputFiles->'$(VS2003Output)%(RecursiveDir)%(Filename)%(Extension)')"
/>
<MakeDir Condition="!Exists('$(VS2003Output)bin\')" Directories="$(VS2003Output)bin\" />
<Message Text="Copying DLL to bin folder #(VS2003OutputBinFiles)" />
<Copy
SourceFiles="#(VS2003OutputBinFiles)"
DestinationFiles="#(VS2003OutputBinFiles->'$(VS2003Output)bin\%(Filename)%(Extension)')"
/>
<OnError ExecuteTargets="VS2003Fail" />
</Target>
<Target Name="VS2003Fail">
<Message Text="Copying log file $(VS2003RelativePath)$(VS2003Project).log" />
<Copy Condition="Exists('$(VS2003RelativePath)$(VS2003Project).log')" SourceFiles="$(VS2003RelativePath)$(VS2003Project).log" DestinationFolder="$(DropLocation)\$(BuildNumber)" />
<CallTarget ContinueOnError ="true" Targets ="CreateWorkItemWhenPartialSucceed" />
</Target>
<Target
Name="CreateWorkItemWhenPartialSucceed"
Condition=" '$(SkipWorkItemCreation)'!='true' and '$(IsDesktopBuild)'!='true' ">
<Message Text="ejecutando work" />
<PropertyGroup>
<WorkItemTitle>$(WorkItemTitle) $(BuildNumber)</WorkItemTitle>
<BuildLogText>$(BuildlogText) <ahref='file:///$(DropLocation)\$(BuildNumber)\BuildLog.txt'>$(DropLocation)\$(BuildNumber)\BuildLog.txt</a >.</BuildLogText>
<ErrorWarningLogText Condition="!Exists('$(MSBuildProjectDirectory)\ErrorsWarningsLog.txt')"></ErrorWarningLogText>
<ErrorWarningLogText Condition="Exists('$(MSBuildProjectDirectory)\ErrorsWarningsLog.txt')">$(ErrorWarningLogText) <a href='file:///$(DropLocation)\$(BuildNumber)\ErrorsWarningsLog.txt'>$(DropLocation)\$(BuildNumber)\ErrorsWarningsLog.txt</a >.</ErrorWarningLogText>
<WorkItemDescription>$(DescriptionText) %3CBR%2F%3E $(BuildlogText) %3CBR%2F%3E $(ErrorWarningLogText)</WorkItemDescription>
</PropertyGroup>
<CreateNewWorkItem
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
BuildNumber="$(BuildNumber)"
Description="$(WorkItemDescription)"
TeamProject="$(TeamProject)"
Title="$(WorkItemTitle)"
WorkItemFieldValues="$(WorkItemFieldValues)"
WorkItemType="$(WorkItemType)"
ContinueOnError="true" />
</Target>
When I see the ouput of this message in the log
<Message Text="Copying DLL to bin folder #(VS2003OutputBinFiles)" />
the first time i see just the name of one file, the second time it prints all the correct files, and the same happens with the ASP sites, if I add a file i see the file in the output in the second build.
I hope you can help me out figuring this out, thanks a lot.
Juan Zamudio
this was the answer in the tfs forum by OsirisJakob
The problem is that you define your item groups at the root level. This means that they are evaluated immediately when the project file loaded. What you want is for them to be evaluated when the AfterCompile target is executed.
Since you are running TFS 2008, you can solve this problem by moving the item groups into the AfterCompile target (a.k.a. Dynamic item groups). This will cause the item group to be evaluated by the time the AfterCompile target is executed, and will give you the correct result.

Resources