Azure-functions-core-tools: F# function based on azure-functions-templates doesn't start; "Host.json file in missing"

Created on 30 Apr 2020  Â·  20Comments  Â·  Source: Azure/azure-functions-core-tools

This is similar to #1734 . I created a new F♯ function from the templates in Azure/azure-functions-templates.

I also installed v3 of the core tools via chocolatey, and when I try to run the function from the project directory, this is the result:

image

Here's the project file:

<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
  <Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>

I've opened an issue over in the template repository, but there's been no response yet, and I'm a bit at a loss for how to get this to work. https://github.com/Azure/azure-functions-templates/issues/945

Needs investigate

Most helpful comment

It looks like the problem is related to the F# project template.

Let's take a look at the project file here: https://github.com/Azure/azure-functions-templates/blob/dev/Functions.Templates/ProjectTemplate/FSharp/Company.FunctionApp.fsproj#L11-L17

  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>

Both of these items are <None> with a "include type" of Update. What happens here is that when MSBuild runs it will update any reference to host.json and set it's CopyToOutputDirectory to PreserveNewest. But, that file hasn't been added to the MSBuild "file list" that it's internally tracking and because it's an "Update" not an "InsertOrUpdate", it doesn't have anything to update and thus it's ignored.

If you change it to <None Include="host.json"> it will tell MSBuild that you want to explicitly include that file with the following metadata, which is why it then works.

So, if you look at the C# project template (https://github.com/Azure/azure-functions-templates/blob/dev/Functions.Templates/ProjectTemplate/CSharp/Company.FunctionApp.csproj#L11-L17) you might notice that it does the same as F# but doesn't have the same problem and you as yourself _"why is that?"_, well, it's because C# will include files by default, whereas F# is explicit includes (and that's why you don't need <Compile> items for all .cs files in SDK projects).

This is controlled by the file Microsoft.NET.Sdk.DefaultItems.targets - https://github.com/dotnet/sdk/blob/master/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.targets#L19 and this file is referenced implicitly when you use SDK project templates. F# on the other hand uses Microsoft.FSharp.NetSdk.props - https://github.com/dotnet/fsharp/blob/master/src/fsharp/FSharp.Build/Microsoft.FSharp.NetSdk.props#L34

Since the .props file is referenced first, it sets the value of EnableDefaultNoneItems to false and then when the Microsoft.NET.Sdk.DefaultItems.targets is used, the value is already set, so it won't set it to true.

The result is that the host.json and local.settings.json aren't included in the files MSBuild is tracking and thus output by MSBuild (either when you run dotnet build or func start).

TL;DR: Change <None Update="host.json"> to <None Include="host.json"> and I'll open a PR.

(PS: Big thanks to @davidwengier for helping me to understand MSBuild better and for this video)

All 20 comments

I've experimented a bit more with this, and it appears that the version of core tools distributed with VS2019 (3.0.2245) works. The difference is that 3.0.2245 _generates_ a host.json file in the output folder, whereas the latest version of the core tools (3.0.2358 via chocolatey) fail because it (a) doesn't copy the host.json file from the project as shown above, but also (b) doesn't generate the file like the VS version does.

Of course, generating the host.json file also has its problems. I can't meaningfully edit the file, for example. It gets deleted and re-generated every time I use func start.

I see that this has been "Triaged". Could someone from the team share what that means, whether or not there is a workaround I could use right now, whether this is something that will be worked on?

It seems that https://github.com/microsoft/msbuild/issues/4494 might be related to this; perhaps there was a regression in one of the recent .NET Core SDKs?

I have implemented a workaround by adding the following to a Directory.Build.targets:

<Project DefaultTarggets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="CopyFilesWorkaround" BeforeTargets="Build">
        <Copy SourceFiles="host.json;local.settings.json" DestinationFolder="$(OutDir)" />
    </Target>
</Project>

@aggieben "Triaged" means that we've looked at it and it's in our backlog to investigate further.

Can you check if it works if you have a reference to the Functions SDK like this https://github.com/Azure/azure-functions-templates/blob/2f6b05cbcae21dea993f386702b314605a5cfa4d/Functions.Templates/ProjectTemplate/FSharp/Company.FunctionApp.fsproj#L8 ?

It's needed for some file operations.

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.

I also came across this issue. I created the function using:
dotnet new func --language F#
dotnet new timer --language F# --name Timer

@anthonychu The fsproj file has a reference to the Functions SDK:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="BackupTimer\Backup.fs" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.5" />
    <PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.5.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.3" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

I used @aggieben 's workaround with the BeforeTargets hook to copy the files to the output directory which works for the time being. Thanks!

This issue should definitely not be closed, @anthonychu. The bot is quick on the “no activity” trigger, not to mention that the ball is actually in MSFTs court. The fact that we have a crappy workaround doesn’t change the fact that this is broken out of the box.

Thanks @caleb-motivity. I'm able to repro the issue. Reopening for investigation.

https://github.com/anthonychu/20200605-fsharp-func

dotnet build

# no host.json in bin/Debug/netcoreapp3.1


mv 20200605-fsharp-func.fsproj 20200605-fsharp-func.csproj
dotnet clean
dotnet build

# host.json exists in bin/Debug/netcoreapp3.1

Likely an issue with .NET SDK or functions templates.

@aaronpowell Do you know what's happening here?

/cc @fabiocav

I wonder if it's related to https://github.com/Azure/azure-functions-durable-extension/issues/1140 (and the observations I noted there).

I'll have a dig at it.

It looks like the problem is related to the F# project template.

Let's take a look at the project file here: https://github.com/Azure/azure-functions-templates/blob/dev/Functions.Templates/ProjectTemplate/FSharp/Company.FunctionApp.fsproj#L11-L17

  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>

Both of these items are <None> with a "include type" of Update. What happens here is that when MSBuild runs it will update any reference to host.json and set it's CopyToOutputDirectory to PreserveNewest. But, that file hasn't been added to the MSBuild "file list" that it's internally tracking and because it's an "Update" not an "InsertOrUpdate", it doesn't have anything to update and thus it's ignored.

If you change it to <None Include="host.json"> it will tell MSBuild that you want to explicitly include that file with the following metadata, which is why it then works.

So, if you look at the C# project template (https://github.com/Azure/azure-functions-templates/blob/dev/Functions.Templates/ProjectTemplate/CSharp/Company.FunctionApp.csproj#L11-L17) you might notice that it does the same as F# but doesn't have the same problem and you as yourself _"why is that?"_, well, it's because C# will include files by default, whereas F# is explicit includes (and that's why you don't need <Compile> items for all .cs files in SDK projects).

This is controlled by the file Microsoft.NET.Sdk.DefaultItems.targets - https://github.com/dotnet/sdk/blob/master/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.targets#L19 and this file is referenced implicitly when you use SDK project templates. F# on the other hand uses Microsoft.FSharp.NetSdk.props - https://github.com/dotnet/fsharp/blob/master/src/fsharp/FSharp.Build/Microsoft.FSharp.NetSdk.props#L34

Since the .props file is referenced first, it sets the value of EnableDefaultNoneItems to false and then when the Microsoft.NET.Sdk.DefaultItems.targets is used, the value is already set, so it won't set it to true.

The result is that the host.json and local.settings.json aren't included in the files MSBuild is tracking and thus output by MSBuild (either when you run dotnet build or func start).

TL;DR: Change <None Update="host.json"> to <None Include="host.json"> and I'll open a PR.

(PS: Big thanks to @davidwengier for helping me to understand MSBuild better and for this video)

🤯 Thanks for tracking this down @aaronpowell!

/cc @fabiocav

@anthonychu and @aaronpowell I was running into the same issue. By adding the following itemgroup

 <ItemGroup>
    <None Include="../host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="../local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>

I can confirm that my azure function is executing locally with the latest Azure Function core tool. Thanks for your help on that.

The suggested workaround has the unfortunate side effect of including the local.settings.json among the artifacts deployed into Azure - at least when Azure Functions Tools with VS Code is used.

@vjraitila oh that's a good point, I wonder if there's a smarter way to have it excluded from the output. Maybe a conditional include is better.

What I've done in my CD workflow is simply create a stub file before build—because <CopyToPublishDirectory>Never</CopyToPublishDirectory> is set it'll never make it into the published output dir.

.fsproj:

<ItemGroup>
  <None Include="host.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
  <None Include="local.settings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
  </None>
</ItemGroup>

action:

- name: Build and publish
  run: |
    touch local.settings.json
    dotnet build -c Release
    dotnet publish -c Release -o ./output

Seems to work as expected for me now as well when deploying from VS Code. As @pheuter mentioned, <CopyToPublishDirectory>Never</CopyToPublishDirectory> is being respected and func start still works locally. Either something changed in the latest iteration of vscode-azurefunctions or I just messed something up - probably the latter.

But yes. The local settings file is also now required to exist for a successful CI build when <None Include="local.settings.json"> is used, whereas for <None Update="local.settings.json"> it does not. Is this really desired behaviour as said file is really not meant to be committed into version control and isn't strictly required for the build either?

As mentioned above, for C# projects the file is included by default (if exists) and the Update-attribute is designed to only update the item metadata. If the file does not exist, it is obviously not included, the update directive has no effect either and the build succeeds. Whereas the workaround for an F# project now expects the file to exist - as it is explicitly included. At least this is how I understand it.

Intuitively, the proper fix would be to implement similar default includes for F# projects instead. Or, alternatively, if further divergence from the way C# projects are handled is acceptable, a conditional include for this specific file could work as well in the Azure Functions template.

This seems to work for me without having to create stub files during the CI build.

.fsproj:

<ItemGroup>
  <None Include="host.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
  <None Include="local.settings.json" Condition="Exists('local.settings.json')">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
  </None>
</ItemGroup>

Wow. I just fell for this as well. Just changing from Update to Include fixes it.

Where is the source code - can someone just change this and submit a PR? It's totally broken at the moment - a really unfortunate situation and will be a poor first-time experience for functional programmers trying to write functions (!).

@isaacabraham I created a PR for the template to fix it: https://github.com/Azure/azure-functions-templates/pull/954 but as @cartermp points out, this is a related issue on the compiler/build (https://github.com/dotnet/fsharp/issues/8914), and as @vjraitila points out, changing the inclusion mode can result in it being published to Azure, which you don't want.

I think using the Condition attribute of the None for inclusion is probably the quickest solution, but it's not a perfect one.

Was this page helpful?
0 / 5 - 0 ratings