Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing FileNameFactories #109

Closed
TaffarelJr opened this issue May 24, 2022 · 11 comments
Closed

Missing FileNameFactories #109

TaffarelJr opened this issue May 24, 2022 · 11 comments

Comments

@TaffarelJr
Copy link

I seem to have encountered a strange bug, where the FileNameFactories aren't loading properly.

I've imported the NuGet package into my project:

<ItemGroup>
  <PackageReference Include="DefaultDocumentation" Version="0.8.1" />
</ItemGroup>

I'm leaving all configuration set to default for now. But upon building the application, it throws (paths shortened):

..\DefaultDocumentation.targets(45,3): error MSB4018: The "DefaultDocumentationTask" task failed unexpectedly. [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018: System.Exception: FileNameFactory 'FullName' not found [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018:    at DefaultDocumentation.Internal.Context..ctor(JObject configuration, Type[] availableTypes) in D:\a\DefaultDocumentation\DefaultDocumentation\source\DefaultDocumentation.Common\Internal\Context.cs:line 20 [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018:    at DefaultDocumentation.Internal.GeneralContext..ctor(JObject config, Type[] availableTypes, Settings settings, IReadOnlyDictionary`2 items) in D:\a\DefaultDocumentation\DefaultDocumentation\source\DefaultDocumentation.Common\Internal\GeneralContext.cs:line 30 [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018:    at DefaultDocumentation.Generator..ctor(Target loggerTarget, IRawSettings settings) in D:\a\DefaultDocumentation\DefaultDocumentation\source\DefaultDocumentation.Common\Generator.cs:line 116 [..\Utilities.csproj]  
..\DefaultDocumentation.targets(45,3): error MSB4018:    at DefaultDocumentation.Generator.Execute(Target loggerTarget, IRawSettings settings) in D:\a\DefaultDocumentation\DefaultDocumentation\source\DefaultDocumentation.Common\Generator.cs:line 196 [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018:    at DefaultDocumentation.DefaultDocumentationTask.Execute() in D:\a\DefaultDocumentation\DefaultDocumentation\source\DefaultDocumentation\DefaultDocumentationTask.cs:line 57 [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018:    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute() [..\Utilities.csproj]
..\DefaultDocumentation.targets(45,3): error MSB4018:    at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) [..\Utilities.csproj]

Poking around the source code, I can see in Generator.cs line 118 that it's supposed to be loading all the availabletypes:

new[] { typeof(Markdown.Writers.MarkdownWriter).Assembly.Location }
    .Concat(GetSetting<string[]>(nameof(settings.Plugins)) ?? Enumerable.Empty<string>())
    .Select(Assembly.LoadFrom)
    .SelectMany(a => a.GetTypes())
    .ToArray()

... and then in Context.cs line 19 it's sorting through them to find the specified FileNameFactory:

string fileNameFactory = GetSetting<string>(nameof(IRawSettings.FileNameFactory));
FileNameFactory = string.IsNullOrEmpty(fileNameFactory) ? null : availableTypes
    .Where(t => typeof(IFileNameFactory).IsAssignableFrom(t) && !t.IsAbstract)
    .Select(t => (IFileNameFactory)Activator.CreateInstance(t))
    .LastOrDefault(f => f.Name == fileNameFactory || $"{f.GetType().FullName} {f.GetType().Assembly.GetName().Name}" == fileNameFactory)
    ?? throw new Exception($"FileNameFactory '{fileNameFactory}' not found");

But I can't tell where the code would be failing to find the IFileNameFactory types. It all looks good to me. There must be some edge case I've stumbled on, where it's not working. My repo is a monorepo structure, so it's a bit different than a standard repo layout; and perhaps something in the way it's configured is the culprit.

I need to track this down, but I don't know how to go about debugging it further. I'd be happy to work with you, if you have any ideas.

@petarpetrovt
Copy link

I have the same problem.

@Doraku
Copy link
Owner

Doraku commented May 25, 2022

hum that's weird, you have no peculiar settings for DefaultDocumentation? what OS are you using?

@petarpetrovt
Copy link

I got multiple target frameworks.
The error occurred on any OS in the github action.
Doesn't happen in VS.
build
csproj

@TaffarelJr
Copy link
Author

Mine is a monorepo - it contains several projects, each building a separate NuGet package.

I have a shared .props file where I can configure project settings that apply to all my NuGet packages. This is where I've installed DefaultDocumentation. I have no configuration for DD, other than this:

<Project>

  <PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <DefaultDocumentationFolder>$(SolutionDir)\wiki</DefaultDocumentationFolder>
    <DefaultDocumentationLogLevel>Trace</DefaultDocumentationLogLevel>
    <DisableDefaultDocumentation>false</DisableDefaultDocumentation>
    <IsPackable>true</IsPackable>
    ...
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="DefaultDocumentation" Version="0.8.1" />
    ...
  </ItemGroup>

</Project>
  • Each NuGet package targets multiple frameworks: netstandard2.0 netstandard2.1
  • From the command line (Cake script calling dotnet build) it fails with FileNameFactory 'FullName' not found
  • From Visual Studio, it succeeds. And I can see the ./wiki/ folder was created, but it's empty. Looking through the DD log output, I can see it found the relevant classes/properties/methods and processed them. My solution is brand new, and some of my NuGet packages are empty at the moment. I wonder if DD is deleting the ./wiki/ folder and re-creating it for each project.

@TaffarelJr
Copy link
Author

After adding at least one class to each NuGet package in my solution, I can confirm the folder was being overwritten by each project during the build process.

I've now moved the DefaultDocumentationFolder config setting out of the shared props, and each project is now pointing to its own independent output folder. This makes the docs generate successfully from VS.

The command line is still broken however. So it must be some difference in the way DD is loaded (or the app domain is configured) in VS vs from the dotnet CLI.

@aremes
Copy link

aremes commented May 27, 2022

+1 on this one. I seem to have a similar problem to @TaffarelJr:

  • MonoRepo with a single solution and a whole bunch of projects building the docs into the same directory
  • Works in VS
  • Fails with dotnet build
  • Consolidated all the DefaultDocumentation settings into Directory.build.props

I investigated a bit and it seems like there may have been a break at v0.8.0.

I've attached a solution. dotnet build runs fine (and outputs md files) for 0.7.7 (which we used up to now), 0.7.8 and 0.7.9 but fails with 0.8.0. The error is the same: FileNameFactory 'FullName' not found.

I've attached an example. Just changing the reference to 0.7.9 in the csproj files makes it work with dotnet build. Will investigate further
DefaultDocWorking.zip

@aremes
Copy link

aremes commented May 27, 2022

With the exact same configuration, this:

.Where(t => typeof(IFileNameFactory).IsAssignableFrom(t) && !t.IsAbstract)

seems to return nothing with dotnet build whereas it contains all the factories when run in visual studio.. strange

@aremes
Copy link

aremes commented May 27, 2022

Could this be related to dotnet/runtime#44222 ?

I've tried all kinds of variations of <Private>false</Private>, <ExcludeAssets>runtime</ExcludeAssets>, <EnableDynamicLoading>true</EnableDynamicLoading> and <CopyLocalLocFileAssemblies>true</CopyLocalLockFileAssemblies>
but i'm not getting anywhere.

@Doraku
Copy link
Owner

Doraku commented May 28, 2022

I have been using the dotnet cli locally and on github actions for a while without issue, I will have to check on my side if I can reproduce :/ (sorry I have not been available a lot lately I am in the process of moving home!)

I've now moved the DefaultDocumentationFolder config setting out of the shared props, and each project is now pointing to its own independent output folder.

You could have kept the shared configuration, just override the <DefaultDocumentationFolder> in each projects separately or use something like $(MSBuildProjectName) so that their output are different.

I've attached an example. Just changing the reference to 0.7.9 in the csproj files makes it work with dotnet build. Will investigate further

0.8.0 uses plugins to dynamically load available implementations for the file name factory while 0.7.9 is just an enum switch factory so they are not really comparable :/ If I can reproduce the problem I will add more log to see what dll is actually loaded and why types does not coincide correctly when run from the cli.

@aremes
Copy link

aremes commented May 30, 2022

0.8.0 uses plugins to dynamically load available implementations for the file name factory while 0.7.9 is just an enum switch factory so they are not really comparable :/

Yeah, as i looked through the history in hopes of finding what changed the behavior i realized that the whole plug-in architecture actually started in 0.8.0 so that turned out to be a dead end.

I have been using the dotnet cli locally and on github actions for a while without issue, I will have to check on my side if I can reproduce :/

Well the fact that it works for you with the dotnet cli sounds promising. So it might just be an issue with tool/sdk versions, system configuration then?

In case it helps, i'm using VS2022 Enterprise (v17.2.0) and here's the --info output from dotnet:

.NET SDK (reflecting any global.json):
 Version:   6.0.300
 Commit:    8473146e7d

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19043
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\6.0.300\

Host (useful for support):
  Version: 6.0.5
  Commit:  70ae3df4a6

.NET SDKs installed:
  6.0.300 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

@aremes
Copy link

aremes commented May 30, 2022

I believe I have found the issue.

new[] { typeof(Markdown.Writers.MarkdownWriter).Assembly.Location }

This basically loads DefaultDocumentation.Markdown a second time alongside the one already loaded from the project reference. So now .IsAssignableFrom(typeof(IFileNameFactory)) fails because it thinks that the implemented IFileNameFactory is a different one (since it comes from a different assembly)

_context = new GeneralContext(
	_configuration,
	new[] { typeof(Markdown.Writers.MarkdownWriter).Assembly }
		.Concat(
			(GetSetting<string[]>(nameof(settings.Plugins)) ?? Enumerable.Empty<string>())
			.Select(Assembly.LoadFrom)
		)
		.SelectMany(a => a.GetTypes())
		.ToArray(),
	resolvedSettings,
	DocItemReader.GetItems(resolvedSettings));

should fix it.

aremes pushed a commit to aremes/DefaultDocumentation that referenced this issue May 30, 2022
@aremes aremes mentioned this issue May 30, 2022
@Doraku Doraku closed this as completed in 571e99b Jun 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants