Get static content to copy to TFS Build Drop location - visual-studio

I'm using TFS 2013 / VS 2013.
For various reasons, I need some static content copied to a TFS build drop location. I've created a custom project type that simply copies a directory structure/files to an output path.
I would expect that TFS would then take everything in the output path and copy it to the drop location, but it's not. No files from my project show up in the drop location.
Here is my proj file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>db\</OutputPath>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Target Name="Build">
<Copy SourceFiles="#(Content)" DestinationFiles="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)')" />
</Target>
<Target Name="Clean">
<Exec Command="rd /s /q $(OutputPath)" Condition="Exists($(OutputPath))" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
<ItemGroup>
<Content Include="**\*.*" Exclude="db\**\*.*;*.csproj;*.rgdbproj;*.vspscc" />
</ItemGroup>
</Project>
When I build this in Visual Studio, I get the desired directory structure to appear in the /db folder.

In TFS 2013 you can specify a pre-build and post-build script. You can run PowerShell scripts to copy the files. See below question on how to do it. You will have to use TfvcTemplate.12.xaml for that.
Where can we open the `Post-build script` box of a build process template?

I was able to resolve this issue by changing all my references from OutputPath to OutDir. Apparently, OutputPath is deprecated and OutDir is what TFS Build pays attention to. Here is my final build script:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>db\</OutputPath>
<OutDir>db\</OutDir>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Target Name="Build">
<Copy SourceFiles="#(Content)" DestinationFiles="#(Content->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')" />
</Target>
<Target Name="Clean">
<Exec Command="rd /s /q $(OutDir)" Condition="Exists($(OutDir))" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
<ItemGroup>
<Content Include="**\*.*" Exclude="db\**\*.*;*.csproj;*.rgdbproj;*.vspscc" />
</ItemGroup>
</Project>

In your build file, you can define a target for copying files:
<Target Name="CopySomeFiles">
<ItemGroup>
<SomeFiles Include="$(SourceFolder)\*.*"></SomeFiles>
</ItemGroup>
<Copy SourceFiles="#(SomeFiles)" DestinationFolder="$(DestFolder)" SkipUnchangedFiles="false"/>
</Target>
You can then add this target where you want, e.g. after compile:
<Target Name="AfterCompile"
DependsOnTargets="CopySomeFiles">
</Target>

Related

Minimal Visual Studio project with custom target only: Disable IntelliSense and allow single file processing?

For our Visual Studio Solution, I want to create one project that just generates some files with custom scripts. I have a somewhat-working solution, further reduced to provide as an example here:
This is my minimal.vcxproj:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Universal|Win32">
<Configuration>Universal</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<None Include="common.fooconf" />
</ItemGroup>
<ItemGroup>
<Foo Include="file1.foo" />
<Foo Include="file2.foo" />
</ItemGroup>
<Target Name="Build" Inputs="common.fooconf;#(Foo)" Outputs="Output\%(Foo.Filename).bar">
<MakeDir Directories="Output" />
<Exec Command='TYPE "common.fooconf" "%(Foo.FullPath)" > "Output\%(Foo.Filename).bar"' />
</Target>
<Target Name="Clean">
<RemoveDir Directories="Output" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
</Project>
And I need this minimal.vcxproj.filters to make the three contained files show up in VS's solution explorer:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="common.fooconf"/>
</ItemGroup>
<ItemGroup>
<Foo Include="file1.foo"/>
<Foo Include="file2.foo"/>
</ItemGroup>
</Project>
Now there are still two problems:
When loading the project, I get error : Designtime build failed for project '[...]\minimal.vcxproj' configuration 'Universal|Win32'. IntelliSense might be unavailable. Check *.designtime.log files in your %TEMP% directory. If I look up in the log, the error is error MSB4057: The target "GetClCommandLines" does not exist in the project. Of course IntelliSense doesn't make sense here (TYPE is just a placeholder for a custom script), but there must be a way of getting rid of this warning.
Build (e.g. via F7) works like a charm (even incremental), but it would be really important for us to trigger processing single files (e.g. CtrlF7). But I can't find out how to make this work.
Using Visual Studio 2019, I needed to add the following targets to have the project load without warnings. This was done by checking the designtime logs and incrementally adding targets, until there were no more warnings.
<Target Name="GetProjectDirectories" />
<Target Name="GetClCommandLines" />
<Target Name="GetGeneratedFiles" />
<Target Name="GetAssemblyReferences" />
<Target Name="GetWinMDReferences" />
<Target Name="GetComReferences" />
<Target Name="GetSDKReferences" />
<Target Name="GetProjectReferences" />
<Target Name="GetForeignReferences" />
<Target Name="GetResolvedReferences" />
<Target Name="GetResolvedSDKReferences" />
<Target Name="GetProjectReferencesInfo" />
<Target Name="GetResolvedLinkLibs" />

How to create a MSBuild Target that will only run if necessary

I have been experimenting with MSBuild to create custom targets. I am currently attempting to add support for compiling file with the JAWS script compiler. This is what I have so far.
Scripts.props
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DEVDOCS_DIR>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\..\..\DevDocs'))</DEVDOCS_DIR>
<BUILTIN_MASTER>$(DEVDOCS_DIR)\jsd\enu\builtin_master.jsd</BUILTIN_MASTER>
<TOOLBOX_DIR>$(DEVDOCS_DIR)\Toolbox</TOOLBOX_DIR>
<JAWS_VER>17.0</JAWS_VER>
<!-- If perl.exe is not in your path, set the following variable to the full path and file name of perl.exe.
Optionally you could set the PERL_EXE environment variable. -->
<PERL_EXE Condition="'$(PERL_EXE)'==''">perl.exe</PERL_EXE>
<!-- If scompile.exe is not in your path, set the following variable to the full path and file name of scompile.exe.
Optionally you could set the SCOMPILE_EXE environment variable. -->
<SCOMPILE_EXE Condition="'$(SCOMPILE_EXE)'==''">scompile.exe</SCOMPILE_EXE>
</PropertyGroup>
</Project>
Scripts.targets
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GenerateBuiltinJSD">
<Exec Command=""$(PERL_EXE)" compilejsd.pl -v Jaws/$(JAWS_VER) -o "$(MSBuildProjectDirectory)\builtin.jsd" "$(BUILTIN_MASTER)"" WorkingDirectory="$(TOOLBOX_DIR)" />
</Target>
<Target Name="CompileScripts">
<Exec Command=""$(SCOMPILE_EXE)" /d "%(ScriptSourceFiles.FullPath)"" Outputs="%(ScriptSourceFiles.RootDir)%(ScriptSourceFiles.Directory)%(ScriptSourceFiles.Filename).jsb" WorkingDirectory="$(MSBuildProjectDirectory)" />
</Target>
<Target Name="Clean">
<ItemGroup>
<JSBFiles Include="$(MSBuildProjectDirectory)\*.jsb" />
</ItemGroup>
<Delete Files="#(JSBFiles)" />
</Target>
<Target Name="Build">
<CallTarget Targets="GenerateBuiltinJSD;CompileScripts" />
</Target>
</Project>
Scripts.vcxproj
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ScriptSourceFiles Include="Default.jss" />
</ItemGroup>
<ItemGroup>
<ScriptMessageFiles Include="Default.jsm" />
</ItemGroup>
<ItemGroup>
<ScriptHeaderFiles Include="HJConst.jsh" />
</ItemGroup>
<ItemGroup>
<ScriptDocumentationFiles Include="default.jsd" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B2C4363D-D228-425D-AB04-38997EA229C0}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="Scripts.props" />
</ImportGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(MSBuildProjectDirectory)\Scripts.targets" />
</Project>
This works up to a point. The problem is that when I do a build, it rebuilds all the script files, even if the relevant source file has not changed.
My question is how can I modify this so that each file is only built if necessary?
The feature you are asking about is called Incremental Build, and hopefully it is supported by MSBuild utility. All you have to do is to specify inputs and outputs for a target you are planning to skip if no source files are changed.
To specify inputs and outputs for a target: Use the Inputs and Outputs attributes of the Target element. For example:
<Target Name="Build"
Inputs="#(CSFile)"
Outputs="hello.exe">
MSBuild can compare the timestamps of the input files with the timestamps of the output files and determine whether to skip, build, or partially rebuild a target.
To succeed in your task you have to properly generate the Inputs and Outputs of your target. This can be achieved by using special targets, which will populate the required items, or you can use the transformation explicitly:
<Target Name="CompileScripts"
Inputs="#(ScriptSourceFiles)"
Outputs="#(ScriptSourceFiles->'%(RootDir)%(Directory)%(FileName).jsb')">
<Exec Command=""$(SCOMPILE_EXE)" /d "%(ScriptSourceFiles.FullPath)"" Outputs="%(ScriptSourceFiles.RootDir)%(ScriptSourceFiles.Directory)%(ScriptSourceFiles.Filename).jsb" WorkingDirectory="$(MSBuildProjectDirectory)" />
</Target>
(Also note that instead of referencing %(RootDir) item metadata you should use $(OutDir) property, because it is more straightforward, but you should ensure that this property is specified and exists when the target is executed).

Exclude `.js` files but not '.min.js' files from MSBuild publish

Using Visual Studio and MSBuild I would like to be able to exclude all .js files and include all .min.js files in my deployments.
I know this can be achieved using the file properties in visual studio, but this is not an option as there are far too many files.
I have the following PublishProfile in my Visual Studio project. Everything works just fine apart from the <ItemGroup>
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<LastUsedBuildConfiguration>Delpoy-Static</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>True</ExcludeApp_Data>
<publishUrl>\\***\wwwroot\***.com\static</publishUrl>
<DeleteExistingFiles>False</DeleteExistingFiles>
</PropertyGroup>
<!--This does not work, but gives the idea of what I want to achieve-->
<ItemGroup>
<Deploy Exclude="**\*.js" Include="**\*.min.js" />
</ItemGroup>
</Project>
Can this be achieved using the PublishProfile? If so, how?
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<!-- ... -->
</PropertyGroup>
<Target Name="BeforeBuild">
<ItemGroup>
<Minified Include="**\*.min.js" />
<Maxified Include="**\*.js" Exclude="#(Minified)" />
<Content Remove="#(Maxified)" />
</ItemGroup>
</Target>
</Project>
Edit:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<!-- ... -->
</PropertyGroup>
<ItemGroup>
<Minified Include="**\*.min.js" />
<Maxified Include="**\*.js" Exclude="#(Minified)" />
</ItemGroup>
<PropertyGroup>
<ExcludeFoldersFromDeployment>bin</ExcludeFoldersFromDeployment>
<ExcludeFilesFromDeployment>#(Maxified);Web.config</ExcludeFilesFromDeployment>
</PropertyGroup>
</Project>
If you want to exclude files you can place the files to be excluded in the the ExcludeFromPackageFiles item group. In your case you want to take all .js files and exclude all but those that are *.min.js. To do that in your .pubxml file add the following in your .pubxml file .
<ItemGroup>
<ExcludeFromPackageFiles Include="js\**\*.js" Exclude="js\**\*min*.js">
<FromTarget>Project</FromTarget>
</ExcludeFromPackageFiles>
</ItemGroup>
Note: this snippet assumes that your .js files are in a folder named js.
work to me:
Edit .csproj file
Find section MinifyJavaScriptAndCSS
Edit property Exclude in JS tag
Add directory or files to ignore during publish
<Target Name="MinifyJavaScriptAndCSS" AfterTargets="CopyAllFilesToSingleFolderForPackage" Condition="'$(Configuration)'=='Release'">
<ItemGroup>
<!-- Every .js file (exclude *.min.js and *.vsdoc.js files) -->
<JS Include="$(_PackageTempDir)\**\*.js" Exclude="$(_PackageTempDir)\**\*.min.js;$(_PackageTempDir)\**\*vsdoc.js;" />
<CSS Include="$(_PackageTempDir)\**\*.css" Exclude="$(_PackageTempDir)\**\*.min.css" />
</ItemGroup>
<AjaxMin JsKnownGlobalNames="jQuery,$" JsSourceFiles="#(JS)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js" CssSourceFiles="#(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css" />
<Message Text="[pcv] $(MSBuildProjectName) -> Minified: #(JS)" Importance="high" />
<Message Text="[pcv] $(MSBuildProjectName) -> Minified: #(CSS)" Importance="high" />

Visual Studio Post build command line Deployment

In Visual Studio am creating a post-build event for Deploying using
md "$(SolutionDir)Deploy\bin"
which created the bin folder inside Deploy folder, inside my Solution.
How do I point this to the folder in some remote machine (where I have the web server)?
$(SolutionDir) to some other folder on a remote machine?
It may look simple to you. :) This is the first time am trying this stuff.
Thanks
The easiest way is to replace $(SolutionDir) with \\server\share
Just as an alternative, I like to keep my .sln and .csproj files "clean".
Then use a second (mini) .msbuild ( which is just a .xml file) to build the .sln, and then do these copy type events as a second action.
Here is a basic example:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapper">
<PropertyGroup>
<WorkingCheckout>.</WorkingCheckout>
<WorkingOutputs>m:\working\outputs</WorkingOutputs>
</PropertyGroup>
<Target Name="AllTargetsWrapper">
<CallTarget Targets="Clean" />
<CallTarget Targets="Build" />
<CallTarget Targets="CopyItUp" />
</Target>
<Target Name="Clean">
<RemoveDir Directories="$(WorkingOutputs)" />
<MakeDir Directories="$(WorkingOutputs)" />
<Message Text="Cleaning done" />
</Target>
<Target Name="Build">
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"/>
</MSBuild>
<Message Text="Build completed" />
</Target>
<!-- -->
<Target Name="CopyItUp" >
<ItemGroup>
<MyExcludeFiles Include="$(WorkingCheckout)\**\SuperSecretStuff.txt" />
<MyExcludeFiles Include="$(WorkingCheckout)\**\SuperSecretStuff.doc" />
</ItemGroup>
<ItemGroup>
<MyIncludeFiles Include="$(WorkingCheckout)\MyCsProject\bin\$(Configuration)\**\*.*" Exclude="#(MyExcludeFiles)"/>
</ItemGroup>
<Copy
SourceFiles="#(MyIncludeFiles)"
DestinationFiles="#(MyIncludeFiles->'$(WorkingOutputs)\%(RecursiveDir)%(Filename)%(Extension)')"
/>
</Target>
</Project>

Why doesn't my MSBuild project build incrementally?

I have a project which generates a class from its XSD definition, then builds it. My problem is that, even though I specify the inputs and outputs of my Xsd target, it still gets executed every time I build the Visual Studio solution. What could be the problem here?
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" InitialTargets="Xsd" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- snip -->
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="request.cs">
<DependentUpon>request.xsd</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="key.snk" />
</ItemGroup>
<ItemGroup>
<Xsd Include="request.xsd" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="Xsd" Inputs="#(Xsd)" Outputs="request.cs">
<Exec Command='"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\xsd.exe" "#(Xsd)" /c /o:..\.. /n:Order.Messaging' />
</Target>
</Project>
Remove Xsd target from InitialTargets
Initial targets are typically used for error checking.
and add dependency to BeforeBuild Target:
<Target Name="BeforeBuild" DependsOnTargets="Xsd" />

Resources