msbuild icon indicating copy to clipboard operation
msbuild copied to clipboard

Use HelpLink for errors/warnings that already have a URL

Open rainersigwald opened this issue 4 years ago • 12 comments

We have several errors/warnings that have a URL in the text of the message. After #5488 (thanks, @jmezac!) we'll have a structured way to represent this that will eventually (https://github.com/dotnet/project-system/issues/6335) be used in the Visual Studio UI.

We should use that!

Likely candidates:

$ rg "fwlink|aka\.ms" -g *resx
src\Tasks\Resources\Strings.resx
683:    <value>MSB3152: To enable 'Download prerequisites from the same location as my application' in the Prerequisites dialog box, you must download file '{0}' for item '{1}' to your local machine. For more information, see http://go.microsoft.com/fwlink/?LinkId=616018.</value>
1568:    <value>MSB3276: Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.</value>
1971:    <value>MSB3474: The task "{0}" is not supported on the .NET Core version of MSBuild. Use the Microsoft XML Serializer Generator package instead. See https://go.microsoft.com/fwlink/?linkid=858594 for more information.</value>
2156:    <value>MSB3644: The reference assemblies for {0} were not found. To resolve this, install the Developer Pack (SDK/Targeting Pack) for this framework version or retarget your application. You can download .NET Framework Developer Packs at https://aka.ms/msbuild/developerpacks</value>
2545:    <value>MSB3783: Project "{0}" depends upon SDK "{1} v{2}" which was released originally for apps targeting "{3} {4}". To verify whether "{1} v{2}" is compatible with "{5} {6}", contact the SDK author or see http://go.microsoft.com/fwlink/?LinkID=309181.</value>
2553:    <value>MSB3842: Project "{0}" depends upon SDK "{1} v{2}" which supports apps targeting "{3} {4}". To verify whether "{1} v{2}" is compatible with "{5} {6}", contact the SDK author or see http://go.microsoft.com/fwlink/?LinkID=309181.</value>
2730:    <value>MSB4803: The task "{0}" is not supported on the .NET Core version of MSBuild. Please use the .NET Framework version of MSBuild. See https://aka.ms/msbuild/MSB4803 for further details.</value>

src\MSBuild\Resources\Strings.resx
791:    <value>For more detailed information, see https://aka.ms/msbuild/docs</value>

src\Build\Resources\Strings.resx
1197:    <value>Project file contains ToolsVersion="{0}". This toolset may be unknown or missing, in which case you may be able to resolve this by installing the appropriate version of MSBuild, or the build may have been forced to a particular ToolsVersion for policy reasons. Treating the project as if it had ToolsVersion="{1}". For more information, please see http://go.microsoft.com/fwlink/?LinkId=293424.</value>

rainersigwald avatar Jul 08 '20 15:07 rainersigwald

@rainersigwald I am interested in doing this if its available. I have not contributed before so any pointers on what I need to do to get involved is welcome!

Looking at the code, this enhancement looks fairly straightforward for most cases. How were you imagining storing the urls? For example we currently have:

  <!-- Some tasks are only supported on .NET Framework -->
  <data name="TaskRequiresFrameworkFailure" xml:space="preserve">
    <value>MSB4803: The task "{0}" is not supported on the .NET Core version of MSBuild. Please use the .NET Framework version of MSBuild. See https://aka.ms/msbuild/MSB4803 for further details.</value>
    <comment>{StrBegin="MSB4803: "}</comment>
  </data>

Would I just add it as another resource following some naming convention like:

  <data name="TaskRequiresFrameworkFailure.HelpLink" xml:space="preserve">
    <value>https://aka.ms/msbuild/MSB4803</value>
  </data>

PeteBoyRocket avatar Sep 27 '20 08:09 PeteBoyRocket

Hi, Is this still available?

I think the URLs can be just string literals in the source code. Because HTTP server can redirect the browser to a localised article, MSBuild can use the same URLs for all languages and need not place them in string resources where they could be localised.

KalleOlaviNiemitalo avatar Aug 22 '22 07:08 KalleOlaviNiemitalo

@rainersigwald I am interested in doing this if its available. I have not contributed before so any pointers on what I need to do to get involved is welcome!

@PeteBoyRocket sorry! I was out on parental leave when you posted and it looks like the team missed this :( Since that was two years ago I'm going to assume you've moved on, as only makes sense . . .

@vijaya-lakshmi-venkatraman Yes, I'll assign this to you. As @KalleOlaviNiemitalo mentioned, the URLs can be hardcoded in source, as long as they're fwlink or aka.ms links; Microsoft employees can tweak the backend that supports those links to get them to point where they need to go.

rainersigwald avatar Aug 22 '22 14:08 rainersigwald

So, the change should be just changing the value tag as below & removing the comment tag? <value>https://aka.ms/msbuild/MSB4803</value>

https://github.com/dotnet/msbuild/blob/ef920930692067827d0c3b98be0882b85031ce9e/src/Tasks/LC.cs#L102-L106

LogErrorWithCodeFromResources could be overloaded with a method that takes a string helpLink parameter, and then the URL could be specified in this call. However, that would have a high risk of treating some argument as a URL when it's actually intended to go in params string[] messageArgs, thus a source breaking change, and it cannot be fixed by changing the order of parameters as in https://github.com/dotnet/msbuild/pull/5572 because the types of the parameters are the same. For that reason, I think it would be better to add the helpLink parameter only to the higher-arity method, and then add an internal extension method with a different name to simplify the calls. (Changing the type of the parameter to System.Uri could be another way but it would not be consistent with existing methods.)

https://github.com/dotnet/msbuild/blob/65c50fb73faefec0ef2ce8256b802f50cc8236f0/src/Shared/TaskLoggingHelper.cs#L840-L850

https://github.com/dotnet/msbuild/blob/2db11c256ade886f673ed56d12780fb70e6ef92e/src/Shared/TaskLoggingHelper.cs#L670-L683

If there is concern about duplicating the same URLs in the source code of many tasks, then they can be defined as const strings in some internal class.

Another possible strategy would be to change LogErrorWithCodeFromResources to extract the help link from the resource string, too. This would have a higher risk of breaking compatibility, if some third-party tasks call this method and have URLs in their resources but don't want to use them as help strings. Also, it would require keeping the help links in the resource data and formatting them in a consistent way (e.g. always surrounded with <…>) so that they can be reliably detected. The implementation could fit in ResourceUtilities. I think the help link should be extracted before the format arguments are plugged in, rather than after, so that it isn't mislead by format arguments that are URLs but not intended as help links (e.g. if the error is that the resource at the URL cannot be accessed).

By the way, I assumed that LogErrorFromException would read Exception.HelpLink, but it doesn't seem to do that.

KalleOlaviNiemitalo avatar Sep 07 '22 05:09 KalleOlaviNiemitalo

I agree with your analysis @KalleOlaviNiemitalo though I think I lean toward "make a new method named something like LogErrorWithCodeAndHelpLink" that explicitly calls the new LogError overload that passes along HelpLink.

Extracting a URL from the resource sounds nice but a) I'm not sure of the format we'd want to enforce--as you mention should it be <...> or just search for any http...-- and b) do we want the URL in the text part of the message, always, or should it just be in HelpLink?

rainersigwald avatar Sep 07 '22 10:09 rainersigwald

If the help link were in the resource string, I think it should be near the code, which is likewise extracted and removed automatically:

  <data name="TaskRequiresFrameworkFailure" xml:space="preserve">
    <value>MSB4803 &lt;https://aka.ms/msbuild/MSB4803&gt;: The task "{0}" is not supported on the .NET Core version of MSBuild. Please use the .NET Framework version of MSBuild.</value>
    <comment>{StrBegin="MSB4803 &lt;https://aka.ms/msbuild/MSB4803&gt;: "}</comment>
  </data>

The author of the message could then decide whether to repeat the URI in the human-readable part of the string and how to format it there.

KalleOlaviNiemitalo avatar Sep 07 '22 11:09 KalleOlaviNiemitalo

From scratch I like that approach but I think it'd break existing parsing approaches (like CanonicalError). And since ideally the link doesn't need to be localized, I think I prefer separate storage.

rainersigwald avatar Sep 07 '22 13:09 rainersigwald

Does CanonicalError parse the resource strings? I thought the resources were used only with LogErrorWithCodeFromResources and similar.

KalleOlaviNiemitalo avatar Sep 07 '22 13:09 KalleOlaviNiemitalo

That's true, unless you run MSBuild in an Exec task (shudder) or (more common) parse MSBuild's messages using your own similar regex, like actions/setup-dotnet does.

rainersigwald avatar Sep 07 '22 13:09 rainersigwald

How would the resource strings be written to stderr bypassing LogErrorWithCodeFromResources and ResourceUtilities, which would remove the prefix? https://github.com/dotnet/msbuild/blob/2db11c256ade886f673ed56d12780fb70e6ef92e/src/Shared/ResourceUtilities.cs#L122-L125

KalleOlaviNiemitalo avatar Sep 07 '22 14:09 KalleOlaviNiemitalo