From 55fff1f5b14e0c87d38c95b5f26e00c780ae1b5b Mon Sep 17 00:00:00 2001 From: John McCrae Date: Fri, 28 Aug 2026 12:28:05 -0700 Subject: [PATCH 01/14] Correcting a bug we accidentally introduced Signed-off-by: John McCrae --- .../lib/chef-powershell/powershell.rb | 42 +++++++- chef-powershell/lib/chef-powershell/pwsh.rb | 20 ---- .../spec/integration/dll_integration_spec.rb | 102 +++++++++++++++++- 3 files changed, 140 insertions(+), 24 deletions(-) diff --git a/chef-powershell/lib/chef-powershell/powershell.rb b/chef-powershell/lib/chef-powershell/powershell.rb index feda5a6f..eed75e22 100644 --- a/chef-powershell/lib/chef-powershell/powershell.rb +++ b/chef-powershell/lib/chef-powershell/powershell.rb @@ -21,6 +21,26 @@ require_relative "unicode" class ChefPowerShell + module Kernel32 + extend FFI::Library + ffi_lib "kernel32" + attach_function :SetDllDirectoryA, %i{string}, :int + attach_function :SetDefaultDllDirectories, %i{uint32}, :int + attach_function :AddDllDirectory, %i{pointer}, :pointer + + # https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 + + # AddDllDirectory requires a wide (UTF-16LE), null-terminated string (LPCWSTR). + # Returns true if the directory was registered successfully. + def self.register_search_directory(path) + wide_path = (path + "\0").encode("UTF-16LE") + ptr = FFI::MemoryPointer.new(:uint8, wide_path.bytesize) + ptr.put_bytes(0, wide_path) + !(AddDllDirectory(ptr).address == 0) + end + end + def self.bin_dir if ENV["CHEF_POWERSHELL_BIN"] && !ENV["CHEF_POWERSHELL_BIN"].empty? return File.expand_path(ENV["CHEF_POWERSHELL_BIN"]) @@ -59,7 +79,27 @@ def initialize(script, timeout: -1) # Every merge into that repo triggers a Habitat build and verification process. # There is no mechanism to build a Windows gem file. It has to be done manually running manual_gem_release.ps1 # Bundle install ensures that the correct architecture binaries are installed into the path. - @powershell_dll = File.join(ChefPowerShell.bin_dir, "Chef.PowerShell.Wrapper.dll") + bin_dir = ChefPowerShell.bin_dir + @powershell_dll = File.join(bin_dir, "Chef.PowerShell.Wrapper.dll") + + # Windows' default LoadLibrary(Ex) search order used by FFI does NOT + # include the directory of the DLL being loaded -- FFI loads on Windows + # with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, which only searches the hosting + # EXE's directory, System32, and directories registered via + # SetDllDirectory/AddDllDirectory (notably *not* PATH or the current + # directory). Chef.PowerShell.Wrapper.dll depends on native VC++ runtime + # DLLs (vcruntime140.dll, msvcp140.dll, etc.) that live alongside it in + # bin_dir, so that directory must be registered explicitly or those + # dependent DLLs will fail to resolve (error 126) even though the + # wrapper DLL itself loads fine via its absolute path. + Kernel32.SetDefaultDllDirectories(Kernel32::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) + raise LoadError, "Failed to register DLL search directory: #{bin_dir}" unless Kernel32.register_search_directory(bin_dir) + + # Retained alongside AddDllDirectory for defense in depth / older Windows + # compatibility (AddDllDirectory requires Windows 8+/Server 2012+, or + # Windows 7 SP1/Server 2008 R2 SP1 with KB2533623). + Kernel32.SetDllDirectoryA(bin_dir) + exec(script, timeout: timeout) end diff --git a/chef-powershell/lib/chef-powershell/pwsh.rb b/chef-powershell/lib/chef-powershell/pwsh.rb index 5ee5e98f..1b47ffa2 100644 --- a/chef-powershell/lib/chef-powershell/pwsh.rb +++ b/chef-powershell/lib/chef-powershell/pwsh.rb @@ -17,26 +17,6 @@ class ChefPowerShell class Pwsh < ChefPowerShell::PowerShell - module Kernel32 - extend FFI::Library - ffi_lib "kernel32" - attach_function :SetDllDirectoryA, %i{string}, :int - attach_function :SetDefaultDllDirectories, %i{uint32}, :int - attach_function :AddDllDirectory, %i{pointer}, :pointer - - # https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories - LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 - - # AddDllDirectory requires a wide (UTF-16LE), null-terminated string (LPCWSTR). - # Returns true if the directory was registered successfully. - def self.register_search_directory(path) - wide_path = (path + "\0").encode("UTF-16LE") - ptr = FFI::MemoryPointer.new(:uint8, wide_path.bytesize) - ptr.put_bytes(0, wide_path) - !(AddDllDirectory(ptr).address == 0) - end - end - # Run a command under pwsh (powershell core) via FFI # This implementation requires the managed dll, native wrapper and a # published, self contained dotnet core directory tree to exist in the diff --git a/chef-powershell/spec/integration/dll_integration_spec.rb b/chef-powershell/spec/integration/dll_integration_spec.rb index 3906c904..644fe4b0 100644 --- a/chef-powershell/spec/integration/dll_integration_spec.rb +++ b/chef-powershell/spec/integration/dll_integration_spec.rb @@ -14,6 +14,10 @@ # If CHEF_POWERSHELL_BIN is not set, the spec falls back to the gem's own bin/ruby_bin_folder # (useful when running from a fully-installed gem). +require "bundler" +require "open3" +require "tempfile" + # Snapshot CHEF_POWERSHELL_BIN *before* require "chef-powershell", because # powershell_exec.rb unconditionally overwrites it with the gem's own bin path # at module load time. @@ -77,9 +81,9 @@ def raw_pwsh(script, timeout: -1) # nested runtime dir alongside the DLL -- register both explicitly. Do not # rely on System32 already having the VC++ redistributable. core_dir = File.dirname(NET10_DLL) - ChefPowerShell::Pwsh::Kernel32.SetDefaultDllDirectories(ChefPowerShell::Pwsh::Kernel32::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) - [DLL_BIN_DIR, core_dir].each { |dir| ChefPowerShell::Pwsh::Kernel32.register_search_directory(dir) } - ChefPowerShell::Pwsh::Kernel32.SetDllDirectoryA(core_dir) + ChefPowerShell::Kernel32.SetDefaultDllDirectories(ChefPowerShell::Kernel32::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) + [DLL_BIN_DIR, core_dir].each { |dir| ChefPowerShell::Kernel32.register_search_directory(dir) } + ChefPowerShell::Kernel32.SetDllDirectoryA(core_dir) ps = ChefPowerShell::Pwsh.allocate ps.instance_variable_set(:@powershell_dll, NET10_DLL) @@ -361,3 +365,95 @@ def raw_pwsh(script, timeout: -1) expect(ENV["DOTNET_ROOT"]).to eq(original) end end + +# --------------------------------------------------------------------------- +# Regression: PowerShell#initialize must register its own DLL search directory +# --------------------------------------------------------------------------- +# +# Background: AddDllDirectory/SetDefaultDllDirectories mutate GLOBAL, +# PROCESS-WIDE Windows search-path state that is never unregistered. Because +# this entire spec file runs in a single rspec process, a missing directory +# registration in one interpreter's init path can be silently masked by +# another interpreter's init path having already registered the very same +# directory earlier in the run (raw_powershell/raw_pwsh above share DLL_BIN_DIR). +# +# This previously hid a real bug: PowerShell#initialize (the Windows +# PowerShell / .NET Framework 4.8.1 path) never registered `bin_dir` as a DLL +# search directory -- only Pwsh#exec (.NET 10 / PowerShell Core path) did. +# Production usage (e.g. Chef-18) only ever constructs a `:powershell` +# interpreter in a single-purpose process, so no such masking occurs there -- +# it failed with error 126 because the native CRT dependencies +# (vcruntime140.dll, msvcp140.dll, etc.) could not be resolved. +# +# NOTE: on a dev/CI machine that already has the VC++ redistributable +# installed under System32 (part of the classic DLL search order regardless +# of AddDllDirectory registration), removing the registration call will NOT +# reproduce the failure -- System32 quietly satisfies the dependency. The +# behavioral spec below ("registers bin_dir...") is what actually catches a +# regression deterministically, on any machine. This subprocess test instead +# guards against the *masking* mechanism itself: it proves the real +# constructor (not `.allocate`) succeeds end-to-end in a fresh, single-purpose +# process with no possibility of state bleed from other examples in this +# suite -- the same condition production code runs under. +RSpec.describe "PowerShell#initialize DLL directory registration (process isolation)", :windows_only do + before(:all) do + skip "NET481 DLL not found at #{NET481_DLL}" unless File.exist?(NET481_DLL) + end + + it "constructs ChefPowerShell::PowerShell and resolves native DLL dependencies in an otherwise-empty process" do + gem_root = File.expand_path("../..", __dir__) + + script = <<~'RUBY' + require "chef-powershell" + result = ChefPowerShell::PowerShell.new("$PSVersionTable", timeout: -1) + raise "errors: #{result.errors}" unless result.errors.empty? + unless result.result["PSEdition"] == "Desktop" + raise "unexpected PSEdition: #{result.result["PSEdition"].inspect}" + end + + puts "OK" + RUBY + + env = { "CHEF_POWERSHELL_BIN" => DLL_BIN_DIR } + + # Write the script to a real file rather than passing it via `ruby -e`. + # On Windows, `bundle` resolves to a .bat shim, so the OS re-invokes the + # child through cmd.exe -- which treats embedded newlines in a command-line + # argument as command separators and silently truncates a multi-line -e + # script at the first newline. + stdout, stderr, status = Tempfile.create(["dll_isolation_check", ".rb"]) do |file| + file.write(script) + file.close + + Bundler.with_unbundled_env do + Open3.capture3(env, "bundle", "exec", "ruby", file.path, chdir: gem_root) + end + end + + expect(status.success?).to be(true), + "subprocess failed (exit #{status.exitstatus}):\nSTDOUT:\n#{stdout}\nSTDERR:\n#{stderr}" + expect(stdout).to include("OK") + end + + # Deterministic regression coverage: assert the actual Kernel32 calls happen, + # rather than relying on native DLL resolution failing -- which depends on + # whether the VC++ redistributable happens to already be installed on the + # machine running the suite (see NOTE above). + it "registers bin_dir as a DLL search directory before executing" do + orig_bin = ENV["CHEF_POWERSHELL_BIN"] + ENV["CHEF_POWERSHELL_BIN"] = DLL_BIN_DIR + + allow(ChefPowerShell::Kernel32).to receive(:SetDefaultDllDirectories).and_call_original + allow(ChefPowerShell::Kernel32).to receive(:register_search_directory).and_call_original + allow(ChefPowerShell::Kernel32).to receive(:SetDllDirectoryA).and_call_original + + ChefPowerShell::PowerShell.new("$PSVersionTable", timeout: -1) + + expect(ChefPowerShell::Kernel32).to have_received(:SetDefaultDllDirectories) + .with(ChefPowerShell::Kernel32::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) + expect(ChefPowerShell::Kernel32).to have_received(:register_search_directory).with(DLL_BIN_DIR) + expect(ChefPowerShell::Kernel32).to have_received(:SetDllDirectoryA).with(DLL_BIN_DIR) + ensure + ENV["CHEF_POWERSHELL_BIN"] = orig_bin + end +end From 7885c0297f7b50856f04bdb535b4b57b6e9523f8 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Fri, 28 Aug 2026 14:49:19 -0700 Subject: [PATCH 02/14] Debugging a chef-18 build failure Signed-off-by: John McCrae --- habitat/plan.ps1 | 146 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 27 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index e19f1132..efaf200c 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -21,43 +21,135 @@ function Invoke-SetupEnvironment { } function Invoke-Build { - Copy-Item $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname -recurse -force -Exclude ".vs" - nuget restore $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell/packages.config -PackagesDirectory $HAB_CACHE_SRC_PATH/$pkg_dirname/packages -Source "https://www.nuget.org/api/v2" - MSBuild $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper/Chef.Powershell.Wrapper.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 - if($LASTEXITCODE -ne 0) { - Write-Error "dotnet build failed!" - } + Copy-Item $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname -Recurse -Force -Exclude ".vs" - $env:DOTNET_ROOT = "$(Get-HabPackagePath dotnet-10-sdk)\bin" + nuget restore ` + "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell/packages.config" ` + -PackagesDirectory "$HAB_CACHE_SRC_PATH/$pkg_dirname/packages" ` + -Source "https://www.nuget.org/api/v2" - # Step 1: Build the managed .NET 10 core library (CoreCLR). Must use dotnet — VC++ MSBuild - # tasks are .NET Framework-only and cannot build .csproj in the same invocation as .vcxproj. - & "$env:DOTNET_ROOT\dotnet.exe" build $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Core/Chef.Powershell.Core.csproj --configuration Release /p:Platform=x64 - if($LASTEXITCODE -ne 0) { - Write-Error "dotnet core build failed!" - } + $env:DOTNET_ROOT = "$(Get-HabPackagePath dotnet-10-sdk)\bin" - # Step 2: Build the C++/CLI wrapper with the 64-bit MSBuild.exe (.NET Framework). - # Must use amd64\MSBuild.exe — hostfxr.dll is 64-bit and cannot be loaded by a 32-bit process. - $vsBuildToolsPath = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" - $vcTargetsPath = "$vsBuildToolsPath\MSBuild\Microsoft\VC\v180\" - $msbuildExe = "$vsBuildToolsPath\MSBuild\Current\Bin\amd64\MSBuild.exe" + $vsRoot = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" + $msbuildExe = "$vsRoot\MSBuild\Current\Bin\amd64\MSBuild.exe" + $vcTargetsPath = "$vsRoot\MSBuild\Microsoft\VC\v180" + $resolverDir = "$vsRoot\Common7\IDE\CommonExtensions\Microsoft\NuGet" + $resolver = "$resolverDir\Microsoft.Build.NuGetSdkResolver.dll" - # Locate the .NET 10 reference pack and host pack from the Hab dotnet-10-sdk package. - # These are passed to MSBuild so cl.exe and the linker can find the right binaries without - # relying on NuGet restore or workload resolution (both disabled via DisableImplicitFrameworkReferences). $refPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Ref" - $refVersion = (Get-ChildItem $refPackRoot | Sort-Object Name -Descending | Select-Object -First 1).Name + $refVersion = ( + Get-ChildItem $refPackRoot | + Sort-Object Name -Descending | + Select-Object -First 1 + ).Name $refPackPath = "$refPackRoot\$refVersion\ref\net10.0" - $env:LIBPATH = "$refPackPath;$env:LIBPATH" $hostPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Host.win-x64" - $hostPackVersion = (Get-ChildItem $hostPackRoot | Sort-Object Name -Descending | Select-Object -First 1).Name + $hostPackVersion = ( + Get-ChildItem $hostPackRoot | + Sort-Object Name -Descending | + Select-Object -First 1 + ).Name $ijwHostSourcePath = "$hostPackRoot\$hostPackVersion\runtimes\win-x64\native\ijwhost.dll" - & $msbuildExe $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 /p:BuildProjectReferences=false /p:VCTargetsPath="$vcTargetsPath" /p:DotNetSdkRoot="$env:DOTNET_ROOT" /p:DotNetCoreRefPackPath="$refPackPath" /p:IjwHostSourcePath="$ijwHostSourcePath" /p:DisableImplicitFrameworkReferences=true /p:GenerateRuntimeConfigurationFiles=false /nodeReuse:false - if($LASTEXITCODE -ne 0) { - Write-Error "dotnet core build failed!" + + Write-Output "*********************************************************************" + Write-Output "chef-powershell build diagnostics" + Write-Output "Visual Build Tools root: $vsRoot" + Write-Output "MSBuild: $msbuildExe" + Write-Output "Resolver directory: $resolverDir" + Write-Output "Resolver: $resolver" + Write-Output "Resolver exists: $(Test-Path $resolver)" + Write-Output "DOTNET_ROOT: $env:DOTNET_ROOT" + Write-Output "Reference pack: $refPackPath" + Write-Output "Reference pack exists: $(Test-Path $refPackPath)" + Write-Output "Host pack: $hostPackRoot\$hostPackVersion" + Write-Output "ijwhost.dll: $ijwHostSourcePath" + Write-Output "ijwhost.dll exists: $(Test-Path $ijwHostSourcePath)" + Write-Output "MSBuildSDKsPath: $env:MSBuildSDKsPath" + Write-Output "*********************************************************************" + + if (-not (Test-Path $msbuildExe)) { + throw "MSBuild was not found: $msbuildExe" + } + + if (-not (Test-Path $resolver)) { + throw "NuGet SDK resolver was not found: $resolver" + } + + if (-not (Test-Path $refPackPath)) { + throw ".NET 10 reference pack was not found: $refPackPath" + } + + if (-not (Test-Path $ijwHostSourcePath)) { + throw "ijwhost.dll was not found: $ijwHostSourcePath" + } + + Write-Output "NuGet resolver directory contents:" + Get-ChildItem $resolverDir | + Select-Object Name, Length, FullName + + try { + [System.Reflection.Assembly]::LoadFrom($resolver) | Out-Null + Write-Output "Resolver loaded successfully" } + catch { + Write-Error "Resolver failed to load" + Write-Error $_.Exception.ToString() + throw + } + + Write-Output "Building .NET Framework PowerShell wrapper" + + & $msbuildExe ` + "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper/Chef.Powershell.Wrapper.vcxproj" ` + /t:Build ` + /p:Configuration=Release ` + /p:Platform=x64 ` + /nodeReuse:false ` + /verbosity:minimal + + if ($LASTEXITCODE -ne 0) { + throw "Chef.PowerShell.Wrapper build failed with exit code $LASTEXITCODE" + } + + Write-Output "Building .NET 10 managed PowerShell core" + + & "$env:DOTNET_ROOT\dotnet.exe" ` + build ` + "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Core/Chef.Powershell.Core.csproj" ` + --configuration Release ` + /p:Platform=x64 + + if ($LASTEXITCODE -ne 0) { + throw "Chef.Powershell.Core build failed with exit code $LASTEXITCODE" + } + + $env:LIBPATH = "$refPackPath;$env:LIBPATH" + + Write-Output "Building .NET 10 C++/CLI wrapper" + Write-Output "MSBuild command: $msbuildExe" + Write-Output "MSBuild project: $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" + + & $msbuildExe ` + "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` + /t:Build ` + /p:Configuration=Release ` + /p:Platform=x64 ` + /p:BuildProjectReferences=false ` + /p:VCTargetsPath="$vcTargetsPath" ` + /p:DotNetSdkRoot="$env:DOTNET_ROOT" ` + /p:DotNetCoreRefPackPath="$refPackPath" ` + /p:IjwHostSourcePath="$ijwHostSourcePath" ` + /p:DisableImplicitFrameworkReferences=true ` + /p:GenerateRuntimeConfigurationFiles=false ` + /nodeReuse:false ` + /verbosity:diagnostic + + if ($LASTEXITCODE -ne 0) { + throw "Chef.PowerShell.Wrapper.Core build failed with exit code $LASTEXITCODE" + } + + Write-Output "chef-powershell native build completed successfully" } function Invoke-Install { From 2e9b78fcff126cce73a94a461b9dc89d3903b147 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Fri, 28 Aug 2026 15:22:33 -0700 Subject: [PATCH 03/14] Hab plan is updated and working Signed-off-by: John McCrae --- habitat/plan.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index efaf200c..2c5d201e 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -32,7 +32,7 @@ function Invoke-Build { $vsRoot = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" $msbuildExe = "$vsRoot\MSBuild\Current\Bin\amd64\MSBuild.exe" - $vcTargetsPath = "$vsRoot\MSBuild\Microsoft\VC\v180" + $vcTargetsPath = "$vsRoot\MSBuild\Microsoft\VC\v180\" $resolverDir = "$vsRoot\Common7\IDE\CommonExtensions\Microsoft\NuGet" $resolver = "$resolverDir\Microsoft.Build.NuGetSdkResolver.dll" From 088204a38e6ff636f1db090407ccc3f26b70fc4d Mon Sep 17 00:00:00 2001 From: John McCrae Date: Fri, 28 Aug 2026 15:58:39 -0700 Subject: [PATCH 04/14] tweaking the hab plan some more Signed-off-by: John McCrae --- habitat/plan.ps1 | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 2c5d201e..fff8d1e2 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -32,7 +32,6 @@ function Invoke-Build { $vsRoot = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" $msbuildExe = "$vsRoot\MSBuild\Current\Bin\amd64\MSBuild.exe" - $vcTargetsPath = "$vsRoot\MSBuild\Microsoft\VC\v180\" $resolverDir = "$vsRoot\Common7\IDE\CommonExtensions\Microsoft\NuGet" $resolver = "$resolverDir\Microsoft.Build.NuGetSdkResolver.dll" @@ -130,20 +129,19 @@ function Invoke-Build { Write-Output "MSBuild command: $msbuildExe" Write-Output "MSBuild project: $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" - & $msbuildExe ` - "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` - /t:Build ` - /p:Configuration=Release ` - /p:Platform=x64 ` - /p:BuildProjectReferences=false ` - /p:VCTargetsPath="$vcTargetsPath" ` - /p:DotNetSdkRoot="$env:DOTNET_ROOT" ` - /p:DotNetCoreRefPackPath="$refPackPath" ` - /p:IjwHostSourcePath="$ijwHostSourcePath" ` - /p:DisableImplicitFrameworkReferences=true ` - /p:GenerateRuntimeConfigurationFiles=false ` - /nodeReuse:false ` - /verbosity:diagnostic +& $msbuildExe ` + "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` + /t:Build ` + /p:Configuration=Release ` + /p:Platform=x64 ` + /p:BuildProjectReferences=false ` + /p:DotNetSdkRoot="$env:DOTNET_ROOT" ` + /p:DotNetCoreRefPackPath="$refPackPath" ` + /p:IjwHostSourcePath="$ijwHostSourcePath" ` + /p:DisableImplicitFrameworkReferences=true ` + /p:GenerateRuntimeConfigurationFiles=false ` + /nodeReuse:false ` + /verbosity:diagnostic if ($LASTEXITCODE -ne 0) { throw "Chef.PowerShell.Wrapper.Core build failed with exit code $LASTEXITCODE" From 2b4633c8e96f1a0c914ec32a64ef50c85879f9d0 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Fri, 28 Aug 2026 16:22:40 -0700 Subject: [PATCH 05/14] more tweaking for chef-18 Signed-off-by: John McCrae --- habitat/plan.ps1 | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index fff8d1e2..e21de5f5 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -21,7 +21,11 @@ function Invoke-SetupEnvironment { } function Invoke-Build { - Copy-Item $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname -Recurse -Force -Exclude ".vs" + Copy-Item $PLAN_CONTEXT/../* ` + $HAB_CACHE_SRC_PATH/$pkg_dirname ` + -Recurse ` + -Force ` + -Exclude ".vs" nuget restore ` "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell/packages.config" ` @@ -61,7 +65,6 @@ function Invoke-Build { Write-Output "DOTNET_ROOT: $env:DOTNET_ROOT" Write-Output "Reference pack: $refPackPath" Write-Output "Reference pack exists: $(Test-Path $refPackPath)" - Write-Output "Host pack: $hostPackRoot\$hostPackVersion" Write-Output "ijwhost.dll: $ijwHostSourcePath" Write-Output "ijwhost.dll exists: $(Test-Path $ijwHostSourcePath)" Write-Output "MSBuildSDKsPath: $env:MSBuildSDKsPath" @@ -97,6 +100,9 @@ function Invoke-Build { throw } + # Help MSBuild resolve NuGet resolver dependencies. + $env:PATH = "$resolverDir;$env:PATH" + Write-Output "Building .NET Framework PowerShell wrapper" & $msbuildExe ` @@ -126,22 +132,22 @@ function Invoke-Build { $env:LIBPATH = "$refPackPath;$env:LIBPATH" Write-Output "Building .NET 10 C++/CLI wrapper" - Write-Output "MSBuild command: $msbuildExe" - Write-Output "MSBuild project: $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" - -& $msbuildExe ` - "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` - /t:Build ` - /p:Configuration=Release ` - /p:Platform=x64 ` - /p:BuildProjectReferences=false ` - /p:DotNetSdkRoot="$env:DOTNET_ROOT" ` - /p:DotNetCoreRefPackPath="$refPackPath" ` - /p:IjwHostSourcePath="$ijwHostSourcePath" ` - /p:DisableImplicitFrameworkReferences=true ` - /p:GenerateRuntimeConfigurationFiles=false ` - /nodeReuse:false ` - /verbosity:diagnostic + Write-Output "MSBuild: $msbuildExe" + Write-Output "Project: $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" + + & $msbuildExe ` + "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` + /t:Build ` + /p:Configuration=Release ` + /p:Platform=x64 ` + /p:BuildProjectReferences=false ` + /p:DotNetSdkRoot="$env:DOTNET_ROOT" ` + /p:DotNetCoreRefPackPath="$refPackPath" ` + /p:IjwHostSourcePath="$ijwHostSourcePath" ` + /p:DisableImplicitFrameworkReferences=true ` + /p:GenerateRuntimeConfigurationFiles=false ` + /nodeReuse:false ` + /verbosity:diagnostic if ($LASTEXITCODE -ne 0) { throw "Chef.PowerShell.Wrapper.Core build failed with exit code $LASTEXITCODE" From 9a080abe718d25d4b3af69b309866d02f6484951 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Sat, 29 Aug 2026 10:10:24 -0700 Subject: [PATCH 06/14] Finally working with all the sdk paths updated Signed-off-by: John McCrae --- habitat/plan.ps1 | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index e21de5f5..0ed656f5 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -135,6 +135,68 @@ function Invoke-Build { Write-Output "MSBuild: $msbuildExe" Write-Output "Project: $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" + # Step 2: Build the C++/CLI wrapper with the 64-bit MSBuild.exe (.NET Framework). + # MSBuildSDKsPath must point at the installed .NET 10 SDK. + $vsBuildToolsPath = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" + $msbuildExe = "$vsBuildToolsPath\MSBuild\Current\Bin\amd64\MSBuild.exe" + + $sdkRoot = Join-Path $env:DOTNET_ROOT "Sdk" + $sdkVersion = ( + Get-ChildItem $sdkRoot -Directory | + Sort-Object Name -Descending | + Select-Object -First 1 + ).Name + + $env:MSBuildSDKsPath = Join-Path $sdkRoot "$sdkVersion\Sdks" + + # Locate the .NET 10 reference pack and host pack. + $refPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Ref" + $refVersion = ( + Get-ChildItem $refPackRoot -Directory | + Sort-Object Name -Descending | + Select-Object -First 1 + ).Name + $refPackPath = "$refPackRoot\$refVersion\ref\net10.0" + + $hostPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Host.win-x64" + $hostPackVersion = ( + Get-ChildItem $hostPackRoot -Directory | + Sort-Object Name -Descending | + Select-Object -First 1 + ).Name + $ijwHostSourcePath = "$hostPackRoot\$hostPackVersion\runtimes\win-x64\native\ijwhost.dll" + + Write-Output "Building .NET 10 C++/CLI wrapper" + Write-Output "MSBuild: $msbuildExe" + Write-Output "SDK root: $sdkRoot" + Write-Output "SDK version: $sdkVersion" + Write-Output "MSBuildSDKsPath: $env:MSBuildSDKsPath" + Write-Output "Reference pack: $refPackPath" + Write-Output "ijwhost.dll: $ijwHostSourcePath" + Write-Output "Microsoft.NET.Sdk exists: $(Test-Path (Join-Path $env:MSBuildSDKsPath 'Microsoft.NET.Sdk\Sdk'))" + + if (-not (Test-Path $msbuildExe)) { + throw "MSBuild was not found: $msbuildExe" + } + + if (-not (Test-Path $env:MSBuildSDKsPath)) { + throw "MSBuild SDK path was not found: $env:MSBuildSDKsPath" + } + + if (-not (Test-Path (Join-Path $env:MSBuildSDKsPath "Microsoft.NET.Sdk\Sdk"))) { + throw "Microsoft.NET.Sdk was not found under: $env:MSBuildSDKsPath" + } + + if (-not (Test-Path $refPackPath)) { + throw ".NET 10 reference pack was not found: $refPackPath" + } + + if (-not (Test-Path $ijwHostSourcePath)) { + throw "ijwHost.dll was not found: $ijwHostSourcePath" + } + + $env:LIBPATH = "$refPackPath;$env:LIBPATH" + & $msbuildExe ` "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` /t:Build ` @@ -146,6 +208,7 @@ function Invoke-Build { /p:IjwHostSourcePath="$ijwHostSourcePath" ` /p:DisableImplicitFrameworkReferences=true ` /p:GenerateRuntimeConfigurationFiles=false ` + /p:EnableWorkloadResolver=false ` /nodeReuse:false ` /verbosity:diagnostic From ae28a4ef1b775619dab12c5bc5c8cc954e7e3a96 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Sun, 30 Aug 2026 08:50:33 -0700 Subject: [PATCH 07/14] Something weird with pathing Signed-off-by: John McCrae --- habitat/plan.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 0ed656f5..d69d7813 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -21,6 +21,13 @@ function Invoke-SetupEnvironment { } function Invoke-Build { + + Write-Output "*********************************************************************" + Write-Output "MSBuildSDKsPath at Invoke-Build entry: $env:MSBuildSDKsPath" + Write-Output "*********************************************************************" + + pause + Copy-Item $PLAN_CONTEXT/../* ` $HAB_CACHE_SRC_PATH/$pkg_dirname ` -Recurse ` From 62099c0ed2aa997f768952255c9f447c318f5501 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Sun, 30 Aug 2026 08:56:27 -0700 Subject: [PATCH 08/14] removed the pause command Signed-off-by: John McCrae --- habitat/plan.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index d69d7813..379783b3 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -26,8 +26,6 @@ function Invoke-Build { Write-Output "MSBuildSDKsPath at Invoke-Build entry: $env:MSBuildSDKsPath" Write-Output "*********************************************************************" - pause - Copy-Item $PLAN_CONTEXT/../* ` $HAB_CACHE_SRC_PATH/$pkg_dirname ` -Recurse ` From f49fda30b8b7a670c4065a561f17dc1a043ed9a7 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Sun, 30 Aug 2026 09:02:36 -0700 Subject: [PATCH 09/14] More commenting on pathing Signed-off-by: John McCrae --- habitat/plan.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 379783b3..39e6a454 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -202,6 +202,13 @@ function Invoke-Build { $env:LIBPATH = "$refPackPath;$env:LIBPATH" + $env:MSBuildSDKsPath = Join-Path $sdkRoot "$sdkVersion\Sdks" + + Write-Output "*********************************************************************" + Write-Output "MSBuildSDKsPath immediately before second MSBuild call: $env:MSBuildSDKsPath" + Write-Output "Microsoft.NET.Sdk exists at that path: $(Test-Path (Join-Path $env:MSBuildSDKsPath 'Microsoft.NET.Sdk\Sdk'))" + Write-Output "*********************************************************************" + & $msbuildExe ` "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` /t:Build ` From 9f760a34576feaa7ad63aa76f9ee572594eee779 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Sun, 30 Aug 2026 21:57:54 +0000 Subject: [PATCH 10/14] reveritng some things here Signed-off-by: John McCrae --- habitat/plan.ps1 | 212 +++++------------------------------------------ 1 file changed, 23 insertions(+), 189 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 39e6a454..08676cdc 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -21,214 +21,48 @@ function Invoke-SetupEnvironment { } function Invoke-Build { - - Write-Output "*********************************************************************" - Write-Output "MSBuildSDKsPath at Invoke-Build entry: $env:MSBuildSDKsPath" - Write-Output "*********************************************************************" - - Copy-Item $PLAN_CONTEXT/../* ` - $HAB_CACHE_SRC_PATH/$pkg_dirname ` - -Recurse ` - -Force ` - -Exclude ".vs" + Copy-Item $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname -Recurse -Force -Exclude ".vs" nuget restore ` "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell/packages.config" ` -PackagesDirectory "$HAB_CACHE_SRC_PATH/$pkg_dirname/packages" ` -Source "https://www.nuget.org/api/v2" - $env:DOTNET_ROOT = "$(Get-HabPackagePath dotnet-10-sdk)\bin" - - $vsRoot = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" - $msbuildExe = "$vsRoot\MSBuild\Current\Bin\amd64\MSBuild.exe" - $resolverDir = "$vsRoot\Common7\IDE\CommonExtensions\Microsoft\NuGet" - $resolver = "$resolverDir\Microsoft.Build.NuGetSdkResolver.dll" - - $refPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Ref" - $refVersion = ( - Get-ChildItem $refPackRoot | - Sort-Object Name -Descending | - Select-Object -First 1 - ).Name - $refPackPath = "$refPackRoot\$refVersion\ref\net10.0" - - $hostPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Host.win-x64" - $hostPackVersion = ( - Get-ChildItem $hostPackRoot | - Sort-Object Name -Descending | - Select-Object -First 1 - ).Name - $ijwHostSourcePath = "$hostPackRoot\$hostPackVersion\runtimes\win-x64\native\ijwhost.dll" - - Write-Output "*********************************************************************" - Write-Output "chef-powershell build diagnostics" - Write-Output "Visual Build Tools root: $vsRoot" - Write-Output "MSBuild: $msbuildExe" - Write-Output "Resolver directory: $resolverDir" - Write-Output "Resolver: $resolver" - Write-Output "Resolver exists: $(Test-Path $resolver)" - Write-Output "DOTNET_ROOT: $env:DOTNET_ROOT" - Write-Output "Reference pack: $refPackPath" - Write-Output "Reference pack exists: $(Test-Path $refPackPath)" - Write-Output "ijwhost.dll: $ijwHostSourcePath" - Write-Output "ijwhost.dll exists: $(Test-Path $ijwHostSourcePath)" - Write-Output "MSBuildSDKsPath: $env:MSBuildSDKsPath" - Write-Output "*********************************************************************" - - if (-not (Test-Path $msbuildExe)) { - throw "MSBuild was not found: $msbuildExe" - } - - if (-not (Test-Path $resolver)) { - throw "NuGet SDK resolver was not found: $resolver" - } - - if (-not (Test-Path $refPackPath)) { - throw ".NET 10 reference pack was not found: $refPackPath" - } - - if (-not (Test-Path $ijwHostSourcePath)) { - throw "ijwhost.dll was not found: $ijwHostSourcePath" - } - - Write-Output "NuGet resolver directory contents:" - Get-ChildItem $resolverDir | - Select-Object Name, Length, FullName - - try { - [System.Reflection.Assembly]::LoadFrom($resolver) | Out-Null - Write-Output "Resolver loaded successfully" - } - catch { - Write-Error "Resolver failed to load" - Write-Error $_.Exception.ToString() - throw - } - - # Help MSBuild resolve NuGet resolver dependencies. - $env:PATH = "$resolverDir;$env:PATH" - - Write-Output "Building .NET Framework PowerShell wrapper" - - & $msbuildExe ` - "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper/Chef.Powershell.Wrapper.vcxproj" ` - /t:Build ` - /p:Configuration=Release ` - /p:Platform=x64 ` - /nodeReuse:false ` - /verbosity:minimal + $vsBuildToolsPath = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" + $vcTargetsPath = "$vsBuildToolsPath\MSBuild\Microsoft\VC\v180\" + $msbuildExe = "$vsBuildToolsPath\MSBuild\Current\Bin\amd64\MSBuild.exe" - if ($LASTEXITCODE -ne 0) { - throw "Chef.PowerShell.Wrapper build failed with exit code $LASTEXITCODE" + & $msbuildExe $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper/Chef.Powershell.Wrapper.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 + if($LASTEXITCODE -ne 0) { + Write-Error "dotnet build failed!" } - Write-Output "Building .NET 10 managed PowerShell core" - - & "$env:DOTNET_ROOT\dotnet.exe" ` - build ` - "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Core/Chef.Powershell.Core.csproj" ` - --configuration Release ` - /p:Platform=x64 + $env:DOTNET_ROOT = "$(Get-HabPackagePath dotnet-10-sdk)\bin" - if ($LASTEXITCODE -ne 0) { - throw "Chef.Powershell.Core build failed with exit code $LASTEXITCODE" + # Step 1: Build the managed .NET 10 core library (CoreCLR). Must use dotnet — VC++ MSBuild + # tasks are .NET Framework-only and cannot build .csproj in the same invocation as .vcxproj. + & "$env:DOTNET_ROOT\dotnet.exe" build $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Core/Chef.Powershell.Core.csproj --configuration Release /p:Platform=x64 + if($LASTEXITCODE -ne 0) { + Write-Error "dotnet core build failed!" } - $env:LIBPATH = "$refPackPath;$env:LIBPATH" - - Write-Output "Building .NET 10 C++/CLI wrapper" - Write-Output "MSBuild: $msbuildExe" - Write-Output "Project: $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" - # Step 2: Build the C++/CLI wrapper with the 64-bit MSBuild.exe (.NET Framework). - # MSBuildSDKsPath must point at the installed .NET 10 SDK. - $vsBuildToolsPath = "$(Get-HabPackagePath visual-build-tools-2026)\Contents" - $msbuildExe = "$vsBuildToolsPath\MSBuild\Current\Bin\amd64\MSBuild.exe" - - $sdkRoot = Join-Path $env:DOTNET_ROOT "Sdk" - $sdkVersion = ( - Get-ChildItem $sdkRoot -Directory | - Sort-Object Name -Descending | - Select-Object -First 1 - ).Name - - $env:MSBuildSDKsPath = Join-Path $sdkRoot "$sdkVersion\Sdks" - - # Locate the .NET 10 reference pack and host pack. + # Must use amd64\MSBuild.exe — hostfxr.dll is 64-bit and cannot be loaded by a 32-bit process. + # Locate the .NET 10 reference pack and host pack from the Hab dotnet-10-sdk package. + # These are passed to MSBuild so cl.exe and the linker can find the right binaries without + # relying on NuGet restore or workload resolution (both disabled via DisableImplicitFrameworkReferences). $refPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Ref" - $refVersion = ( - Get-ChildItem $refPackRoot -Directory | - Sort-Object Name -Descending | - Select-Object -First 1 - ).Name + $refVersion = (Get-ChildItem $refPackRoot | Sort-Object Name -Descending | Select-Object -First 1).Name $refPackPath = "$refPackRoot\$refVersion\ref\net10.0" + $env:LIBPATH = "$refPackPath;$env:LIBPATH" $hostPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Host.win-x64" - $hostPackVersion = ( - Get-ChildItem $hostPackRoot -Directory | - Sort-Object Name -Descending | - Select-Object -First 1 - ).Name + $hostPackVersion = (Get-ChildItem $hostPackRoot | Sort-Object Name -Descending | Select-Object -First 1).Name $ijwHostSourcePath = "$hostPackRoot\$hostPackVersion\runtimes\win-x64\native\ijwhost.dll" - - Write-Output "Building .NET 10 C++/CLI wrapper" - Write-Output "MSBuild: $msbuildExe" - Write-Output "SDK root: $sdkRoot" - Write-Output "SDK version: $sdkVersion" - Write-Output "MSBuildSDKsPath: $env:MSBuildSDKsPath" - Write-Output "Reference pack: $refPackPath" - Write-Output "ijwhost.dll: $ijwHostSourcePath" - Write-Output "Microsoft.NET.Sdk exists: $(Test-Path (Join-Path $env:MSBuildSDKsPath 'Microsoft.NET.Sdk\Sdk'))" - - if (-not (Test-Path $msbuildExe)) { - throw "MSBuild was not found: $msbuildExe" - } - - if (-not (Test-Path $env:MSBuildSDKsPath)) { - throw "MSBuild SDK path was not found: $env:MSBuildSDKsPath" + & $msbuildExe $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 /p:BuildProjectReferences=false /p:VCTargetsPath="$vcTargetsPath" /p:DotNetSdkRoot="$env:DOTNET_ROOT" /p:DotNetCoreRefPackPath="$refPackPath" /p:IjwHostSourcePath="$ijwHostSourcePath" /p:DisableImplicitFrameworkReferences=true /p:GenerateRuntimeConfigurationFiles=false /nodeReuse:false + if($LASTEXITCODE -ne 0) { + Write-Error "dotnet core build failed!" } - - if (-not (Test-Path (Join-Path $env:MSBuildSDKsPath "Microsoft.NET.Sdk\Sdk"))) { - throw "Microsoft.NET.Sdk was not found under: $env:MSBuildSDKsPath" - } - - if (-not (Test-Path $refPackPath)) { - throw ".NET 10 reference pack was not found: $refPackPath" - } - - if (-not (Test-Path $ijwHostSourcePath)) { - throw "ijwHost.dll was not found: $ijwHostSourcePath" - } - - $env:LIBPATH = "$refPackPath;$env:LIBPATH" - - $env:MSBuildSDKsPath = Join-Path $sdkRoot "$sdkVersion\Sdks" - - Write-Output "*********************************************************************" - Write-Output "MSBuildSDKsPath immediately before second MSBuild call: $env:MSBuildSDKsPath" - Write-Output "Microsoft.NET.Sdk exists at that path: $(Test-Path (Join-Path $env:MSBuildSDKsPath 'Microsoft.NET.Sdk\Sdk'))" - Write-Output "*********************************************************************" - - & $msbuildExe ` - "$HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj" ` - /t:Build ` - /p:Configuration=Release ` - /p:Platform=x64 ` - /p:BuildProjectReferences=false ` - /p:DotNetSdkRoot="$env:DOTNET_ROOT" ` - /p:DotNetCoreRefPackPath="$refPackPath" ` - /p:IjwHostSourcePath="$ijwHostSourcePath" ` - /p:DisableImplicitFrameworkReferences=true ` - /p:GenerateRuntimeConfigurationFiles=false ` - /p:EnableWorkloadResolver=false ` - /nodeReuse:false ` - /verbosity:diagnostic - - if ($LASTEXITCODE -ne 0) { - throw "Chef.PowerShell.Wrapper.Core build failed with exit code $LASTEXITCODE" - } - - Write-Output "chef-powershell native build completed successfully" } function Invoke-Install { From 3f289818bc3f189d2ccc33fd4695e1e18b89293a Mon Sep 17 00:00:00 2001 From: John McCrae Date: Mon, 31 Aug 2026 12:10:38 +0000 Subject: [PATCH 11/14] removing comments Signed-off-by: John McCrae --- habitat/plan.ps1 | 7 ------- 1 file changed, 7 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 08676cdc..a979c33b 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -39,18 +39,11 @@ function Invoke-Build { $env:DOTNET_ROOT = "$(Get-HabPackagePath dotnet-10-sdk)\bin" - # Step 1: Build the managed .NET 10 core library (CoreCLR). Must use dotnet — VC++ MSBuild - # tasks are .NET Framework-only and cannot build .csproj in the same invocation as .vcxproj. & "$env:DOTNET_ROOT\dotnet.exe" build $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Core/Chef.Powershell.Core.csproj --configuration Release /p:Platform=x64 if($LASTEXITCODE -ne 0) { Write-Error "dotnet core build failed!" } - # Step 2: Build the C++/CLI wrapper with the 64-bit MSBuild.exe (.NET Framework). - # Must use amd64\MSBuild.exe — hostfxr.dll is 64-bit and cannot be loaded by a 32-bit process. - # Locate the .NET 10 reference pack and host pack from the Hab dotnet-10-sdk package. - # These are passed to MSBuild so cl.exe and the linker can find the right binaries without - # relying on NuGet restore or workload resolution (both disabled via DisableImplicitFrameworkReferences). $refPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Ref" $refVersion = (Get-ChildItem $refPackRoot | Sort-Object Name -Descending | Select-Object -First 1).Name $refPackPath = "$refPackRoot\$refVersion\ref\net10.0" From 1ce93f1b62859aede3de14233a64358e2edbd1c1 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Mon, 31 Aug 2026 15:23:10 +0000 Subject: [PATCH 12/14] and env vars for testing purposes Signed-off-by: John McCrae --- habitat/plan.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index a979c33b..47de060c 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -12,6 +12,10 @@ $pkg_build_deps=@( ) $pkg_bin_dirs=@("bin") +$env:HAB_BLDR_CHANNEL = "base-2025" +$env:HAB_ORIGIN = "chef" +$env:HAB_REFRESH_CHANNEL = "base-2025" + function Invoke-SetupEnvironment { Push-RuntimeEnv -IsPath "RUBY_DLL_PATH" "$pkg_prefix/bin" Set-RuntimeEnv -IsPath "CHEF_POWERSHELL_BIN" "$pkg_prefix/bin" From d02a39325fad89ff6bacd7f0aabafe0ace772510 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Mon, 31 Aug 2026 15:52:17 +0000 Subject: [PATCH 13/14] !and env vars for testing purposes Signed-off-by: John McCrae --- habitat/plan.ps1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 47de060c..a979c33b 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -12,10 +12,6 @@ $pkg_build_deps=@( ) $pkg_bin_dirs=@("bin") -$env:HAB_BLDR_CHANNEL = "base-2025" -$env:HAB_ORIGIN = "chef" -$env:HAB_REFRESH_CHANNEL = "base-2025" - function Invoke-SetupEnvironment { Push-RuntimeEnv -IsPath "RUBY_DLL_PATH" "$pkg_prefix/bin" Set-RuntimeEnv -IsPath "CHEF_POWERSHELL_BIN" "$pkg_prefix/bin" From de9be20ddb99e675f17008cc0751565e4c4236f4 Mon Sep 17 00:00:00 2001 From: John McCrae Date: Mon, 31 Aug 2026 21:02:28 +0000 Subject: [PATCH 14/14] tweak the build resolvers property Signed-off-by: John McCrae --- habitat/plan.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index a979c33b..0001bbd7 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -52,7 +52,7 @@ function Invoke-Build { $hostPackRoot = "$env:DOTNET_ROOT\packs\Microsoft.NETCore.App.Host.win-x64" $hostPackVersion = (Get-ChildItem $hostPackRoot | Sort-Object Name -Descending | Select-Object -First 1).Name $ijwHostSourcePath = "$hostPackRoot\$hostPackVersion\runtimes\win-x64\native\ijwhost.dll" - & $msbuildExe $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 /p:BuildProjectReferences=false /p:VCTargetsPath="$vcTargetsPath" /p:DotNetSdkRoot="$env:DOTNET_ROOT" /p:DotNetCoreRefPackPath="$refPackPath" /p:IjwHostSourcePath="$ijwHostSourcePath" /p:DisableImplicitFrameworkReferences=true /p:GenerateRuntimeConfigurationFiles=false /nodeReuse:false + & $msbuildExe $HAB_CACHE_SRC_PATH/$pkg_dirname/Chef.Powershell.Wrapper.Core/Chef.Powershell.Wrapper.Core.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 /p:BuildProjectReferences=false /p:VCTargetsPath="$vcTargetsPath" /p:DotNetSdkRoot="$env:DOTNET_ROOT" /p:DotNetCoreRefPackPath="$refPackPath" /p:IjwHostSourcePath="$ijwHostSourcePath" /p:DisableImplicitFrameworkReferences=true /p:GenerateRuntimeConfigurationFiles=false /p:MSBuildEnableWorkloadResolver=false /nodeReuse:false if($LASTEXITCODE -ne 0) { Write-Error "dotnet core build failed!" }