Visual Studio script project - visual-studio-2010

What I would like to do is create a super-simple VS2010 project that would generate a file using a batch script. This project will be necessary as a dependency for another. Is there a simple way of doing this?

Good question, Don. What you could do is create a new directory in your solution and place your script there. You would then create an MSBuild script called MyProject.csproj like the following:
<?xml version="1.0" encoding="utf-8"?>
<Project
ToolsVersion = "4.0"
DefaultTargets = "Build"
xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"
>
<ItemGroup>
<InputTxt Include="$(MSBuildProjectDirectory)\input.txt" />
<OutputTxt Include="$(MSBuildProjectDirectory)\output.txt" />
<Script Include="$(MSBuildProjectDirectory)\script.cmd" />
</ItemGroup>
<Target
Name = "Build"
Inputs = "#(InputTxt);#(Script)"
Outputs = "#(OutputTxt)"
>
<Exec Command = '"#(Script)" "#(InputTxt)" "#(OutputTxt)"' />
</Target>
</Project>

Related

Update Post-Build with custom nuget package

I need help to be able to update the post-build event of a project thanks to a custom nuget package.
I've created a package thanks to a nuspec file that include a .targets file :
<file src="*.targets" target="build"/>
Here is the .targets file :
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="AfterBuild" AfterTargets="Build" >
<Message Text="Hello World" Importance="high" />
</Target>
</Project>
Actually, the file is read when i install the package (if i delete some '>', i've an error).
But the .csproj isn't updated (so, nothing in the post-build event textbox).
Did i miss something ?
I agree with Matt Ward. From NuGet 2.5, NuGet recognizes a new top-level folder: \build.
Within the \build folder, you can provide a “.props” file and/or a
“.targets” file that will be automatically imported into the project.
For this convention, the file name must match your package id with
either a “.props” or “.targets” extension.
Please refer to the MSBuild Integration part in following document:
http://blog.nuget.org/20130426/native-support.html
And after install the package, you will see a import node in .csproj which import the package.targets file. Then when build your project, you will see "Hello World" text in output window.

Can I build multiple configurations of a project within one solution configuration?

I would like to build the same project twice in the same solution configuration, varying some #define flags to toggle features. Both binaries will be deployed with different names.
The solutions that I know could work:
Add a solution configuration - But I will then need to build the solution twice, which I would prefer to avoid. Both project configurations will always be built.
Copy the project - But then I have the overhead of maintaining a new project when I only want to maintain a different configuration.
Batch build - I avoid using batch build as I use both devenv for local development and msbuild for continuous integration.
Any other ideas or suggestions?
Just figured out a way to do what you asked for. Create one msbuild file (I named mine multiple.proj) and add the script below.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Choose>
<When Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<ItemGroup>
<ProjectToBuild Include="$(MSBuildProjectName).csproj">
<Properties>Configuration=Release</Properties>
</ProjectToBuild>
</ItemGroup>
</When>
</Choose>
<Target Name="BeforeBuild">
<Message Text="Building configuration $(Configuration)..." />
</Target>
<Target Name="AfterBuild">
<MSBuild Projects="#(ProjectToBuild)"/>
</Target>
</Project>
</type>
</this>
Import the script on your projects (csproj or vbproj):
<Import Project="..\multiple.proj" />
This script tells msbuild to build again your project with another configuration as an AfterBuild event. I used Debug/Release to make the example, but you can easily change the script to support other configurations, or make the decision to build again based on other variables.
Be careful because you're running two builds at once, so build errors can be harder to understand.
Hope this helps.

How to turn off caching of build definitions in Visual studio

In project file I import my own target file
<Import Project="Build\CopyDependencies.target" />
and later I call target from that target file
<CallTarget Targets="CopyDependencies" UseResultsCache="false" />
If I edit CopyDependencies.target file I have to reload whole solution and only then changes to CopyDependencies.target take effect. I believe it is some sort of build definitions caching in Visual Studio? If it is, maybe it can be turned off?
Thanks #KazR
Here is a smaller Solution that you can insert into your .csproj file
<Target Name="AfterBuild">
<PropertyGroup>
<TempProjectFile>Build.$([System.Guid]::NewGuid()).proj</TempProjectFile>
</PropertyGroup>
<Copy SourceFiles="Build.proj" DestinationFiles="$(TempProjectFile)" />
<MSBuild Projects="$(TempProjectFile)" />
<ItemGroup>
<TempProjectFiles Include="Build.????????-????-????-????-????????????.proj"/>
</ItemGroup>
<Delete Files="#(TempProjectFiles)" />
</Target>
Problem solved
I don't know how you would disable the VS cache, however I may have a workaround that would allow you to edit the build target without having to reload the solution.
You could use the MSBuild task in your proj file to call a wrapper target that copies your CopyDependencies.target file to CopyDependencies.[RandomNumber].target, then invokes your CopyDependencies target in the newly created file, and finally deletes it.
This would force VS to reload the target on each invocation as the filename is different.
Here's an example:
myProject.proj
Add this to the AfterBuild target:
<MSBuild Projects="Wrapper.target" Targets="MyWrappedTarget" UnloadProjectsOnCompletion="true"/>
Wrapper.target
Here we have the target that will - at build time - copy the real target file and invoke the desired build target within it (I've used an inline c# task which is only available in MSBuild 4.0):
<UsingTask TaskName="RandomNumber" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<Number ParameterType="System.Int32" Output="true"/>
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<!-- CDATA -->
Random rndGenerator = new Random();
Number = rndGenerator.Next(Int32.MaxValue);
<!-- CDATA -->
</Code>
</Task>
</UsingTask>
<Target Name="MyWrappedTarget">
<Message Text="MyWrappedTarget target called"/>
<RandomNumber>
<Output TaskParameter="Number" PropertyName="FileNumber"/>
</RandomNumber>
<PropertyGroup>
<CopiedTarget>inner.test.$(FileNumber).target</CopiedTarget>
</PropertyGroup>
<Copy SourceFiles="inner.test.target" DestinationFiles="$(CopiedTarget)"/>
<MSBuild Projects="$(CopiedTarget)" Targets="_innerTestTarget"/>
<Delete Files="$(CopiedTarget)"/>
</Target>
inner.test.target
This contains the real build target you want to execute, in this example it's a simple file copy.
<Target Name="_innerTestTarget">
<Message Text="This is a inner test text message"/>
<Copy SourceFiles="x.txt" DestinationFiles="x1.txt"/>
</Target>
This isn't production ready, but hopefully illustrates my point.
With this (slightly convoluted) process in place, you can change the inner.test.target file without having to reload the solution in VS.
Here's a solution that doesn't require any MSBuild scripting at all.
I noticed that unloading and reloading a project doesn't get around the cache, but closing and reopening the solution does. In addition, Visual Studio will prompt you to reload the solution if it notices the .sln file has changed. And finally, this superuser question explains how to touch a file in Windows.
Putting these together, I added a Visual Studio external tool to touch the current solution file. Here's how:
Select TOOLS > External Tools ...
Click the Add button to add a new tool.
Set properties as follows:
Title: Reload Solution
Command: cmd.exe
Arguments: /c copy "$(SolutionFileName)"+>nul
Initial directory: $(SolutionDir)
and turn on Use Output window
Click OK to close the External Tools window
Now if you have made changes to your MSBuild files, just select TOOLS > Reload Solution and all your build files will be reloaded.
I'm using Windows 7 64-bit and Visual Studio 2012 Express for Windows Desktop.
I have a different solution, not involving temporary files:
Include.targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foobar">
<Copy SourceFiles="test.source" DestinationFiles="testFoobar.dest" />
</Target>
</Project>
Project file:
....
<Target Name="BeforeBuild">
<Exec Command="$(MSBuildToolsPath)\MSBuild.exe Include.targets /t:Foobar" ContinueOnError="false" />
</Target>
....
in this case VS does not recognize the MSBuild command, and does not cache the file.
happy coding!
Before running MSBuild I run this to clear the download cache:
call "%VS120COMNTOOLS%vsvars32.bat"
echo Clear download cache
gacutil -cdl

Is there such a thing as a "content/data only project" in visual studio

I have a bunch of ancillary XML and XSLT files that I want to edit and manage in visual studio.
The files do not logically belong under any code project in my solution and so in order to group them neatly, I have created a "dummy" C# dll project in visual studio and disabled it from building in Debug / release builds).
I wondered if there was a nicer way of achieving the same result (i.e. having all the files visible in solution explorer). What I think really want is a visual studio project type of "content only" but such a thing does not exist (or have I not looked hard enough?).
I have toyed with the idea of adding the files as solution items but then they seem harder to manage because creating a new "solution item folder" does not actually create a folder on disk.
Any one have any ideas?
Visual Studio 2015 has a project type called "Shared Project" which is essentially a content only project with no targets. It's listed under Visual C# but it can be used for any files.
A work colleague has come up with a solution.
He has suggested hand editing the project to remove the DefaultTargets from the Project (and delete a load of now unused properties).
MSBuild complains if there are no targets in the project so he has added three empty targets.
The final project looks something like this
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{541463A7-7CFA-4F62-B839-6367178B16BD}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
... files ...
</ItemGroup>
<ItemGroup>
... files ...
</ItemGroup>
<Target Name="Build"/>
<Target Name="Rebuild"/>
<Target Name="Clean"/>
</Project>
Admittedly, this solution requires more fiddling that I would have liked but seems to achieve what I was after: namely a project that does not aattempt to produce any build output.
Andy posted a link with a solution that's mostly worked for me; basically delete the following line from the project file:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
and add the following lines:
<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">
</Target>
I also found that disabling the project Debug property "Enable the Visual Studio hosting process" (for each configuration) prevented the MyProject.vshost.exe file from being generated.
As David I. McIntosh pointed out in a comment on this answer, if your project is part of a solution with multiple projects and any other projects use the same output path as the content-only project, the above Clean target will delete all of the files in the output path, i.e. the build output of other projects, and would thus only be correct if the content-only project is the first project built (among those sharing the same build output path). The following is a safer and friendlier Clean target for this scenario:
<Target Name="Clean">
<Delete Files="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(E‌​xtension)')"/>
</Target>
Then, try creating a Blank solution. Create Empty project. Have your files in respective folders with in the solution folder. From property window, use the Show all files, include those folders into the project. There is no better solution other then this. I hope.
This answer is just a convenient consolidation of the answers above given by Chris Fewtrell and Kenny Evitt, along with the slight modification in my comments above, and a bit more detail on what the declaration of the content items should/could look like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{541463A7-7CFA-4F62-B839-6367178B16BD}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '64-bit|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\builds\$(Configuration)\</OutputPath>
<IntermediateOutputPath>..\builds\$(Configuration)\Intermediate\YourProjectName\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Content Include="fileInProjectFolder.csv" />
<Content Include="SubDir\fileInSubdir.txt" />
<Content Include="..\actualSourceDirectoryOfFile\app.log.basic.config">
<Link>targetSubdirInOutputDir\app.log.basic.config</Link>
</Content>
<Content Include="..\actualSourceDirectoryOfFile\yetAnotherFile.config">
<Link>yetAnotherFile.config</Link>
</Content>
... more files ...
</ItemGroup>
<Target Name="Build">
<Copy
SourceFiles="#(Content)"
DestinationFiles="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)')" />
</Target>
<Target Name="Clean">
<Delete Files="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(E‌​xtension)')"/>
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build">
</Target>
</Project>
Note that this always copies all the "content" files to the output directory - the options "Copy If Newer", "Copy Always" and "Do Not Copy", as presented in the visual studio GUI ( appears as, for example, <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> in the .csproj file) are ignored.
In my situation, I needed to have a set of configuration files that would be common to many projects. To simply achieve this, I performed the following steps:
Create a Class Library project named "Configuration"
Delete all *.cs files from Configuration project
Put configuration files in a "Configuration" folder in the Configuration project
Copy configuration files to required projects in the post-build event. In Configuration project's Properties > Build Events > Post-build event:
xcopy "$(TargetDir)Configuration\*" "$(SolutionDir)TARGET_PROJECT\$(OutDir)" /i /v /q /s /y
In the above, replace TARGET_PROJECT with your actual project
This will copy all the files in the Configurations folder to the output directory of the project that needs the configuration files (eg. MyProject/bin/Debug, etc).

How to Set the Working Directory in NAnt?

I am just getting started using NAnt. I was working from a tutorial, and just trying to set a target to clean my solution on build. My Visual Studio Solution structure is as follows:
Solution Folder
Project Folder
Project Folder
Tools Folder
NAnt Folder
The NAnt .exe file resides in the Tools/NAnt folder. My .build file is also in there. Here is my .build file:
<?xml version="1.0" encoding="utf-8" ?>
<project name="NAntTest" default="build" xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
<property name="solution.file.name" value="NAntTest.sln" />
<property name="project.config" value="debug" />
<target name="build" depends="clean.source" />
<target name="clean.source">
<exec program="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe"
commandline="${solution.file.name} /t:Clean /p:Configuration=${project.config} /v:q"
workingdir="."/>
</target>
</project>
This is how the example I am following was formatted. If I try to run this build, I get an error stating that the project file does not exist. In the clean.source target, if I replace the workingdir attribute with a hard coded path to my base solution folder, the script compiles and runs correctly. Obviously, this is not ideal for portability if I need to move the project anywhere.
How do I get NAnt to see the base working directory?
My recommendation is to always place the build file at solution level. Then all relative paths in the build file will be equal to that of the solution.
There is not builtin function to change the current directory, but you can create one in a script block :
<target name="foo">
<echo message="Current directory set to ${directory::set-current-directory('C:')}"/>
<echo message="Current directory is now ${directory::get-current-directory()}"/>
</target>
<script language="C#" prefix="directory">
<code><![CDATA[
[Function("set-current-directory")]
public static string SetCurrentDirectory(string path)
{
System.IO.Directory.SetCurrentDirectory(path);
return path;
}
]]></code>
</script>
Of course, you should avoid relying on the current directory in your scripts or in your code.
You could try setting the basedir attribute of the project node. This may resolve your problem.
<project name="NAntTest" default="build" basedir="C:\Code\MyProject" xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
If you set the verbose attribute of the nant exec task then it will spit out the exact command line that it generated. Not sure what your specific problem is regarding executing msbuild - I've been using the nantcontrib msbuild task instead.
There is now a workingdir attribute you can define on your exec element.
According to the documentation, workingdir refers to "The directory in which the command will be executed.".
As a task instead of a function:
<?xml version="1.0"?>
<project name="test" default="build">
<script language="C#" prefix="path" >
<code>
<![CDATA[
[TaskName("set-current-directory")]
public class SetCurrentDirectory : Task {
private string _path;
[TaskAttribute("path", Required=true)]
public string Path {
get { return _path; }
set { _path = value; }
}
protected override void ExecuteTask() {
System.IO.Directory.SetCurrentDirectory(_path);;
}
}
]]>
</code>
</script>
<target name="build">
<set-current-directory path="c:\Program Files" />
<echo message="${directory::get-current-directory()}" />
</target>
</project>
Output:
$ nant
build:
[echo] c:\Program Files

Resources