dotnet / docfx

Static site generator for .NET API documentation.
https://dotnet.github.io/docfx/
MIT License
3.93k stars 839 forks source link

MSBUILD Integration? #8414

Open softworkz opened 1 year ago

softworkz commented 1 year ago

Is this completely dropped now?

Can I no longer have a Visual Studio Project which builds the documentation as part of the project build?

Thanks

lanthonyneville commented 1 year ago

@softworkz - I struggled with this, but got it working. Take a look at: How to build DocFx site via Visual Studio/Azure DevOps/TFS

softworkz commented 1 year ago

@lanthonyneville - Thanks a lot - nice write-up!

Though, we have decided to stick with (a customized) v2.59 for the next years and make required modifications ourselves. Meanwhile, we had the need for an additional xref property and additional header properties for conceptual documents, also some plugins needed modifications, so a custom package is required anyway.

Also, TBH, the past 1-2 years have shown that the project cannot be trusted to make reliable, expectable and non-breaking progress:

I'm sure your solution is working fine and I understand that it's possible to use a specific version of the dotnet tool by creating some json file. 
But why should everybody have to do this now and spend energy and research on this topic? This is wasting thousands of hours in total just for nothing. The console method was fine and it worked well. Nothing against adding another way, but there was no need to remove the console package.

We have four projects using docfx and no time to continuously react to breaking updates, so disconnecting seems to be the safest approach at this time. Hopefully it will get better in the future

inthemedium commented 7 months ago

I'm also disappointed by the lack of communication from this project (and I can message the devs directly on teams!).

Here's the workaround I came up with, you can basically take the old targets inside the docfx.console package and put them inside your project or inside Directory.Build.targets (I have multiple projects so that's the approach I will take here). I have some additional changes to the original targets for things we need (i.e. a longer git timeout [side note: would have preferred exponential backoff rather just setting a timeout as an env variable...])

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask
      TaskName="SetEnvironmentVariableTask"
      TaskFactory="CodeTaskFactory"
      AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">

    <ParameterGroup>
      <Name ParameterType="System.String" Required="true" />
      <Value ParameterType="System.String" Required="true" />
    </ParameterGroup>

    <Task>
      <Using Namespace="System" />
      <Code Type="Fragment" Language="cs">
        <![CDATA[
          Environment.SetEnvironmentVariable(Name, Value);
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <!--Launch a Process in Parallel-->
  <UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <!--The file path is the full path to the executable file to run-->
      <FilePath ParameterType="System.String" Required="true" />
      <!--The arguments should contain all the command line arguments that need to be sent to the application-->
      <Arguments ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
  string name = System.IO.Path.GetFileNameWithoutExtension(FilePath);
  Log.LogMessage("Starting {0}...", name);        
  System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(FilePath, Arguments);
  processStartInfo.UseShellExecute = true;
  System.Diagnostics.Process.Start(processStartInfo);
  Log.LogMessage("Finished running process {0}.", name);
  ]]>
      </Code>
    </Task>
  </UsingTask>

  <ItemGroup>
    <PackageReference Include="docfx" ExcludeAssets="all" GeneratePathProperty="true" Condition="'$(MSBuildProjectExtension)' == '.csproj'" />
  </ItemGroup>

  <PropertyGroup>
    <OutputFolder>$(OutDir.Replace('\', '/'))</OutputFolder>
    <PreviewOutputFolder>$(OutputFolder)_site</PreviewOutputFolder>
    <MetadataOutputFolder>$(OutputFolder)</MetadataOutputFolder>
  </PropertyGroup>

  <Import Project="$([MSBuild]::GetPathOfFileAbove('$(MSBuildThisFile)', '$(MSBuildThisFileDirectory)../'))" />

  <PropertyGroup>
    <BuildDocToolPrefix>dotnet</BuildDocToolPrefix>
    <_BuildDocToolPrefixFinal Condition="'$(BuildDocToolPrefix)' != ''">&quot;$(BuildDocToolPrefix)&quot; </_BuildDocToolPrefixFinal>
    <BuildDocToolPath>$(Pkgdocfx)\tools\net8.0\any\docfx.dll</BuildDocToolPath>
    <DocfxConfigFile Condition=" '$(DocfxConfigFile)' == '' ">$(MSBuildProjectDirectory)/docfx.json</DocfxConfigFile>
    <PreviewPort Condition=" '$(PreviewPort)' == '' ">8002</PreviewPort>
    <IsServing Condition="'$(IsServing)'==''">False</IsServing>
    <PreviewOutputFolder Condition=" '$(PreviewOutputFolder)' == '' ">$(OutputFolder)/_site</PreviewOutputFolder>
    <MetadataOutputFolder Condition=" '$(MetadataOutputFolder)' == '' ">$(OutputFolder)</MetadataOutputFolder>
    <LogFile Condition=" '$(LogFile)' == '' ">log.txt</LogFile>
    <LogLevel Condition=" '$(LogLevel)' == '' ">Warning</LogLevel>
    <BuildDocFx Condition=" '$(BuildDocFx)' == '' ">true</BuildDocFx>
    <BuildDocFx Condition=" '$(BuildingForLiveUnitTesting)' == 'true' ">false</BuildDocFx>

    <!-- Website project's output directory is current folder, disable it as a temp workaround until a better way is found-->
    <CopyToOutput>False</CopyToOutput>
    <RebuildDoc Condition=" '$(RebuildDoc)' == '' ">False</RebuildDoc>
  </PropertyGroup>
  <Target Name="DocRebuild" Condition="'$(BuildDocFx)' == 'true'">
    <CallTarget Targets="DocClean"/>
    <CallTarget Targets="DocBuild"/>
  </Target>
  <Target Name="DocBuild" AfterTargets="CoreCompile" Condition="'$(BuildDocFx)' == 'true'">
    <CallTarget Targets="DocValidation"/>
    <CallTarget Targets="DocGenerateMetadata"/>
  </Target>
  <Target Name="DocValidation" Condition="'$(BuildDocFx)' == 'true'">
    <Error Condition="!Exists($(BuildDocToolPath))" Text="docfx.dll is not found! Please check docfx.exe exists in tools folder of the package!"/>
  </Target>
  <Target Name="DocClean" DependsOnTargets="DocSetRebuildXDocParameter" AfterTargets="Clean" Condition="'$(BuildDocFx)' == 'true'">
  </Target>
  <Target Name="DocSetRebuildXDocParameter" Condition="'$(BuildDocFx)' == 'true'">
    <PropertyGroup>
      <RebuildDoc>True</RebuildDoc>
    </PropertyGroup>
  </Target>
  <Target Name="SetDOCFX_GIT_TIMEOUT" BeforeTargets="CoreCompile">
      <SetEnvironmentVariableTask Name="DOCFX_GIT_TIMEOUT" Value="10000" /><!-- 10 second git timeout. See here: https://github.com/dotnet/docfx/pull/8553/files-->
  </Target>
  <!-- TODO file bug on DocFX because we have to redefine their targets to get
       a warning to fail the build -->
  <Target Name="DocServe" Condition="'$(BuildDocFx)' == 'true'">
    <ExecAsync FilePath="$(_BuildDocToolPrefixFinal)$(BuildDocToolPath)" Arguments="serve &quot;$(PreviewOutputFolder)&quot;" />
  </Target>
  <Target Name="DocPreview" Condition="'$(BuildDocFx)' == 'true'">
    <Exec Condition="$(IsServing)" Command="start cmd /c &quot;&quot;$(_BuildDocToolPrefixFinal) $(BuildDocToolPath)&quot; build &quot;$(DocfxConfigFile)&quot; -o &quot;$(PreviewOutputFolder)&quot; --template &quot;$(DocTemplate)&quot; --serve -l &quot;$(LogFile)&quot; --logLevel &quot;$(LogLevel)&quot; --warningsAsErrors&quot;" />
    <Exec Condition="!$(IsServing)" Command="$(_BuildDocToolPrefixFinal)&quot;$(BuildDocToolPath)&quot; build &quot;$(DocfxConfigFile)&quot; -o &quot;$(PreviewOutputFolder)&quot; --template &quot;$(DocTemplate)&quot; -l &quot;$(LogFile)&quot; --logLevel &quot;$(LogLevel) --warningsAsErrors&quot;" />
  </Target>

  <!-- ************************************************************************* -->
  <!-- *************************** GenerateMetadata Phase ********************** -->
  <!-- ************************************************************************* -->
  <Target Name="DocGenerateMetadata" Condition="'$(BuildDocFx)' == 'true'">
    <PropertyGroup>
      <DocGenerateCommand>$(_BuildDocToolPrefixFinal)&quot;$(BuildDocToolPath)&quot; &quot;$(DocfxConfigFile)&quot; -o &quot;$(MetadataOutputFolder)&quot; -l &quot;$(LogFile)&quot; --logLevel &quot;$(LogLevel)&quot; --warningsAsErrors</DocGenerateCommand>
      <DocGenerateCommand Condition="$(RebuildDoc)">$(DocGenerateCommand) -f</DocGenerateCommand>
      <DocGenerateCommand Condition="'$(DocTemplate)' != ''">$(DocGenerateCommand) --template &quot;$(DocTemplate)&quot; </DocGenerateCommand>
      <DocGenerateCommand Condition="'$(DocParameters)' != ''">$(DocGenerateCommand) $(DocParameters)</DocGenerateCommand>
    </PropertyGroup>
    <Message Condition="!Exists($(DocfxConfigFile))" Text="Init docfx config files" />
    <Exec Condition="!Exists($(DocfxConfigFile))" Command="$(_BuildDocToolPrefixFinal)&quot;$(BuildDocToolPath)&quot; init -o &quot;$(MSBuildProjectDirectory)&quot; -q --apiGlobPattern **.csproj --apiSourceFolder &quot;$(MSBuildProjectDirectory)&quot; --warningsAsErrors" />
    <Message Text="Executing $(DocGenerateCommand)" />
    <Exec Command="$(DocGenerateCommand)"></Exec>
  </Target>
</Project>
moamenhredeen commented 7 months ago

Hi, i want to integrate docfx in my project. we still use msbuild to build our project, this means i can not use the suggested solutions / workaround. the better solution would be to write custom msbuild task. i could just create the custom task in my project and it should works. but i think, it would be better if i contribute the custom task to docfx. So, my question is: can i work on this issue ? my solution would be to create new project (something like "Docfx.MSBuild") and define the the custom task with in this new project. (sorry, my english is not good)

lanthonyneville commented 7 months ago

@moamenhredeen - my solution above (How to build DocFx site via Visual Studio/Azure DevOps/TFS) should work under MSBUILD. I have it running in an Azure DevOps 2019 server (on-premises) pipeline. Which part does not work?

moamenhredeen commented 7 months ago

@lanthonyneville you use dotnet tool to call docfx. we don't/can't have dotnet on our ci server. msbuild custom task make it possible to use docfx from msbuild and you get better logging and error handling