I'm seeing an exception when trying to use the dragonfruit API with the new NET5.0 style single file publish.
Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'path1')
at System.IO.Path.Combine(String path1, String path2)
at System.CommandLine.DragonFruit.CommandLine.GetDefaultXmlDocsFileLocation(Assembly assembly)
at System.CommandLine.DragonFruit.CommandLine.ConfigureHelpFromXmlComments(CommandLineBuilder builder, MethodInfo method, String xmlDocsFilePath)
at System.CommandLine.DragonFruit.CommandLine.BuildParser(MethodInfo method, String xmlDocsFilePath, Object target)
at System.CommandLine.DragonFruit.CommandLine.InvokeMethodAsync(String[] args, MethodInfo method, String xmlDocsFilePath, Object target, IConsole console)
at System.CommandLine.DragonFruit.CommandLine.ExecuteAssemblyAsync(Assembly entryAssembly, String[] args, String entryPointFullTypeName, String xmlDocsFilePath, IConsole console)
at AutoGeneratedProgram.Main(String[] args)
at AutoGeneratedProgram.<Main>(String[] args)
A little bit of source reading shows that Assembly.Location is being read here.
This is probably running afoul of this part of the design
I know dragonfruit is an experiment, but is there any plan to support this scenario?
I think we'll want to fix this (and would certainly accept a PR).
Running into this as well. Looks like the code in GetDefaultXmlDocsFileLocation assumes assembly.Location is non-null and has the path to the xml docs.
private static string GetDefaultXmlDocsFileLocation(Assembly assembly)
{
return Path.Combine(
Path.GetDirectoryName(assembly.Location),
Path.GetFileNameWithoutExtension(assembly.Location) + ".xml");
}
Reading this blog post from Scott Hanselman it seems like AppContext.BaseDirectory might be better to use but I'm not sure.
I have the same issue when running from SingleFilePublish. No errors when running from VS.
I also have the same issue when publishing with .NET CLI. I only get this runtime error followed by a segfault after publishing and running.
same issue
I started playing around with making a fix for this, and AppContext.BaseDirectory looked promising until I came across the <DocumentationFile> msbuild property.
Suppose the location of the XML doc is redirected to another path like this:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>D:\temp\doc.xml</DocumentationFile>
</PropertyGroup>
A few things happen:
doc.xml is generated under D:\temp\ ($(DocumentationFile) is directly passed to Csc task)doc.xml is also generated under AppContext.BaseDirectory [1]$(DocumentationFile) is specified/defined, then $(AssemblyName).xml is no longer produced (anywhere). [2][1] Microsoft.Common.CurrentVersion.Targets always generates doc-file under `$(Outdir)
<ItemGroup>
<IntermediateAssembly Include="$(IntermediateOutputPath)$(TargetName)$(TargetExt)"/>
<FinalDocFile Include="@(DocFileItem->'$(OutDir)%(Filename)%(Extension)')"/>
<CopyUpToDateMarker Include="$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', '$(MSBuildProjectFile).CopyComplete'))" />
</ItemGroup>
[2] Microsoft.NET.Sdk.BeforeCommon.targets controls this:
<PropertyGroup Condition="'$(GenerateDocumentationFile)' == 'true' and '$(DocumentationFile)' == ''">
<DocumentationFile Condition="'$(MSBuildProjectExtension)' == '.vbproj'">$(AssemblyName).xml</DocumentationFile>
<DocumentationFile Condition="'$(MSBuildProjectExtension)' != '.vbproj'">$(IntermediateOutputPath)$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
Also, there doesn't seem to be a way to detect when an assembly is a bundled-assembly - at least none I could find from a cursory search of docs. It's clear that Assembly.Location will return string.Empty for bundled-assemblies, but I'm not sure that that converse is true (i.e., when Assembly.Location returns string.Empty, the assembly in question isn't always bundled, is it?).
So when Assembly.Location returns string.Empty, we could make an educated guess that what we are dealing with is a bundled-assembly, and probe AppContext.BaseDirectory for an xml file matching the name $(AssemblyName).xml (only when assembly.Name in GetDefaultXmlDocsFileLocation happens to also match $(AssemblyName)) . Here, $(AssemblyName) is the name of the main assembly. If such an xml is found, that could be loaded as a fallback.
For e.g., if the main assembly were named TestApplication, then we'd have to find TestApplication.xml at Appcontext.BaseDirectory, and assembly.Name in GetDefaultXmlDocsFileLocation would have to also match TestApplication for that probe to be made. I want to protect against the possibility of GetDefaultXmlDocsFileLocation being used to look for documentation for SomeOtherAssembly....
I'm hoping for feedback and suggestions
Something like this seems to work well with self-contained exes:
```C#
private static string GetDefaultXmlDocsFileLocation(Assembly assembly)
{
if (!string.IsNullOrEmpty(assembly.Location))
{
return Path.Combine(
Path.GetDirectoryName(assembly.Location),
Path.GetFileNameWithoutExtension(assembly.Location) + ".xml");
}
if (assembly == Assembly.GetEntryAssembly())
{
return Path.Combine(
AppContext.BaseDirectory,
assembly.GetName().Name + ".xml");
}
return string.Empty;
}
```
When the <DocumentationFile> property is specified (i.e., the generated xml is named something other than $(AssemblyName)), --help is based on reflection and terse; whereas when <DocumentationFile> is absent and the generated xml is named the same as $(AssemblyName) then --help works nicely and prints out content based on documentation.
Most helpful comment
Running into this as well. Looks like the code in GetDefaultXmlDocsFileLocation assumes assembly.Location is non-null and has the path to the xml docs.
Reading this blog post from Scott Hanselman it seems like
AppContext.BaseDirectorymight be better to use but I'm not sure.