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

docfx.console NuGet package does not work well with a class library with multiple items in <TargetFrameworks> #2284

Closed
lextm opened this issue Dec 10, 2017 · 15 comments
Labels
bug A bug to fix dotnet Generate .NET API reference docs

Comments

@lextm
Copy link
Contributor

lextm commented Dec 10, 2017

DocFX Version Used: 2.28.2

Template used: default

Steps to Reproduce:

  1. Create a .NET Standard Class Library project and modify its <TargetFrameworks> tag to use multiple values like netstandard1.3;net452.
  2. Add docfx.console NuGet package to this project.
  3. Create a docfx.json file to include the following information,
{
    "metadata": [
      {
        "src": [
          {
            "files": [
              "*.csproj"
            ],
            "exclude": [ "**/bin/**", "**/obj/**" ],
            "cwd": "."
          }
        ],
        "properties": {
            "TargetFrameworks": "net452"
        },
        "dest": "obj/10.0.2/api",
        "shouldSkipMarkup": true
      }
    ],
    "build": {
      "content": [
        { "files": [ "**/*.yml", "api/*.md" ], "src": "obj/10.0.2", "dest": "10.0.2" }
      ],
      "globalMetadata": {
        "_appTitle": "Some Documentation",
        "_disableContribution": true,
        "_appFooter": " ",
        "_navRel": "../toc.html"
      },
      "template": [
        "default",
        "template"
      ],
      "overwrite": [
        { "files": [ "obj/10.0.2/overwrite/*.md" ] }
      ],
      "dest": "_site"
    }
  }

Expected Behavior:
The documentation should be generated without any error.

Actual Behavior:
docfx seemed to be called twice, and the second instance could not access the log file and crashed,

1>[17-12-10 04:36:38.791]Info:[MetadataCommand.ExtractMetadata]Using msbuild C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin as inner compiler.
1>[17-12-10 04:36:38.792]Error:System.IO.IOException: The process cannot access the file 'F:\sharpsnmplib\SharpSnmpLib\log.txt' because it is being used by another process.
1>   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
1>   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
1>   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
1>   at System.IO.StreamWriter.CreateFile(String path, Boolean append, Boolean checkHost)
1>   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize, Boolean checkHost)
1>   at System.IO.StreamWriter..ctor(String path, Boolean append)
1>   at Microsoft.DocAsCode.Common.ReportLogListener..ctor(String reportPath, String repoRoot, String root)
1>   at Microsoft.DocAsCode.SubCommands.CommandCreator`2.Create(String[] args, ISubCommandController controller, SubCommandParseOption option)
1>   at Microsoft.DocAsCode.SubCommands.CompositeCommand..ctor(String[] args, ISubCommandController controller, CompositeOptions options)
1>   at Microsoft.DocAsCode.SubCommands.CommandController.Create()
1>   at Microsoft.DocAsCode.Program.ExecSubCommand(String[] args)
1>docfx 2.28.2.0
1>Copyright (C) 2017 Copyright c Microsoft docfx 2015-2017
1>This is open-source software under MIT License.
1>
1>
1>   Usage1: docfx <docfx.json file path> [-o <output folder path>]
1>
1>   Usage2: docfx <subcommand> [<args>]
1>
1>
1>
1>
1>
1>See 'docfx help <command> to read about a specific subcommand guide
1>
1>
1>    build           : Generate client-only website combining API in YAML files and conceptual files
1>    dependency      : Export dependency file
1>    download        : Download remote xref map file and create an xref archive in local.
1>    help            : Get an overall guide for the command and sub-commands
1>    init            : Generate an initial docfx.json following the instructions
1>    merge           : Merge .net base API in YAML files and toc files.
1>    metadata        : Generate YAML files from source code
1>    pdf             : Generate pdf file
1>    serve           : Host a local static website
1>    template        : List or export existing template
1>
1>
1>Build failed.
1>[17-12-10 04:36:38.830]Error:System.IO.IOException: The process cannot access the file 'F:\sharpsnmplib\SharpSnmpLib\log.txt' because it is being used by another process.
1>   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
1>   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
1>   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
1>   at System.IO.StreamWriter.CreateFile(String path, Boolean append, Boolean checkHost)
1>   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize, Boolean checkHost)
1>   at System.IO.StreamWriter..ctor(String path, Boolean append)
1>   at Microsoft.DocAsCode.Common.ReportLogListener..ctor(String reportPath, String repoRoot, String root)
1>   at Microsoft.DocAsCode.SubCommands.CommandCreator`2.Create(String[] args, ISubCommandController controller, SubCommandParseOption option)
1>[17-12-10 04:36:38.950]Info:[MetadataCommand.ExtractMetadata]Loading projects...
1>   at Microsoft.DocAsCode.SubCommands.CompositeCommand..ctor(String[] args, ISubCommandController controller, CompositeOptions options)
1>   at Microsoft.DocAsCode.SubCommands.CommandController.Create()
1>   at Microsoft.DocAsCode.Program.ExecSubCommand(String[] args)
1>	0 Warning(s)
1>	1 Error(s)
1>[17-12-10 04:36:38.953]Verbose:[MetadataCommand.ExtractMetadata](F:/sharpsnmplib/SharpSnmpLib/SharpSnmpLib.csproj)Loading project F:/sharpsnmplib/SharpSnmpLib/SharpSnmpLib.csproj
1>C:\Users\lextm\.nuget\packages\docfx.console\2.28.2\build\docfx.console.targets(53,5): error MSB3073: The command ""C:\Users\lextm\.nuget\packages\docfx.console\2.28.2\build\..\tools\docfx.exe" "F:\sharpsnmplib\SharpSnmpLib\docfx.json" -o "" -l "log.txt" --logLevel "Verbose"" exited with code 1.
1>Done building project "SharpSnmpLib.csproj" -- FAILED.
@smaillet
Copy link

I found that if I change the VS build settings to disable parallel builds I can get past the log.txt in use problem. However, there are still blocking issues. (The API YML files end up in an _Api folder despite having no mention of such a thing in the docfx.json file, so it gets warning that ti is unable to find the toc.yml or toc.md file and ends up with no docs in the final output...)

(DOCFX just seems like an alpha release all around, and ironically has very little in the way of basic usage documentation)

@smaillet
Copy link

As an alternative to forcing single threaded builds I realized you can add the following property to your project:

<PropertyGroup>
    <LogFile>Docfx-$(TargetFramework).log</LogFile>
</PropertyGroup>

This creates a unique log file name for each TargetFramework and therfore they don't collide. Still, a pretty
bad user experience but at least it works.

If you don't disable the default inclusion "globs" then the log files wll become part of your project in VS so you may want to add the following as well:

<ItemGroup>
    <None Remove="Docfx-*.log" />
</ItemGroup>

@lextm
Copy link
Contributor Author

lextm commented Dec 10, 2017

@smaillet the toc warning you got is desired and not related to this file lock issue. That's caused by the "version" parts I used in the docfx.json. If you rip out all "version" related parts, then together with your workaround, the build can be successful.

@smaillet
Copy link

smaillet commented Dec 10, 2017

@lextm - I don't follow that version comment, I'm not building your sample, I'm using my own and I do see that toc warning and the _api folder being generated despite not having any such references. So, yes the warning is legit, however the tool shouldn't be getting into a state where that warning triggers.

In your case you aren't doing a merge, in mine I am, following the comment on Github for handling multiple TargetFrameworks. That is supposed to generate a seperate set of YML files into two distinct temp locations and then merge the results of both into a single result set in the 'api' folder. But that's not at all what is happening

@lextm
Copy link
Contributor Author

lextm commented Dec 10, 2017

@smaillet Thanks for mentioning the merge operation. I did a quick test but hit another bug #2286 .

Overall, this tool can work in simplest setup, but can break if you really touch the edges. Not surprising, but just hope the issues can be fixed soon.

@vicancy vicancy added the bug A bug to fix label Dec 11, 2017
@tom-made
Copy link

tom-made commented May 8, 2018

One of my projects is currently hitting this issue. Is there a workaround for this or is this still awaiting a fix?

@joymon
Copy link

joymon commented Aug 28, 2018

What is the solution / work around here?
Is it to edit the proj file as mentioned by @smaillet on Dec 10, 2017? Or any fix expected?

@Konard
Copy link

Konard commented Sep 13, 2019

As I found in the documentation, instead of:

        "properties": {
            "TargetFrameworks": "net452"
        },

Should be

        "properties": {
            "TargetFramework": "net452"
        },

@Mike-E-angelo
Copy link

As I found in the documentation, instead of:
...

Greetings all... I am investigating DocFX for https://github.com/ExtendedXmlSerializer/ExtendedXmlSerializer/issues/284

And right out of the gate I ran into this issue.

This is my metadata element from the generated docfx.json document after adding docfx.console v2.47.0.0 as a dependency and adding the properties as suggested above:

"metadata": [
    {
      "src": "*.csproj",
      "dest": "api",
      "properties": {
        "TargetFramework": "netstandard2.0"
      },
      "disableGitFeatures": false,
      "disableDefaultFilter": false
    }
  ],

Perhaps there is something obvious I am missing? I am still getting errors about locked log.txt file. 😞

1>[19-11-16 10:39:35.922]Error:System.IO.IOException: The process cannot access the file '<...>\src\ExtendedXmlSerializer\log.txt' because it is being used by another process.
1>   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
1>   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
1>   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
1>   at System.IO.StreamWriter.CreateFile(String path, Boolean append, Boolean checkHost)
1>   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize, Boolean checkHost)
1>   at System.IO.StreamWriter..ctor(String path, Boolean append)
1>   at Microsoft.DocAsCode.Common.ReportLogListener..ctor(String reportPath, String repoRoot, String root)
1>   at Microsoft.DocAsCode.SubCommands.CommandCreator`2.Create(String[] args, ISubCommandController controller, SubCommandParseOption option)
1>   at Microsoft.DocAsCode.SubCommands.CompositeCommand..ctor(String[] args, ISubCommandController controller, CompositeOptions options)
1>   at Microsoft.DocAsCode.SubCommands.CommandController.Create()
1>   at Microsoft.DocAsCode.Program.ExecSubCommand(String[] args)
1>docfx 2.47.0.0

@Mike-E-angelo
Copy link

This seems to do the trick:

  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
    <PackageReference Include="docfx.console" Version="2.47.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

Not sure how that's going to work for updating to latest from Nuget, however. 🤷‍♂️

It would seem that for each TargetFramework defined in the .csproj file, a complete buildset is executing, meaning if you have two elements in your metadata element within your docfx.json (SO MUCH CONFIGURATION 😉) there are two threads working to each build their own sets of netstandard2.0 and net45 documentation.

Even after the logging hack that @smaillet provided, I was still getting lock exceptions on other files because of this.

From this, it would seem that DocFX simply needs to make sure that this build has occurred once per entire build and not once per TargetFrameworks as defined in the .csproj file.

@Jason1269
Copy link

This is still an issue in version 2.58.8, please fix

Setting up conditional item group works to an extent, but you need to manually update csproj file every time you update the nuget pkg

@BionicCode
Copy link

Very annoying tat this is still not fixed to this date: 5 years later!

@BionicCode
Copy link

BionicCode commented May 24, 2022

The documentation reads:

"if your project targets multiple frameworks, you have to indicate one to be the main for the documentation, through the TargetFramework property in docfx.json:

"metadata": [ { "src": "...", "dest": "...", "properties": { "TargetFramework": <one_of_your_framework> } }, ]"

This gives a hint that Docfx has troubles building docs for multi target projects right out of the box.
But sadly, the suggested solution from the documentation doesn't work either.
There seems to be a bug in the way Docfx configures the build task for the target project.

Solution

The only solution that solved my issue was to add the following condition to the particular .csproj project file, where, in this case, the default version is chosen to be net6.0-windows:

<PropertyGroup>
    <BuildDocFx Condition="$(TargetFramework) != 'net6.0-windows'">false</BuildDocFx>
</PropertyGroup>

Docfx should add the <PropertyGroup> to the project file automatically. Unless a target framework was explicitly defined in the docfx.joson file, it should pick the first framework it finds inside the <TargetFrameworks> tag.

@macrogreg
Copy link

macrogreg commented Jun 1, 2022

The workaround described here works if the multitargeted library being documented has identical API surface for all targets. If not, it seems that the merge approach described above is required, however, that fails with a Null Reference.

#3067
#7106

Is there really no way to use DocFx with multi-targeting libraries? Seems like such a basic thing to prioritize, but I may be missing the context the team uses for deciding on issue priorities. Would it be please possible to learn whether there is any hope for this to be addressed in the foreseeable future, or should we be looking for completely different solutions for the time being?

@lextm
Copy link
Contributor Author

lextm commented Jun 1, 2022

@macrogreg If you read other threads, such as #7050, you should know that placing any hope on this project is not realistic.

@yufeih yufeih added the dotnet Generate .NET API reference docs label Dec 15, 2022
@yufeih yufeih closed this as not planned Won't fix, can't repro, duplicate, stale Sep 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A bug to fix dotnet Generate .NET API reference docs
Projects
None yet
Development

No branches or pull requests