Microsoft Visual Studio Not Linking My CUDA .obj Files Into My .lib - visual-studio-2010

So, we recently upgraded our project to be using Microsoft Visual Studio 2010 and we are having some issues with our CUDA projects.
I do have 2008 installed, and I am using the vc90 toolkit, and the files appear to be compiling (their associate .obj files are created). This is in the a project which creates a .lib as its output. The .lib is then linked against in another project which produces linker errors because one of files in the library (Matrix.obj) can't find one of the symbols that should be in CUDAMatrix.obj.
I ran dumpbin /SYMBOLS on CUDAMatrix.obj, and the symbol is in there, is not UNDEF, and is External. I ran dumpbin on our .lib, and the symbol does not appear to be inside it. I turned up the verbosity of the build of the library and the list of .obj files after:
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\Lib.exe
Does not appear to contain CUDAMatrix.obj.
To sum up, how does the Visual Studio project know what .obj files to stick into the .lib that is being made? I couldn't find the actual list anywhere, and the librarian submenu didn't seem to help.
Or least conveniently, is there an external tool (similar to 'ar' on linux) that I can use to merge the .obj it produced with the library? I thought maybe the /MERGE switch on lib.exe would do it, but I kept getting a .dll not found error when I tried to run that from the command line.
P.S. The dumpbins also confirmed that both the .lib and the .objs were x86 targeted.
P.P.S. Adding them as "additional dependencies" in the librarian seems to work, but isn't there any good way to make this happen automatically? They are, after all, part of the project.

This seems harder, maybe some more competent/experienced people will answer too...
My first thought, however, is that maybe your CUDA build step does not "inform" the following building steps correctly what files it outputs and what they should do about it? Because VS2010 and VS2008 uses different build system, troubles may origin from that.
I must say, I do not really fully understand the new build system. I created my files through trial-and-error + some help with the buildin VS2008->VS2010 build-rule converter (which is still buggy and does not support everything).
In my CUDA build rule file I have (CUDA.targets)
<Target
Name="_SDF_CUDA"
...
Outputs="#(SDF_CUDA->Metadata('Outputs')->Distinct())"
...
> ...
<ItemGroup>
<SDF_CUDA
Condition="'%(SDF_CUDA.NvccCompilation)' == '0'">
<NvccCompilationLine>--compile -o "$(IntDir)%(OutputFile).cu.obj"</NvccCompilationLine>
</SDF_CUDA>
<SDF_CUDA
Condition="'%(SDF_CUDA.NvccCompilation)' == '1'">
<NvccCompilationLine>-cuda -o "$(IntDir)%(OutputFile).cu.c"</NvccCompilationLine>
</SDF_CUDA>
<SDF_CUDA
Condition="'%(SDF_CUDA.NvccCompilation)' == '2'">
<NvccCompilationLine>-gpu -o "$(IntDir)%(OutputFile).gpu"</NvccCompilationLine>
</SDF_CUDA>
<SDF_CUDA
Condition="'%(SDF_CUDA.NvccCompilation)' == '3'">
<NvccCompilationLine>-cubin -o "$(IntDir)%(OutputFile).cubin"</NvccCompilationLine>
</SDF_CUDA>
<SDF_CUDA
Condition="'%(SDF_CUDA.NvccCompilation)' == '4'">
<NvccCompilationLine>-ptx -o "$(IntDir)%(OutputFile).ptx"</NvccCompilationLine>
</SDF_CUDA>
</ItemGroup>
...
<SDF_CUDA
Condition="'#(SDF_CUDA)' != '' and '%(SDF_CUDA.ExcludedFromBuild)' != 'true'"
...
OutputFile="%(SDF_CUDA.OutputFile)"
... />
</Target>
...
<PropertyGroup>
<ComputeLinkInputsTargets>
$(ComputeLinkInputsTargets);
ComputeSDF_CUDAOutput;
</ComputeLinkInputsTargets>
<ComputeLibInputsTargets>
$(ComputeLibInputsTargets);
ComputeSDF_CUDAOutput;
</ComputeLibInputsTargets>
</PropertyGroup>
<Target
Name="ComputeSDF_CUDAOutput"
Condition="'#(SDF_CUDA)' != ''">
<ItemGroup>
<SDF_CUDA Condition="'%(SDF_CUDA.NvccCompilation)' == '0'">
<Outputs>$(IntDir)%(OutputFile).cu.obj</Outputs>
</SDF_CUDA>
<SDF_CUDA Condition="'%(SDF_CUDA.NvccCompilation)' == '1'">
<Outputs>$(IntDir)%(OutputFile).cu.c</Outputs>
</SDF_CUDA>
<SDF_CUDA Condition="'%(SDF_CUDA.NvccCompilation)' == '2'">
<Outputs>$(IntDir)%(OutputFile).gpu</Outputs>
</SDF_CUDA>
<SDF_CUDA Condition="'%(SDF_CUDA.NvccCompilation)' == '3'">
<Outputs>$(IntDir)%(OutputFile).cubin</Outputs>
</SDF_CUDA>
<SDF_CUDA Condition="'%(SDF_CUDA.NvccCompilation)' == '4'">
<Outputs>$(IntDir)%(OutputFile).ptx</Outputs>
</SDF_CUDA>
</ItemGroup>
<Message Text="Outputs file: %(SDF_CUDA.Outputs)" />
<ItemGroup>
<SDF_CUDADirsToMake
Condition="'#(SDF_CUDA)' != '' and '%(SDF_CUDA.ExcludedFromBuild)' != 'true'"
Include="%(SDF_CUDA.Outputs)" />
<Link
Include="%(SDF_CUDADirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<Lib
Include="%(SDF_CUDADirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
<ImpLib
Include="%(SDF_CUDADirsToMake.Identity)"
Condition="'%(Extension)'=='.obj' or '%(Extension)'=='.res' or '%(Extension)'=='.rsc' or '%(Extension)'=='.lib'" />
</ItemGroup>
<MakeDir
Directories="#(SDF_CUDADirsToMake->'%(RootDir)%(Directory)')" />
</Target>
I believe the one of the last nodes actually guides the subsequent linking stage to include the generated .obj file. Which one exactly? I don't know :)
Hope it will help... somehow... good luck!

Related

Why does overriding the Build target in msbuild work for C++ projects but fail for C# projects?

I am overriding the Build target like this in my file OverrideBuild.targets:
<Target Name="OriginalBuild" DependsOnTargets="$(BuildDependsOn)">
<Message Text="Finished running target OriginalBuild" Importance="High" />
</Target>
<Target Name="Build" >
<CheckArtifacts ProjectGuid = "$(ProjectGuid)" SolutionPath = "$(SolutionPath)" >
<Output PropertyName = "ArtifactsHaveChanged" TaskParameter = "Result" />
</CheckArtifacts>
<Message Text="ArtifactsHaveChanged = $(ArtifactsHaveChanged)" Importance="high" />
<!-- if the artifacts.props file has not just been updated then we can run the original build target -->
<Message Condition="'$(ArtifactsHaveChanged)' == 'false'" Text="Running target OriginalBuild" Importance="High" />
<CallTarget Condition="'$(ArtifactsHaveChanged)' == 'false'" Targets="OriginalBuild" />
<!-- Otherwise we need to run a new msbuild to avoid using an out-of-date cached version of the artifacts.props file.
To force the msbuild process not to use the cached values from this process we must pass at least one property.
-->
<Message Condition="'$(ArtifactsHaveChanged)' == 'true'" Text="Running target OriginalBuild in nested msbuild" Importance="High" />
<MSBuild Condition="'$(ArtifactsHaveChanged)' == 'true'" Targets="OriginalBuild"
Projects="$(MSBuildProjectFullPath)" Properties="InNestedMsbuild=true" />
<!-- Visual Studio doesn't pick up on the modified artifacts.props file unless we force it to reload the solution -->
<Touch Condition="'$(ArtifactsHaveChanged)' == 'true' and '$(BuildingInsideVisualStudio)' == 'true'" Files = "$(SolutionPath)" />
<Message Text="Finished running build target override" Importance="High" />
</Target>
and each of my .vcxproj or .csproj files includes this file at the end:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\OverrideBuild.targets" />
</Project>
This works as I want it to for the C++ projects but fails with the C# projects. When building a C# project via msbuild it fails because the command line to the C# compiler is missing reference arguments for local assemblies. For example, a C# file that has a line like this at the top of the file:
using My.Utils.Common;
fails with the following error message:
error CS0234: The type or namespace name 'Common' does not exist in the namespace 'My.Utils' (are you missing an assembly reference?)
And looking at the compiler command used it is missing this line:
/reference:C:\Code\scratch\Build\My.Utils.Common\Bin\Release\My.Utils.Common.dll
That missing line is present when I comment out my override of the Build target. And weirdly enough it will build fine from within Visual Studio even with my Build override in place. It only fails when building using msbuild from the command line and only for C# projects.
I thought that the way I had overriden the Build target would be completely transparent but apparently it isn't. Can anybody shed some light on what is going wrong ?
It seems that when project A depends on project B with a project reference, the outputs of the Build target of B are used to deduce what should be passed as a reference to the compiler when building A. This is presumably somewhere in the ResolveAssemblyReferences logic.
Therefore to get your replacement Build target working, you need to make its outputs match those of the standard Build.
Here is how you can achieve this:
<Target
Name="Build"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="GetTargetPathWithTargetPlatformMoniker"
Returns="#(TargetPathWithTargetPlatformMoniker)" >
</Target>
Here Returns="#(TargetPathWithTargetPlatformMoniker)" is what the Returns of the standard Build in the SDK is. But the item array #(TargetPathWithTargetPlatformMoniker) is initially empty, so you need to run the Target GetTargetPathWithTargetPlatformMoniker to populate it before hand.
These are implementation details of the build system, so they may vary by SDK version, but you can always inspect the logic in C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.target or equivalent.
Note that this cannot be used directly with C++ projects, their default Build target is a bit different. You may need to vary by the project type to support both. The Condition on a Target does not stop it from overwriting the existing one, it only stops it from executing, so if you need a target overwrite to differ, you need to put the alternatives in files and import them conditionally. I don't know of a more convenient way, but that at least works.
Why does overriding the Build target in msbuild work for C++ projects but fail for C# projects?
After test your sample, I found the error not comes from the overriden the Build target, it should be related to the project type which you referenced.
Because I have tried comment the import line in the HelloWorld project file:
<Import Project="..\..\OverrideBuild.targets" />
Then MSBuild command line still throw that error.
Besides, I found your referenced project HelloWorldHelper is a Console application project, which output type is Class library.
To resolve this issue, I have created a new Class library instead of Console application, then build it from MSBuild command line, it works fine.
So, please try to convert your referenced project to Class library.
Hope this helps.

Copy output binaries to custom path for xproj

I have solution with about 50 projects.
The projects have 2 target frameworks: netcore1.1 and .net 4.6.
All the projects are based on xproj.
So as build result I have binaries in bin/$configuration/$targetfw/.
F.ex. in my case I have binaries in bin/debug/netcoreapp and bin/debug/net462 outputs.
But I need to have copy of bin/debug/net462 content in /bin directory too.
How to make it correctly by script in project.json or smth else for all 50 projcts of solution?
And it would be great if fix wont be visible for git source control.
P.s. Why i need it? Because VS code map tool looks for binaries in bin directly
UPD.
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>9e2d06cb-21aa-4457-ab44-6e67298037e3</ProjectGuid>
<RootNamespace>SmartDoc.Domain.Document</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<SccProjectName>SAK</SccProjectName>
<SccProvider>SAK</SccProvider>
<SccAuxPath>SAK</SccAuxPath>
<SccLocalPath>SAK</SccLocalPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
How to make it correctly by script in project.json or smth else for all 50 projcts of solution? And it would be great if fix wont be visible for git source control.
You can add a custom MSBuild task to copy the binaries from bin/$configuration/$targetfw/ to /bin directory.
To accomplish this, unload your project, edit it, add following code before ending tag </Project>:
<ItemGroup>
<MySourceFiles Include="$(ProjectDir)bin\$(Configuration)\**\$(ProjectName).dll"/>
</ItemGroup>
<Target Name="TestCopy" AfterTargets="Build">
<Message Text ="Copy file to bin folder." Importance="high"></Message>
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="$(ProjectDir)bin"
/>
</Target>
With this target, VS/MSBuild will copy the .dll file to the /bin directory.
Note: Since we have to modify the project file, this fix have to be visible for git source control.
Update:
Could I modify f.ex. Microsoft.DotNet.Props file and add your fix with
Target there?
Yes, you can. After test, it works fine. But I need to explain that we are not recommend this solution, because it may effect all the project which have Microsoft.DotNet.targets imported. So when you use this method, you should pay more attention and should back up that Microsoft.DotNet.targets.
Hope this helps.

MSBuild missing output files in AfterBuild when solution is cleaned

I'm sure there is something small that I'm missing. Here's the problem:
I have a solution that has multiple projects which after each build will be zipped. Here is an example of the zip creation in one project (they are pretty much identical in others):
<ItemGroup>
<CopySourceFiles Include="$(OutDir)\**\*.*" Exclude="$(OutDir)\**\*.pdb;$(OutDir)\*.mdf;$(OutDir)\*.ldf;$(OutDir)\*.vshost.*" />
</ItemGroup>
...
<Target Name="AfterBuild" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<MakeDir Directories="$(OutDir)\..\zip_working" />
<!-- first copy the source files specified in the CorySourceFiles ItemGroup above. -->
<Copy SourceFiles="#(CopySourceFiles)" DestinationFiles="#(CopySourceFiles->'$(OutDir)\..\zip_working\%(RecursiveDir)%(Filename)%(Extension)')" />
<!-- Perform the zip by calling the UsingTask. Make sure the DestinationFiles above and the SourceDirectory below are pointing to the same place -->
<Zip SourceDirectory="$(OutDir)\..\zip_working" OutputFilename="$(OutDir)\..\zip\$(ProjectName).zip" />
<!-- Clean up. -->
<RemoveDir Directories="$(OutDir)\..\zip_working" />
</Target>
There is a final project which has links to the zipped files that it combines into a package. All appears normal, but apparently only when the bin and zip_working folders already exist. I.e. if I clean the solution, delete the bin folders and then rebuild, the final zip that is created in the "zip" folder for each project is empty...
And then the zip files have content only after I build again.
So I'm guessing that during the build process, the AfterBuild target is running before the build output files exist. Does that sound right? I trigger the builds purely from within Visual Studio.
Regardless, how can I ensure that I can run a task on build output files only after they've been created?
Applies to Visual Studio 2013 Update 5 / MSBuild 12.0
If you delete everything in OutDir and then build the project, a top-level (as in, not inside a target) ItemGroup is evaluated before the build even starts. Some info can be found here for example. In other words, before a build and with an empty OutDir $(OutDir)\**\*.* evaluates to nothing and your CopySourceFiles item is empty.
The solution is simply to move the ItemGroup inside of the AfterBuild target. It will then be evaluated after the build and hence gets a proper view on the current files in outDir.

TypeScript files compiling on save but not on build

Problem
I've found that my TypeScript files only compile when I save them. However, when I build or rebuild the project, they do not compile. I'm using TypeScript 0.9.1.1.
When I check the build output of a new TypeScript project, it includes the following entry:
CompileTypeScript:
C:\Program Files\Microsoft SDKs\TypeScript\tsc.exe --module AMD --sourcemap --target ES3 "app.ts"
But my project doesn't produce this in its build output.
Setup
Here are the relevant parts of the project file:
<ItemGroup>
<TypeScriptCompile Include="Scripts\app\example.ts" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<TypeScriptTarget>ES5</TypeScriptTarget>
<TypeScriptIncludeComments>true</TypeScriptIncludeComments>
<TypeScriptSourceMap>true</TypeScriptSourceMap>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<TypeScriptTarget>ES5</TypeScriptTarget>
<TypeScriptIncludeComments>false</TypeScriptIncludeComments>
<TypeScriptSourceMap>false</TypeScriptSourceMap>
</PropertyGroup>
<Import Project="$(VSToolsPath)\TypeScript\Microsoft.TypeScript.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
What I've checked
The files compile on build in new TypeScript projects, so it should be possible.
The targets file exists in the correct location
The project file configuration shown above appears to be correct when compared with a new TypeScript project
Compiling a new TypeScript project, which works fine
Cannot compile TypeScript files in Visual Studio 2012, but the symptoms of the problem are different to mine, and it's not clear if the author was using the same setup
Typescript will not properly compile in VS2012, but the symptoms of the problem are different to mine
The build action for each TypeScript file is set to TypeScriptCompile as shown above
After comparing the project file with a new TypeScript project file, I narrowed the problem down to the following:
The TypeScript targets file must be imported after the C# targets file.
In the code in the question, fix the problem by moving the following line up:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

Determine whether it's a build or rebuild in .cmd script called in prelink step inside Visual Studio

How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild (Build.RebuildSolution/Build.RebuildOnlyProject) or "ordinary" build (Build.BuildSolution/Build.BuildOnlyProject)?
This is an external script (LuaJIT, if you must know) and I don't want to rebuild the library every single build of the project. Instead I'd like to limit the complete rebuild to situations where I choose exactly that option.
How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild ... or "ordinary" build ... ?
I do not know if the exact thing that you are asking can be done - perhaps someone else knows how to do it. I will, however, suggest an alternate approach.
My approach is to remove the build of the Lua library from the pre-link step to a separate Visual Studio NMake project. If you create an NMake project, you will be able to know which type of build (build or rebuild) is occurring.
Note that later versions of Visual Studio simply refer to the project type as "Make". For discussion purposes here, I will refer to the project type as "NMake". I believe this is just a naming difference, and that the underlying build project remains the same between the two versions.
As a simple test, I created two Visual Studio applications: 1) an NMake project that calls a batch file to create a static library, and 2) a console application that consumes the library from step 1.
The NMake Project
In Visual Studio, if you create a new NMake project, you will see a dialog that allows you to provide MS-DOS commands:
As you can see, there are commands for: Build, Clean, Rebuild, and others. I don't have a screen shot of the above dialog with my commands, but here is my NMake project's properties:
My Build command just checks for the existence of the output file (lua.lib). If it does not exist, then it calls the rebuild.bat batch file. My Rebuild command always calls the batch file. My Clean command just deletes the output. I am not really sure what the Output command is used for, but I just filled in the path to the build output (lua.lib).
Now if you do a build, the lua.lib file will only be created if it is not there. If it is already there, nothing is done. If you do a rebuild, then a new lua.lib file is created.
The Console Application
In my console application, I added a reference to the NMake project - this way the NMake project is built prior to the console application. Here is the console application's reference page:
I also added the lua.lib file as an input during the application's link stage:
When the console application is built (during a build), it will build the NMake project if needed, and use the output (lua.lib) during the linker stage. When the console application is rebuilt (during a rebuild), it will also rebuild the NMake project.
Other Thoughts
My screen shots above only show the debug version of the properties. Your projects will have to account for the release version. There probably is a VS macro to handle this, but I am not sure since it has been ages since I've done anything with C/ C++.
In my testing above I use a single build batch file for both the build and rebuild. Obviously, you could do the same or you could use different batch files.
It may be a bit of a hack, but in .csproj file there are sections
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
You can set an variable from BeforeBuild and retrieve it from cmd script. Later on reset this variable in AfterBuild and you should be good to go.
Ok, this is going to be a long one.
First of all - do not take my code 'as is' - it is terrible one with lots of hacks, I had no idea msbuild is so broken by default (it seems at work I have access to waaaay more commands that make life easier). And another thing - it seems vcxproj is broken at some poin - I was not able to integrate the way I wanted with only BeforeRebuild and AfterRebuild targets - I had to redefine hole Rebuild target (it is located in C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets)
So, the idea is the following: when a Rebuild is happening we create an anchor. Then, during PreLink stage we execute cmd which is able to use created anchor. If the anchor is in place - we deal with Rebuild, if there is no anchor - it is a simple Build. After Rebuild is done - we delete the anchor.
modifications in vcxproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
....
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
....
<PreLinkEventUseInBuild>true</PreLinkEventUseInBuild>
....
</PropertyGroup>
....
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
.....
<PreLinkEvent>
<Command>D:\PreLink\b.cmd</Command>
</PreLinkEvent>
.....
</ItemDefinitionGroup>
.....
<Target Name="BeforeRebuild">
<Exec Command="echo 2 > D:\PreLink\2.txt" />
</Target>
<Target Name="AfterRebuild">
<Exec Command="del D:\PreLink\2.txt" />
</Target>
<!-- This was copied from MS file -->
<PropertyGroup>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' != ''">$(MSBuildProjectDefaultTargets)</_ProjectDefaultTargets>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' == ''">Build</_ProjectDefaultTargets>
<RebuildDependsOn>
BeforeRebuild;
Clean;
$(_ProjectDefaultTargets);
AfterRebuild;
</RebuildDependsOn>
<RebuildDependsOn Condition=" '$(MSBuildProjectDefaultTargets)' == 'Rebuild' " >
BeforeRebuild;
Clean;
Build;
AfterRebuild;
</RebuildDependsOn>
</PropertyGroup>
<Target
Name="Rebuild"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(RebuildDependsOn)"
Returns="$(TargetPath)"/>
<!-- End of copy -->
</Project>
And the cmd looks like this:
if exist 2.txt (
echo Rebuild818181
) else (
echo Build12312312
)
The output from Output window:
1>Task "Exec" (TaskId:41)
1> Task Parameter:Command=D:\PreLink\b.cmd
1> :VCEnd (TaskId:41)
1> Build12312312 (TaskId:41)
Things to improve:
Use normal variables instead of external file (it seems MsBuild extension pack should do it)
Probably find a way to override only BeforeRebuild and AfterRebuild instead of the hole Rebuild part
It is much easier. Just add the following target to your build file or visual Studio Project
<Target Name="AfterRebuild">
<Message Text="AFTER REBUILD" Importance="High" />
<!--
Do whatever Needs to be done on Rebuild - as the message shows in VS Output
window it is only executed when an explicit rebuild is triggered
-->
</Target>
If you want a two step solution use this as a template:
<PropertyGroup>
<IsRebuild>false</IsRebuild>
</PropertyGroup>
<Target Name="BeforeRebuild">
<Message Text="BEFORE REBUILD" Importance="High" />
<PropertyGroup>
<IsRebuild>true</IsRebuild>
</PropertyGroup>
</Target>
<Target Name="BeforeBuild">
<Message Text="BEFORE BUILD: IsRebuild: $(IsRebuild)" Importance="High" />
</Target>

Resources