3

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.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
  • Is this for an internal build where you control the tool sets, or is this something that you need, for example, to be able to have your clients do? – chue x Dec 01 '13 at 19:35
  • @chuex: I am in control, but it will be used by others. In fact this is for an open source program. – 0xC0000022L Dec 01 '13 at 21:00
  • 1
    Unfortunately that means my idea may not work, since not all VS versions support MS Build. I was going to suggest trying to do an [incremental build using MS Build](http://msdn.microsoft.com/en-us/library/ms171483.aspx). You can use VS 2010 (or greater) to target older versions of the VC compiler. See example here where VC 6 is targeted from VS 2012: [Is it possible to use the VC++ 6 compiler in Visual Studio 2012?](http://stackoverflow.com/questions/15029762/is-it-possible-to-use-the-vc-6-compiler-in-visual-studio-2012) – chue x Dec 01 '13 at 21:04
  • @chuex: yep, it's why I added the [tag:visualc++] tag. – 0xC0000022L Dec 01 '13 at 21:11
  • Pretty hard to see why this flexibility is necessary. Just remove the project from your solution. If you need to rebuild it for some reason then just open the project. – Hans Passant Dec 02 '13 at 22:03

4 Answers4

2

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:

Dialog for new make project

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:

NMake lib project 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:

application's reference

I also added the lua.lib file as an input during the application's link stage:

application's link properties

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.

chue x
  • 18,573
  • 7
  • 56
  • 70
1

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.

Community
  • 1
  • 1
Aleksei Poliakov
  • 1,322
  • 1
  • 14
  • 27
  • Older versions of VS didn't use `msbuild` as basis for the C/C++ projects and, yes, LuaJIT is C, not C#. But thanks for the pointers so far. – 0xC0000022L Dec 01 '13 at 15:06
  • Also, could you explain how setting a variable helps me *distinguish* a "full" build (rebuild) from an "incremental" build ("normal" build)? – 0xC0000022L Dec 01 '13 at 15:24
  • You have `` element in the project with smth like `run script.cmd`. You can add variable in there like `run script.cmd $(ThisVariableWasSetInpreBuildEvent)`. Or you can even do smth like this run script.cmd Doing stuff I really hope formatting is not too terrible. This is all relevant only for the sutiation with msbuild. Check out this article: http://msdn.microsoft.com/en-us/library/dd293582.aspx – Aleksei Poliakov Dec 01 '13 at 16:33
  • yes, I got that, but how does that help to distinguish a full rebuild from a partial build? – 0xC0000022L Dec 01 '13 at 17:12
  • With `ThisVariableWasSetInpreBuildEvent` parameter passed to cmd you can access it using %1 inside the cmd and do `IF [%1]==[]` – Aleksei Poliakov Dec 01 '13 at 17:19
  • 1
    So you're saying that `PreLinkEvent` (or the others you mentioned) get called during a full rebuild but not an incremental one? Or the other way around? I really don't follow. Honestly, I know how `.cmd` script work. The problem is that I need to distinguish `Build.BuildSolution`/`Build.BuildOnlyProject` from `Build.RebuildSolution`/`Build.RebuildOnlyProject` to speak in terms of the "commands" VS uses when you define keyboard mappings. I still can't see how your suggestions get me any further concerning this problem. Inevitably these events happen during build *and* rebuild. – 0xC0000022L Dec 01 '13 at 17:33
1

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 &gt; 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:

  1. Use normal variables instead of external file (it seems MsBuild extension pack should do it)

  2. Probably find a way to override only BeforeRebuild and AfterRebuild instead of the hole Rebuild part

Aleksei Poliakov
  • 1,322
  • 1
  • 14
  • 27
  • It is a great help to use `Message` while debugging msbuild, but you need to set detalization level: http://stackoverflow.com/questions/3352228/ms-build-beforebuild-not-firing – Aleksei Poliakov Dec 01 '13 at 19:35
1

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>
Daniel Fisher lennybacon
  • 3,865
  • 1
  • 30
  • 38