Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a .NET Core CLI pre before build task?

The question

I assume when msbuild is about to compile source code files, it produces a list of files to build, based on Include and Exclude rules of the project.

Is there a way to execute a task before the list of files to compile is evaluated ?

This is to be able to generate a source code file and get it to be taken in the build.

Current research and trials

I'm making a .NET Core CLI tool that has to be run before the build of the project that is using it, because it (the CLI tool) generates a file that has to be included in the build.

The project is created with the new .csproj system, not the old project.json one.

Along with my .NET Core CLI tool project, I created a library project for testing purpose.

If I add this in the .csproj of the testing library:

<ItemGroup>
    <DotNetCliToolReference Include="MyCliTool" Version="x.x.x" />
    <!-- here x.x.x is just a placeholder -->
</ItemGroup>

<Target Name="MyCliToolTarget" AfterTargets="Restore" BeforeTargets="BeforeBuild">
    <Exec Command="dotnet my-cli-tool" />
</Target>

then the file generated by the CLI tool is not taken into account in the compilation if it didn't exist before. That means when the file exists, it is OK but it also means the very first build (after a clone, cleanup or what) will always fail.

I tried several different targets for BeforeTargets but I couldn't find a way to make it work. I tried to set my target in the InitialTargets of the Project node, but it didn't work either. I tried to set the Outputs property of the Target node to the filename generated by the CLI tool, but same, it fails.

The only solution I found that worked is to manually add a Compile directive, as follow:

<ItemGroup>
    <Compile Include="MyGeneratedFile.cs" />
</ItemGroup>

This solution is fine for the moment, but the filename may change based on the CLI tool options, and this would make two places where a filename would have to be updated on change, like the following:

<ItemGroup>
    <Compile Include="PATH\TO\CUSTOM_FILENAME_HERE.CS" />
    <DotNetCliToolReference Include="MyCliTool" Version="x.x.x" />
</ItemGroup>

<Target Name="MyCliToolTarget" AfterTargets="Restore" BeforeTargets="BeforeBuild">
    <Exec Command="dotnet my-cli-tool --output PATH\TO\CUSTOM_FILENAME_HERE.CS" />
</Target>

(see CUSTOM_FILENAME_HERE.CS appears twice)

I know I could use a constant, as follow:

<PropertyGroup>
    <MyFilename>PATH\TO\CUSTOM_FILENAME_HERE.CS</MyFilename>
</PropertyGroup>

<ItemGroup>
    <Compile Condition="!Exists('$(MyFilename)')" Include="$(MyFilename)" />
    <DotNetCliToolReference Include="MyCliTool" Version="x.x.x" />
</ItemGroup>

<Target Name="MyCliToolTarget" AfterTargets="Restore" BeforeTargets="BeforeBuild">
    <Exec Command="dotnet my-cli-tool --output $(MyFilename)" />
</Target>

but I'm not satisfied with this approach, it makes things way too complicated for a lambda user to integrate, assuming that this is still a simplified version, because the CLI tool can take several other options, meaning having other variables, bla bla bla.

I'm using .NET Core SDK 1.0.1.

Sorry for the lengthy question and my noobness with msbuild.

Thanks in advance for your time and help.




Side note: calling dotnet my-cli-tool in pre-build event, as in:

<PropertyGroup>
    <PreBuildEvent>dotnet my-cli-tool</PreBuildEvent>
</PropertyGroup>

doesn't work at all, I get the following error:

Code: MSB3073 Description: The command "dotnet my-cli-tool" exited with code 1. Project: the testing library project, not the tool one File: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets Line: 4935

Though, the following works fine:

<PropertyGroup>
    <PreBuildEvent>echo meh</PreBuildEvent>
</PropertyGroup>

so this is not a bug with the pre build events.

Anyway this is another story which I don't care much about for the moment.

like image 374
Sebastien ROBERT Avatar asked Jun 29 '17 07:06

Sebastien ROBERT


People also ask

Which command in dotnet CLI is used to build a .NET core application?

To create a new . NET Core project, we have to use new command followed by template name argument. We can create console, class library, web, mvc, webapi, razor, angular, react etc. projects using CLI.

Does dotnet pack also build?

By default, dotnet pack builds the project first. If you wish to avoid this behavior, pass the --no-build option. This option is often useful in Continuous Integration (CI) build scenarios where you know the code was previously built. In some cases, the implicit build cannot be performed.

What is dotnet build command?

The dotnet build command builds the project and its dependencies into a set of binaries. The binaries include the project's code in Intermediate Language (IL) files with a . dll extension.


2 Answers

The "default items" as the .NET SDK calls it are part of the static evaluation of the project file - before any target is run. So you'll need a target that is run before the @(Compile) items are needed.

The trick is to include files added to the filesystem after the custom tool is run. This can be done by re-scanning all files and excluding those already part of the project inside a target that is run before the build:

  <Target Name="GenerateSomeFiles" BeforeTargets="BeforeBuild">
    <Exec Command="dotnet my-tool" />
    <ItemGroup>
      <Compile Include="**/*$(DefaultLanguageSourceExtension)"
               Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(BaseIntermediateOutputPath)**;$(BaseOutputPath)**;@(Compile)" />
    </ItemGroup>
  </Target>
like image 171
Martin Ullrich Avatar answered Oct 10 '22 06:10

Martin Ullrich


If your custom tool creates just one file, you can create it directly in the intermediate output path and include it in your compile:

  <Target Name="GenerateSomeFiles" BeforeTargets="CoreCompile" DependsOnTargets="PrepareForBuild">
    <Exec Command="dotnet my-tool $(MSBuildProjectDirectory)\$(IntermediateOutputPath)\my-output.cs" />
  </Target>

  <ItemGroup>
    <Compile Include="$(MSBuildProjectDirectory)\$(IntermediateOutputPath)\my-output.cs" />
  </ItemGroup>
like image 37
shuelsmeier Avatar answered Oct 10 '22 04:10

shuelsmeier