[cmake-developers] Fragile behaviour of msbuild when using Visual Studio solution/project generators

James Johnston JamesJ at motionview3d.com
Thu Oct 22 13:52:48 EDT 2015


> -----Original Message-----
> From: cmake-developers [mailto:cmake-developers-bounces at cmake.org]
> On Behalf Of Brad King
> Sent: Thursday, October 22, 2015 15:43
> To: cmake-developers at cmake.org
> Subject: Re: [cmake-developers] Fragile behaviour of msbuild when using
> Visual Studio solution/project generators
> 
> > We seem to use <CustomBuild>'s <Command> rather than <Exec>
> 
> We use that because it is what the IDE uses when creating a project file
so
> using it makes the command visible in the IDE properties.  It also handles
all
> the dependencies and such instead of always executing, and is the only way
> to get proper semantics we've found.  If no setting is available then I
think
> the solution is "don't do that" :(
> 
> > internals of msbuild and I couldn't find any obvious documentation for
> > <CustomBuild>.
> 
> I'm not very familiar with it either.  I see something that looks like a
definition
> of the CustomBuild target type in files like these:
> 
>  c:/Program Files
> (x86)/MSBuild/Microsoft.Cpp/v4.0/Microsoft.CppCommon.targets
>  c:/Program Files
> (x86)/MSBuild/Microsoft.Cpp/v4.0/V110/Microsoft.CppCommon.targets
>  c:/Program Files
> (x86)/MSBuild/Microsoft.Cpp/v4.0/V120/Microsoft.CppCommon.targets
>  c:/Program Files
> (x86)/MSBuild/Microsoft.Cpp/v4.0/V140/Microsoft.CppCommon.targets

Summary: I think there is a solution but it isn't the prettiest; see below:

Ultimately, the VC tasks derive from Microsoft.Build.Utilities.ToolTask.  I
examined MSBuild 4.0 (newer versions may offer more flexibility):

1.  Private ToolTask.LogMessagesFromStandardError is called whenever the
process emits something on standard error.  It's a one liner function that
calls private LogMessagesFromStandardErrorOrOutput.
2.  ToolTask.LogMessagesFromStandardErrorOrOutput calls protected virtual
LogEventsFromTextOutput with the error/output line.
3.  ToolTask's virtual LogEventsFromTextOutput implementation calls base
class Task.Log.LogMessageFromText.  Task.Log is a TaskLoggingHelper type
property.  It itself is a read-only property so that a user can't swap out
that variable.
4.  Public TaskLoggingHelper.LogMessageFromText is where it starts getting
interesting.  It calls CanonicalError.Parse, which checks for magic "error"
and "warning" strings.  If either string is found, a regex is used and if
it's a match, then the function returns information about the parsed
error/warning.  There isn't a way to skip it that I can see.  If Parse
indicates that it parsed an error, you're guaranteed to have LogError called
instead of the normal LogMessage.

So as far as I can tell, anyone making an MSBuild task derived from ToolTask
(very common) is guaranteed to get this error parsing capability by default
whether they like it or not.

Unless they override virtual LogEventsFromTextOutput.  Which is what the
Exec task does.  Exec overrides LogEventsFromTextOutput so that it can honor
the IgnoreStandardErrorWarningFormat property, which is specific to the Exec
task.  If the user doesn't set that property, it will call
base.Log.LogMessageFromText (see above).  If they do set it, they always
just log it as a Message and do not try to parse it.

Unfortunately, the CustomBuild task and all of its base classes between
CustomBuild and ToolTask do not override LogEventsFromTextOutput.  So
CustomBuild will get the default ToolTask error parsing capability that
can't be flipped off any other way.  CustomBuild is totally unrelated to and
independent of Exec task, other than that both of them derive from ToolTask.

It looks like somebody else discusses it here:
http://aschultz.us/blog/archives/316

I'm not sure why MSBuild team put IgnoreStandardErrorWarningFormat in Exec
task instead of the base ToolTask class.  (Or at least provide protected
members to easily allow derived classes to control the behavior without
having to completely reimplement the functionality in
LogEventsFromTextOutput).  It doesn't seem like a smart design.  Maybe
somebody can encourage the MSBuild team to rectify this problem - e.g. by
adding something similar to IgnoreStandardErrorWarningFormat to ToolTask
would seem like a simple enough thing to do that won't break
compatibility...

In the meantime, probably the only way to use CustomBuild and dodge this
error parsing behavior is to write a new MSBuild task that derives from
CustomBuild and then overrides LogEventsFromTextOutput.  Normally MSBuild
tasks require a precompiled .NET assembly to be available like in the
.targets files Brad pointed out.  However in MSBuild 4 I guess there is a
way to put the task implementation inline in the project file: " MSBuild
Inline Tasks " https://msdn.microsoft.com/en-us/library/dd722601.aspx ----
however this solution might have some downsides and somebody would have to
experiment to see if they are tolerable: (1) how will the IDE project
properties, etc. react to the proposed "CMakeCustomBuild" task derived from
CustomBuild instead of "CustomBuild" directly? E.g. will it let you view/set
the task properties like normal still from the IDE? (2) the IDE might give
some annoying security warnings when opening the project since the project
file is basically executing arbitrary C# code, (3) adds complexity to the
CMake generator.

Best regards,

James Johnston



More information about the cmake-developers mailing list