Skip to content

Commit

Permalink
Custom host changes for VS Scenario (#3831)
Browse files Browse the repository at this point in the history
* this doesn't work

* this finally works now

* default should be oop host

* added edge cases

* fixing formatting

* using determineTargetFramework

* saving tests

* adding copy step for OOP

* fixing the webhost reference

* removing test

* setting host version in ps script

* update to validate worker versions script

* update versions for worker packages

* adding ToString

* validating worker version

* validate worker versions

* updating csproj to compile

* addressing pr feedback

* updating build steps

* adding build step

* fixing build step

* trying to get this working

* reverrting target runtimes

* updating tests

* adding dotnet info step

* adding changes

* removing extra test

* trying to specify architecture

* modifying tests to see if they work

* narrowing down to tests that are failing

* trying to see if it works with nobuild flag

* addressing pr feedback

* updating tests with latest logging

* addressing comments and marking flaky tests

* updating so that we are only using net8 framework

* pushing change for branch build

* adding single quotes

* reverting quotes

* adding code mirror fiile

* updating build step

* updatinng build steps

* updating build step

* adding step for dotnet publish

* set inprochost compilation system to diff value and skip flaky test

* updating public build pipeline to trigger

* adding extra changes for pipeline

* public build yml

* updating official build

* readd net8 build artifact step

* readding space back

* addressing initial comments

* adding explicit openTelemetry dlls

* simplifying logic of startHostAction

* addressing PR feedback

* start tests

* changing some of the tests back

* reverting test back to normal

* fixing spacing for csproj

* addressing PR feedback

* adding extra variable

* adding logic for edge case scenarios

* added edge cases tests

* initial changes

* removing extra line in node

* removing files not needed for custom host

* adding build steps

* addressing comments

* moving validate host runtime to its own method

* forgot to add return statement

* trying out creating directory

* remove skip artifact gen

* updating build step

* updating build to include in official pipeline

* updating publishing custom host step

* code mirror

* trying to get official build working

* program cs update

* updating custom host to work with parsing local.settings.json

* console read line for stalling process

* removing changes not needed for custom host

* only including inproc6 and inproc8 changes

* addressing initial comments

* removing skip artifact gen flag

* verbose logging

* tryna add release pipeline

* windows yaml changes

* addressing comments about yml

* updating log message

* fixing spacing

* fixing spacing again

* trying spacing again

* spacing change

* tryan fix spacing this way

* spacing

* spacing

* new line

* regular indentation

* super indent

* changing spacing back to normal

* addressing comments

* fixing pipeline
  • Loading branch information
aishwaryabh authored Sep 24, 2024
1 parent 2d35b1b commit 7b7abaf
Show file tree
Hide file tree
Showing 15 changed files with 509 additions and 4 deletions.
63 changes: 63 additions & 0 deletions build-core-tools-host-artifacts-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
jobs:

- job: BuildCoreToolsHostWindows
displayName: '[Windows] Build CoreToolsHost'

templateContext:
outputParentDirectory: $(Build.ArtifactStagingDirectory)
outputs:
- output: pipelineArtifact
displayName: Publish CoreToolsHost packages
path: $(Build.ArtifactStagingDirectory)/_coreToolsHostPackagesWindows
artifact: _coreToolsHostPackagesWindows

steps:
- task: UseDotNet@2
displayName: Install .NET SDK from global.json
inputs:
useGlobalJson: true

- task: DotnetCoreCLI@2
displayName: Dotnet Publish (win-x64)
inputs:
command: publish
publishWebProjects: false
zipAfterPublish: false
arguments: -c Release -r win-x64 -o $(Build.SourcesDirectory)/pkg_output/windows/win-x64
workingDirectory: $(Build.SourcesDirectory)/host/src/CoreToolsHost

- task: DotnetCoreCLI@2
displayName: Dotnet Publish (win-arm64)
inputs:
command: publish
publishWebProjects: false
zipAfterPublish: false
arguments: -c Release -r win-arm64 -o $(Build.SourcesDirectory)/pkg_output/windows/win-arm64
workingDirectory: $(Build.SourcesDirectory)/host/src/CoreToolsHost

- task: CopyFiles@2
displayName: Copy files (win-x64)
inputs:
SourceFolder: $(Build.SourcesDirectory)/pkg_output/windows/win-x64
# Publish output will include many other files. We only need func.exe, pdb & nethost.dll
Contents: |
func.exe
nethost.dll
TargetFolder: $(Build.ArtifactStagingDirectory)/_coreToolsHostPackagesWindows/win-x64

- task: CopyFiles@2
displayName: Copy files (win-arm64)
inputs:
SourceFolder: $(Build.SourcesDirectory)/pkg_output/windows/win-arm64
# Publish output will include many other files. We only need func.exe, pdb & nethost.dll
Contents: |
func.exe
nethost.dll
TargetFolder: $(Build.ArtifactStagingDirectory)/_coreToolsHostPackagesWindows/win-arm64

- task: PublishPipelineArtifact@1
displayName: 'Publish CoreToolsHost packages artifact'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/_coreToolsHostPackagesWindows'
artifact: '_coreToolsHostPackagesWindows'
publishLocation: 'pipeline'
4 changes: 2 additions & 2 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ if ($env:IntegrationBuildNumber)
throw $errorMessage
}

$buildCommand = { dotnet run --integrationTests --skipArtifactGeneration}
$buildCommand = { dotnet run --integrationTests }
}
else
{
$buildCommand = { dotnet run --ci --skipArtifactGeneration}
$buildCommand = { dotnet run --ci }
}

Write-Host "Running $buildCommand"
Expand Down
4 changes: 2 additions & 2 deletions build/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ static void Main(string[] args)
.Then(TestPreSignedArtifacts, skip: !args.Contains("--ci"))
.Then(CopyBinariesToSign, skip: !args.Contains("--ci"))
.Then(Test)
.Then(Zip, skip: args.Contains("--skipArtifactGeneration"))
.Then(Zip)
.Then(DotnetPublishForNupkg)
.Then(DotnetPack)
.Then(CreateIntegrationTestsBuildManifest, skip: !args.Contains("--integrationTests"))
.Then(UploadToStorage, skip: !args.Contains("--ci") || args.Contains("--skipArtifactGeneration"))
.Then(UploadToStorage, skip: !args.Contains("--ci"))
.Run();
}
}
Expand Down
111 changes: 111 additions & 0 deletions host/src/CoreToolsHost/AppLoader/AppLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Runtime.InteropServices;

namespace CoreToolsHost
{
// If having problems with the managed host, enable the following:
// Environment.SetEnvironmentVariable("COREHOST_TRACE", "1");
// In Unix environment, you need to run the below command in the terminal to set the environment variable.
// export COREHOST_TRACE=1

/// <summary>
/// Manages loading hostfxr
/// </summary>
internal sealed class AppLoader : IDisposable
{
private IntPtr _hostfxrHandle = IntPtr.Zero;
private IntPtr _hostContextHandle = IntPtr.Zero;
private bool _disposed;
private bool isVerbose = false;

internal int RunApplication(string? assemblyPath, string[] commandLineArgs)
{
ArgumentNullException.ThrowIfNull(assemblyPath, nameof(assemblyPath));

unsafe
{
var parameters = new NetHost.get_hostfxr_parameters
{
size = sizeof(NetHost.get_hostfxr_parameters),
assembly_path = GetCharArrayPointer(assemblyPath)
};

isVerbose = commandLineArgs.Contains(DotnetConstants.Verbose);

var hostfxrFullPath = NetHost.GetHostFxrPath(&parameters);
Logger.LogVerbose(isVerbose, $"hostfxr path:{hostfxrFullPath}");

_hostfxrHandle = NativeLibrary.Load(hostfxrFullPath);

if (_hostfxrHandle == IntPtr.Zero)
{
Logger.Log($"Failed to load hostfxr. hostfxrFullPath:{hostfxrFullPath}");
return -1;
}

Logger.LogVerbose(isVerbose, $"hostfxr loaded.");

var commandLineArguments = commandLineArgs.Prepend(assemblyPath).ToArray();
var error = HostFxr.Initialize(commandLineArguments.Length, commandLineArguments, IntPtr.Zero, out _hostContextHandle);

if (_hostContextHandle == IntPtr.Zero)
{
Logger.Log($"Failed to initialize the .NET Core runtime. Assembly path:{assemblyPath}");
return -1;
}

if (error < 0)
{
return error;
}
Logger.LogVerbose(isVerbose, $"hostfxr initialized with {assemblyPath}");

return HostFxr.Run(_hostContextHandle);
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (!_disposed)
{
if (!disposing)
{
return;
}

if (_hostfxrHandle != IntPtr.Zero)
{
NativeLibrary.Free(_hostfxrHandle);
Logger.LogVerbose(isVerbose, $"Freed hostfxr library handle");
_hostfxrHandle = IntPtr.Zero;
}

if (_hostContextHandle != IntPtr.Zero)
{
HostFxr.Close(_hostContextHandle);
Logger.LogVerbose(isVerbose, $"Closed hostcontext handle");
_hostContextHandle = IntPtr.Zero;
}

_disposed = true;
}
}

private static unsafe char* GetCharArrayPointer(string assemblyPath)
{
#if OS_LINUX
return (char*)Marshal.StringToHGlobalAnsi(assemblyPath).ToPointer();
#else
return (char*)Marshal.StringToHGlobalUni(assemblyPath).ToPointer();
#endif
}
}
}
46 changes: 46 additions & 0 deletions host/src/CoreToolsHost/AppLoader/HostFxr.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Runtime.InteropServices;

namespace CoreToolsHost
{
static partial class HostFxr
{
public unsafe struct hostfxr_initialize_parameters
{
public nint size;
public char* host_path;
public char* dotnet_root;
};

[LibraryImport("hostfxr", EntryPoint = "hostfxr_initialize_for_dotnet_command_line",
#if OS_LINUX
StringMarshalling = StringMarshalling.Utf8
#else
StringMarshalling = StringMarshalling.Utf16
#endif
)]
public unsafe static partial int Initialize(
int argc,
string[] argv,
IntPtr parameters,
out IntPtr host_context_handle
);

[LibraryImport("hostfxr", EntryPoint = "hostfxr_run_app")]
public static partial int Run(IntPtr host_context_handle);

[LibraryImport("hostfxr", EntryPoint = "hostfxr_set_runtime_property_value",
#if OS_LINUX
StringMarshalling = StringMarshalling.Utf8
#else
StringMarshalling = StringMarshalling.Utf16
#endif
)]
public static partial int SetAppContextData(IntPtr host_context_handle, string name, string value);

[LibraryImport("hostfxr", EntryPoint = "hostfxr_close")]
public static partial int Close(IntPtr host_context_handle);
}
}
41 changes: 41 additions & 0 deletions host/src/CoreToolsHost/AppLoader/NetHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Runtime.InteropServices;

namespace CoreToolsHost
{
internal class NetHost
{
public unsafe struct get_hostfxr_parameters
{
public nint size;

// Optional.Path to the application assembly,
// If specified, hostfxr is located from this directory if present (For self-contained deployments)
public char* assembly_path;
public char* dotnet_root;
}

[DllImport("nethost", CharSet = CharSet.Auto)]
private unsafe static extern int get_hostfxr_path(
[Out] char[] buffer,
[In] ref int buffer_size,
get_hostfxr_parameters* parameters);

internal unsafe static string GetHostFxrPath(get_hostfxr_parameters* parameters)
{
char[] buffer = new char[200];
int bufferSize = buffer.Length;

int rc = get_hostfxr_path(buffer, ref bufferSize, parameters);

if (rc != 0)
{
throw new InvalidOperationException("Failed to get the hostfxr path.");
}

return new string(buffer, 0, bufferSize - 1);
}
}
}
27 changes: 27 additions & 0 deletions host/src/CoreToolsHost/CoreToolsHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<PublishAot>true</PublishAot>
<OptimizationPreference>Speed</OptimizationPreference>
<AssemblyName>func</AssemblyName>
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
</PropertyGroup>

<PropertyGroup>
<DefineConstants Condition=" '$([MSBuild]::IsOSPlatform(`Linux`))' == 'true' ">OS_LINUX</DefineConstants>
</PropertyGroup>

<PropertyGroup>
<ExportsFile>$(MSBuildThisFileDirectory)exports.def</ExportsFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NETCore.DotNetAppHost" Version="8.0.8" />
</ItemGroup>

</Project>
17 changes: 17 additions & 0 deletions host/src/CoreToolsHost/DotnetConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreToolsHost
{
internal static class DotnetConstants
{
public const string ExecutableName = "func.dll";
public const string InProc8DirectoryName = "in-proc8";
public const string InProc6DirectoryName = "in-proc6";
public const string DotnetWorkerRuntime = "dotnet";
public const string Verbose = "--verbose";
}
}
37 changes: 37 additions & 0 deletions host/src/CoreToolsHost/Environment/EnvironmentUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace CoreToolsHost
{
internal static class EnvironmentUtils
{
#if OS_LINUX
[System.Runtime.InteropServices.DllImport("libc")]
private static extern int setenv(string name, string value, int overwrite);
#endif

/// <summary>
/// Gets the environment variable value.
/// </summary>
internal static string? GetValue(string environmentVariableName)
{
return Environment.GetEnvironmentVariable(environmentVariableName);
}

/// <summary>
/// Sets the environment variable value.
/// </summary>
internal static void SetValue(string name, string value)
{
/*
* Environment.SetEnvironmentVariable is not setting the value of the parent process in Unix.
* So using the native method directly here.
* */
#if OS_LINUX
setenv(name, value, 1);
#else
Environment.SetEnvironmentVariable(name, value);
#endif
}
}
}
17 changes: 17 additions & 0 deletions host/src/CoreToolsHost/Environment/EnvironmentVariables.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace CoreToolsHost;

internal static class EnvironmentVariables
{
/// <summary>
/// The worker runtime. Example value: "dotnet-isolated"
/// </summary>
internal const string FunctionsWorkerRuntime = "FUNCTIONS_WORKER_RUNTIME";

/// <summary>
/// Determines if Functions InProc NET8 is enabled for a .NET 8 in-proc app
/// </summary>
internal const string FunctionsInProcNet8Enabled = "FUNCTIONS_INPROC_NET8_ENABLED";
}
Loading

0 comments on commit 7b7abaf

Please sign in to comment.