Note:
From the docs, It is generally unclear what -OutputType values are expected to work with what edition / on what platform - see this docs issue requesting clarification.
On Unix, -OutputType ConsoleApplication is currently fundamentally broken - seemingly, a _Windows_ PE file is created, which obviously cannot run on Unix-like platforms.
On Windows, the code below works fine in _Windows PowerShell_, but in PS Core it produces an *.exe file that complains about a missing System.Runtime assembly.
System.Runtime and System.Console explicitly to -RequiredAssemblies makes no difference.Perhaps this is ultimately primarily a _documentation_ problem, but we should definitely also at least prevent effectively unsupported parameter combinations by errorring out right away, rather than quietly producing broken output binaries.
Run on Windows.
The following works in _Windows PowerShell_, but not in PowerShell Core:
Remove-Item -ea Ignore ./foo.exe
Add-Type -OutputType ConsoleApplication -OutputAssembly ./foo.exe -TypeDefinition @'
using System;
static class ConsoleApp {
static int Main(string[] args) {
Console.WriteLine("hi");
return 0;
}
}
'@
./foo.exe | Should -be 'hi'
The test should succeed .
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
The system cannot find the file specified.
InvalidResult:
Line |
13 | ./foo.exe | Should -be 'hi'
| Expected 'hi', but got $null.
PowerShell Core 7.1.0-preview.5
Tested this on v6.2.7 and got the same results.
@PowerShell/powershell-committee should consider deprecating this parameter
yah - the API which is used to create these executables does not work correctly for non-Windows platforms. Probably the right thing to do is remove this option for non-Windows systems. As @mklement0 states it builds WinPE binaries.
It should be possible to provide support for this scenario, but would need a somewhat large change. I think our only path is something along the lines of what dotnet publish does, as the current API doesn't do the right thing.
Quoted from https://github.com/MicrosoftDocs/PowerShell-Docs/issues/6417#issuecomment-668261738:
I don't think
ConsoleApplicationandWindowsApplicationcurentlly work in PowerShell 7. And I doubt if they actually work in .NET Core. They both can produce an executable on Windows, neither works when running the executable.For 'ConsoleApplication':
PS> Add-Type -TypeDefinition 'public class Foo { public static void Main() { System.Console.WriteLine("Hello World!"); } }' ->OutputAssembly c:\blah.exe -OutputType ConsoleApplication PS> C:\blah.exe Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=4.2.2.0, >Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.For 'WindowsApplication':
PS> Add-Type -TypeDefinition 'public class Foo { public static void Main() { System.IO.File.Create(@"C:\arena\tmp\daxian"); } }' ->OutputAssembly c:\zoo.exe -OutputType WindowsApplication PS> C:\zoo.exe PS> Test-Path C:\arena\tmp\daxian False
Thanks, @JamesWTruher and @daxian-dbw.
Obviously, WindowsApplication will never make sense on Unix-like platforms.
Note that Library _does_ work, even on Unix.
The question is: Do we anticipate ConsoleApplication to ever work, even if only on Windows? What about WindowsApplication on Windows, in the future?
Either way, for now we should throw an error for ConsoleApplication on parameter validation, and for WindowsApplication on Unix.
Only if we never expect these things to work should we deprecated the -OutputType parameter altogether (in which case -OutputType Library would be invariably implied).
@PowerShell/powershell-committee discussed this, we agree that we should do a runtime check if ConsoleApplication or WindowsApplication is specified then a terminating error with descriptive message indicating both of those are not currently supported should be thrown.
Can we ask the API owner? Is this a bug in the cmdlet, in the API or it is "by design"?
@iSazonov I think the problem is no GAC (and none of the core libs would be in the GAC anyway if custom handling was added). So PS could reimplement all the logic to copy dependencies to the target directory (which I don't think would be desirable anyway) or write a framework dependent app (which wouldn't be super reliable). Either way I think the current behavior would be considered by design.
Edit: Nah that's not it, marking outdated.
I think the problem is no GAC
In the case it is not broken behavior and should be documented but not disabled.
In the case it is not broken behavior and should be documented but not disabled.
I mean document what though? You'd have to manually copy all the framework dll's to the same folder you're emitting the executable to. I think it's better to break in an obvious way, a user expecting a ready to run standalone exe is going to get some pretty contextually confusing errors.
The behavior is pretty broken for the use case, just not in a feasible to fix way.
If user compiles in pwsh home will this work?
Runtime-dependent apps (not self-contained) will discover installed runtime.
The .exe files generated with ConsoleApplication or WindowsApplication target full .NET Framework, not .NET Core.
You can view the dependencies of those files by running dumpbin /DEPENDENTS <generated-exe-file>
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
mscoree.dll
Summary
2000 .reloc
2000 .text
They depend on C:\Windows\System32\mscoree.dll, which indicates they are targeting .NET Framework, not .NET Core. However, at the meantime, they are referencing System.Runtime, which is .NET Core only. This combination just won't work.
As a comparison, this is the dependencies of a .NET Core .exe file:
Dump of file .\console.exe
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
KERNEL32.dll
USER32.dll
SHELL32.dll
ADVAPI32.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-filesystem-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll
Summary
2000 .data
2000 .pdata
F000 .rdata
1000 .reloc
1000 .rsrc
18000 .text
https://github.com/dotnet/roslyn/issues/46865 was opened in dotnet/roslyn.
@daxian-dbw Thanks for these investigations!
I want to add that users can certainly use external tools but it is expected for PowerShell to use powerful cmdlets. For Add-Type I always want to expose as many Roslyn capabilities as possible so that users always stay in PowerShell.
It sounds like it _is_ possible to make this work in .NET Core (which would enable us to support both ConsoleApplication and WindowsApplication on Windows, and ConsoleApplication on Unix).
Using the net .NET Core CLI, you can create platform-specific single-file executables as follows:
# E.g., on 64-bit Windows; run from the project root dir.
net publish -p:PublishSingleFile=true -r win-x64 -c Release
The resulting binary is created as ./bin/<configuration>/<target-framework>/<rid>/<project>[.exe]; e.g.: ./bin/Release/net5.0/win-x64/foo.exe, and is indeed a self-contained executable - but that comes at cost:
From what I can tell, such a _self-contained_ executable is actually a ZIP archive that bundles all referenced assemblies and the runtime.
You can opt out of bundling the runtime and make the executable _runtime-dependent_ with --self-contained=false, which means you still need the targeted .NET runtime installed separately on the machine.
The first time you execute such a file, it extracts the support files in the archive to a temporary Temp:/.net/<executableName>/<randomFileName/ folder and then runs the actual application referencing these files.
The temporary folder is left in place, so that you don't pay the self-extraction performance in subsequent invocations, but, of course, manual or scheduled cleanup of temp:/ could make re-extraction necessary.
A self-contained hello-world console application (dotnet new console) weighs in at about 51 _mega_-bytes, and a runtime-dependent one at only 0.59 megabytes, which, however, is still hefty compared to an equivalent (GAC-dependent) .NET Framework application, which is 4 _kilo_-bytes (4,096 _bytes_).
Most helpful comment
yah - the API which is used to create these executables does not work correctly for non-Windows platforms. Probably the right thing to do is remove this option for non-Windows systems. As @mklement0 states it builds WinPE binaries.
It should be possible to provide support for this scenario, but would need a somewhat large change. I think our only path is something along the lines of what
dotnet publishdoes, as the current API doesn't do the right thing.