none
Why does MSBuild build with two command line MSBuild.exe calls, but don't build with an equivalent MSBuild task?

    Question

  • I have a MSBuild configuration with two targets 'Rebuild' and 'Build' in a MSBuild task for a C++ project. The projects will generate .LIB and .DLL files. The 'Rebuild' task sometimes build with errors, because of unresolved symbols. The errors are ignored, because there are circular references between DLLs and I have to build projects two or more times (first build against a dummy lib and later times a second build with newer generated project .LIB from the first Rebuild target). Refactoring is currently not an option. This works fine with Visual Studio and command line devenv.exe. It also works fine by calling MSBuild.exe two times in the command line for the whole solution, e. g.

    msbuild msbuild.proj /t:Rebuild /p:BuildInParallel=false /m /v:Diag

    msbuild msbuild.proj /t:Built p:BuildInParallel=false /m /v:Diag

    In all three variants the DLLs for all projects in the solution will be successfully build.

    But I always get an error with a MSBuild task and two targets in it. In my opinion it should be the same like the two MSBuild calls above. The target 'Rebuild' for the MSBuild file will call 'Rebuild' and 'Build' for the projects in the solution file. All .LIB files are generated by the first 'Rebuild', but the second 'Build' target doesn't work.

    I have looked into diagnose log output and the the second 'Build' will be skipped, because the previous build was not successfull. Yes, it was not successful, but it could be successful in the second step, because all files are prepared by the first 'Rebuild' task. Why the MSBuild task doesn't work properly, but the two calls in the command line does?

    Any ideas? 

    Targets in MSBuild file für building the DLLs with 'Rebuild' and 'Build':

    <Target Name="Rebuild">
        <Message Text="Rebuild solutions with configuration $(Configuration)|$(Platform) - BuildInParallel = $(BuildInParallel)" />  
            <MSBuild 
        Projects="@(SolutionToBuild)"
        Targets="Rebuild;Build"
        BuildInParallel="$(BuildInParallel)"
        RunEachTargetSeparately="true"
        StopOnFirstFailure="false"
        ContinueOnError="true"
        > 
        </MSBuild>
    </Target>
    <Target Name="Build">
     <Message Text="Build solutions with configuration $(Configuration)|$(Platform) - BuildInParallel = $(BuildInParallel)" /> 
         <MSBuild 
            Projects="@(SolutionToBuild)"
    Targets="Build"
    BuildInParallel="$(BuildInParallel)"
    RunEachTargetSeparately="true"
    StopOnFirstFailure="false"
    ContinueOnError="true"
        >  
        </MSBuild>
    </Target>

    The output from the diagnose log.

    17:34:03.456   5:2>Das Projekt "Solution.sln" (5:2) erstellt "C:\VC\Framework\Base.vcxproj" (7:2) auf Knoten "2" (Standardziele).
    17:34:03.456   7:2>Erstellung mit der Toolsversion 12.0.
    Das Ziel "_CheckForInvalidConfigurationAndPlatform" wurde übersprungen. Die vorherige Erstellung war erfolgreich.
    Das Ziel "Build" wurde übersprungen. Die vorherige Erstellung war nicht erfolgreich.

    Sunday, January 28, 2018 5:28 PM

Answers

  • yes, just as Rajeev Goel said "For every target in every project, MSBuild keeps track of the target's state -- NotStarted, InProgress, Skipped, CompletedSuccessfully, and CompletedUnsuccessfully.  This state is tracked from the time the entire build begins to the time the entire build ends.  Once a target enters one of the CompletedXXX states, it never gets executed again during that build." So if the task was exectued successfully, then thistask will be skipped next time.

    MSDN Community Support Please remember to click &quot;Mark as Answer&quot; the responses that resolved your issue, and to click &quot;Unmark as Answer&quot; if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, February 16, 2018 1:17 PM

All replies

  • Hi Andreas Gallien,

    Thanks for posting here.

    As far as I know, when you build with command line with special target /t:Rebuild or /t:Build, the custom build target will be specified, that target will be executed. When you build with MSBuild task, the default target is Build, the same target name with your custom name. If default build is failed, the custom target will be skip.

    To resolve the difference, I suggest that you can use other name of your custom build task, for example:

    <Target Name="CustomBuildName" BeforeTargets="Build">
    Hope this helps.


    MSDN Community Support Please remember to click &quot;Mark as Answer&quot; the responses that resolved your issue, and to click &quot;Unmark as Answer&quot; if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, January 29, 2018 9:21 AM
  • Hi Leo,

    thanks for your reply. I have defined a DefaultTargets="Rebuild" in the MSBuild file, so the default task is 'Rebuild' and calls the right tasks to build the solutions themself with 'Build' after a 'Rebuild'. I have also seen the 'Rebuild' task AND 'Build' task for the C++ projects in the diagnose log. The second 'Build' task is skipped, because the previous build was not successful. I don't understand, why the second 'Build' doesn't work here. Calling the second task separately after the first default MSBuild with command

    msbuild msbuild.proj /t:Built p:BuildInParallel=false /m /v:Diag

    build the projects without the skipping messages.

    It seems the tasks
    Targets="Rebuild;Build"
    inside the MSBuild tasks have an other behaviour like the command line runs.


    <Project DefaultTargets="Rebuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <PropertyGroup>
            <Configuration Condition="'$(Configuration)' == '' or '$(Configuration)' == '1'">Debug</Configuration>
            <Platform Condition="'$(Platform)' == ''">Win32</Platform>
            <BuildInParallel Condition="'$(BuildInParallel)' == ''">true</BuildInParallel>
        </PropertyGroup>
        <ItemGroup>
            <SolutionToBuild Include=".\BaseResource\Base_en.sln">
                <Properties>Configuration=Release;Platform=$(Platform)</Properties>
            </SolutionToBuild>
            <SolutionToBuild Include=".\BaseUnicode.sln">
                <Properties>Configuration=$(Configuration);Platform=$(Platform);BuildInParallel=$(BuildInParallel);DisableFastUpToDateCheck=$(DisableFastUpToDateCheck)</Properties>
            </SolutionToBuild>
        </ItemGroup>
        <Target Name="Rebuild">
            <Message Text="Rebuild solutions with configuration $(Configuration)|$(Platform) - BuildInParallel = $(BuildInParallel)" />
            <MSBuild
                Projects="@(SolutionToBuild)"
                Targets="Rebuild;Build"
                BuildInParallel="$(BuildInParallel)"
                RunEachTargetSeparately="true"
                StopOnFirstFailure="false"
                ContinueOnError="true"
            >
            </MSBuild>
        </Target>
        <Target Name="Build">
            <Message Text="Build solutions with configuration $(Configuration)|$(Platform) - BuildInParallel = $(BuildInParallel)" />
            <MSBuild
                Projects="@(SolutionToBuild)"
                Targets="Build"
                BuildInParallel="$(BuildInParallel)"
                RunEachTargetSeparately="true"
                StopOnFirstFailure="false"
                ContinueOnError="true"
            >
            </MSBuild>
        </Target>
    </Project>


    Monday, January 29, 2018 11:59 AM
  • @Andreas Gallien,

    >>> I don't understand, why the second 'Build' doesn't work here.

    That because the default targets is only Rebuild, when you build the MSBuild file without specify the second target /t:Built, MSBuild will not execute the second build, only build the default build Rebuild. Because there is no wrench to trigger the second build. For example, I create a new blank project, unload the project, edit the project file, at the very end of the project, just before the end-tag, place below scripts:

      <Target Name="TestBuild">
        <Message Text="This is the Test build"></Message>
      </Target>

    Then we build this project file with command line without specify that target:

    msbuild "C:\Users\Admin\Source\repos\TestProject\TestProject.sln"

    In the log, you will find that target not be triggered. Now if we trigger that target by the property in the command line:

    msbuild "C:\Users\Admin\Source\repos\TestProject\TestProject.sln" /t:TestBuild

    In the log, you will notice that the target will be triggered:

    So that the reason why the second build will be triggered when you specify the target name /t:Build.

    To make sure the second build is triggered, you could specify it with target name in the command line, or you can set the default build are Rebuild and Build, like:

    <Project DefaultTargets="Rebuild;Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    Or you can set the build order for the Build target, like:

    <Target Name="Build" AfterTargets="Rebuild">

    For some details, please check following thread:

    https://msdn.microsoft.com/en-us/library/ee216359.aspx

    Hope this helps.


     

    MSDN Community Support Please remember to click &quot;Mark as Answer&quot; the responses that resolved your issue, and to click &quot;Unmark as Answer&quot; if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, January 30, 2018 10:11 AM
  • I understand, but I mean this task with 'Rebuild;Build'

    <MSBuild
                Projects="@(SolutionToBuild)"
                Targets="Rebuild;Build"
                BuildInParallel="$(BuildInParallel)"
                RunEachTargetSeparately="true"
                StopOnFirstFailure="false"
                ContinueOnError="true"
            >

    Both targets will be processed and the second 'Build' task is skipped (see log above), because the previous build was not successfull. These messages comes from the compiler and linker.

    Thursday, February 1, 2018 9:53 PM
  • @Andreas Gallien, Got it. Thanks for your reply. You mean when you build the msbuild.proj file without specify /t:Built in the command line, the second 'Build' task in the 'Targets="Rebuild;Build"' will be skipped due to the previous build was not successful? Am I right? If not, could you please share me some more detailed steps to reproduce this issue or a test sample to reproduce it?

    MSDN Community Support Please remember to click &quot;Mark as Answer&quot; the responses that resolved your issue, and to click &quot;Unmark as Answer&quot; if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, February 2, 2018 9:47 AM
  • Hello Leo,

    yes you are right. Is there any flag to force the build of the second MSBuild task?

    I have modified the MSBuild project file and it's called by

    >msbuild msbuild.proj /t:Rebuild /m /v:Diag

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <PropertyGroup>
            <Configuration Condition="'$(Configuration)' == '' or '$(Configuration)' == '1'">Debug</Configuration>
            <Platform Condition="'$(Platform)' == ''">Win32</Platform>
            <BuildInParallel Condition="'$(BuildInParallel)' == ''">true</BuildInParallel>
    	<TrackFileAccess>True</TrackFileAccess>
        </PropertyGroup>
        <ItemGroup>
            <SolutionToBuild Include=".\BaseResource\Base_en.sln">
                <Properties>Configuration=Release;Platform=$(Platform);TrackFileAccess=$(TrackFileAccess)</Properties>
            </SolutionToBuild>
    		<SolutionToBuild Include=".\BaseUnicode.sln">
                <Properties>Configuration=$(Configuration);Platform=$(Platform);BuildInParallel=$(BuildInParallel);TrackFileAccess=$(TrackFileAccess)</Properties>
            </SolutionToBuild>
        </ItemGroup>
        <Target Name="Rebuild">
            <Message Text="Rebuild solutions with configuration $(Configuration)|$(Platform) - BuildInParallel = $(BuildInParallel)" />
            <MSBuild
                Projects="@(SolutionToBuild)"
                Targets="Rebuild"
                BuildInParallel="$(BuildInParallel)"
    	    Properties="$(Properties);ForceFileOutput=UndefinedSymbolOnly"
    	    ContinueOnError="true"
            >
            </MSBuild>
    	    <MSBuild
                Projects="@(SolutionToBuild)"
                Targets="Build"
    	    Properties="$(Properties);BuildSteps=BuildLink"
                BuildInParallel="$(BuildInParallel)"
            >
            </MSBuild>
        </Target>
    	<Target Name="Build">
            <Message Text="Build solutions with configuration $(Configuration)|$(Platform) - BuildInParallel = $(BuildInParallel)" />
            <MSBuild
                Projects="@(SolutionToBuild)"
                Targets="Build"
                BuildInParallel="$(BuildInParallel)"
            >
            </MSBuild>
        </Target>
    </Project>

    The target 'Rebuild' calls the first MSBuild task 'Rebuild' with Property 'ForceFileOutput=UndefinedSymbolOnly' and compile and link the projects. The second MSBuild task with Property 'BuildSteps=BuildLink' is skipped, because the first MSBuild task was successful. I'd like to link again. How that could be achieved?




    Friday, February 9, 2018 12:39 AM
  • I have also found some articles about building tasks twice:

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/0b0e58e3-dae3-4cc0-b7b6-1629994d68d5/targets-wont-execute-twice?forum=msbuild

    The BuildLink task was called by the first MSBuild task. Maybe it is not called by the second task, because it was successful?

    Monday, February 12, 2018 4:51 AM
  • yes, just as Rajeev Goel said "For every target in every project, MSBuild keeps track of the target's state -- NotStarted, InProgress, Skipped, CompletedSuccessfully, and CompletedUnsuccessfully.  This state is tracked from the time the entire build begins to the time the entire build ends.  Once a target enters one of the CompletedXXX states, it never gets executed again during that build." So if the task was exectued successfully, then thistask will be skipped next time.

    MSDN Community Support Please remember to click &quot;Mark as Answer&quot; the responses that resolved your issue, and to click &quot;Unmark as Answer&quot; if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, February 16, 2018 1:17 PM