How to distribute a .net core 2.0 console application on osx
Asked Answered
K

3

16

I am using Visual Studio for Mac, my application is written, it does what I want. The user's target platform is OSX. It relies on 2 Nuget Packages with their own dependencies, specifically:

  • NetMQ
  • Microsoft.Extensions.Configuration.CommandLine

I run the following command to build the application:

dotnet build -c Release -r osx.10.11-x64

The output tells me it succeeded and shows me where it put the files.

Contained in that folder are the following files, this folder is what I assume I distribute to my user, but that leads to errors hence this question:

MyApp <---- executable
MyApp.deps.json
MyApp.dll
MyApp.pdb
MyApp.runtimeconfig.dev.json
MyApp.runtimeconfig.json
libhostfxr.dylib
libhostpolicy.dylib

So my user receives this and they have the dotnet runtime installed. When they go to run the executable (Here MyApp aka ./MyApp)they receive the following error:

An assembly specified in the application dependencies manifest (MyApp.deps.json) was not found:
package: 'AsyncIO', version: '0.1.26'
path: 'lib/netstandard1.3/AsyncIO.dll'

Now, AsyncIO is a dependency of NetMQ so that's where that is coming from. However, I can ./MyApp from anywhere on my filesystem and it works. So I'm left to think that the Nuget packages being installed on my system as the development system are still accessible where as on the new users's system they are not. I haven't really found any documentation pertaining to distribution from a Mac. The only thing I can think to do right now is to distribute the project files instead and instruct the user to run:

dotnet run -p MyApp.csproj

Which, if I understand correctly, will install the nuget packages.

I can also have the user use:

dotnet MyApp.dll

I have seen this form around in my googling. That begs they question though, of why an executable is generated though. Beggars can't be choosers, if the way to do it dotnet <dll> I'm happy, I just need to get it to run.

There has to be something I'm missing here or else this detail, distribution, of netcore2.0 apps was just overlooked?

In digging around some more this also seems like a useful command:

dotnet publish -c Release --framework netcoreapp2.0 --runtime osx.10.11-x64

And that adds ALL kinds of DLLs and all of the dependency DLLs as well. Words like publish make me think this is the way this needs to go.

These 2 links are where I am primarily getting my info:

https://learn.microsoft.com/en-us/dotnet/core/deploying/deploy-with-cli https://learn.microsoft.com/en-us/dotnet/core/tools/project-json-to-csproj

Kowtow answered 19/10, 2017 at 12:26 Comment(0)
A
20

You have the right intuition about the difference between build and publish.

dotnet build will build the application for local development. One aspect is that the build will assume all dependencies are available via local nuget cache.

dotnet publish will build the application for deployment to other machines. This deals with dependencies explicitly.

There are two modes of publishing: self-contained deployments and framework-dependent deployments.

Framework-dependent deployments work by relying on a framework already present on the system. If you publish your application in this mode, you will only include your application and its dependencies.

To publish in a FDD, use:

dotnet publish -c Release --framework netcoreapp2.0

Self-containted deployments, in contrast, will include the entire .NET Core runtime and your application and its dependencies.

To publish a SCD, use:

dotnet publish -c Release --framework netcoreapp2.0 --runtime osx-x64

(Btw, please do use the more general osx-x64 rather than the very specific osx.10.11-x64)

Do you see the difference between the two modes of publishing? It is just the presence/absence of the runtime id. When, in your example, you use --runtime flag, you are asking your application to be published as a SCD, which ends up including all of .NET Core runtime as well. Just leave it out and you should get what you expect.

When you publish your application as a FDD, you should see a directory called bin/Release/netcoreapp2.0/publish in your source code. Use that directory (and not bin/Release/netcoreapp2.0/) as your release archive. Your users should just run dotnet ./path/to/publish/MyApp.dll.

Take a look at https://learn.microsoft.com/en-us/dotnet/core/deploying/ for more information.

Aureomycin answered 19/10, 2017 at 13:43 Comment(1)
Thanks. Is there a way to create a thin SCD? I want to create a minimal SCD. Currently, even a simple "Hello World" is 60+ MB in size and it wouldn't require the whole .NET Core runtime.Dorolice
M
6

Following up on @omajid's answer,

If you want to pack your console application in a nice and tidy package where you just 'click' it and it opens a terminal to start your app... heres an example app (with a tree icon):

You first want a folder/file structure like this (begin the mkdir!):

enter image description here

Then in info.plist, just paste in

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>launcher</string>
    <key>CFBundleIconFile</key>
    <string>trees.icns</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>2.2</string>
    <key>CFBundleSignature</key>
    <string>xmmd</string>
    <key>CFBundleVersion</key>
    <string>2.2</string>
    <key>NSAppleScriptEnabled</key>
    <string>NO</string>
</dict>
</plist>

Get your 'trees.icns' from some .icns site off the internet.

then copy everything from dotnet publish -c Release --framework netcoreapp3.1 --runtime osx-x64 command into the osx-x64 folder (theres going to be a TON of .dll files in here.. i had ~197)

enter image description here

Finally in launcher add the following script:

#!/bin/sh

# Set the working directory
DIR=$(cd "$(dirname "$0")"; pwd)

# Run the application
echo "running from $DIR/osx-x64"

open -a Terminal $DIR/osx-x64/dotnetconsole

Then Voila... go into your finder and click the "MyApp" application enter image description here

reference: packaging jar tutorial

Marikomaril answered 1/5, 2021 at 3:2 Comment(0)
V
1

It still goes back to his original observation which is what I see, that the executable 'MyApp' is not truly independent binary. If you move that binary to a different directory it will complain it cannot find MyApp.dll.

Specifically the commands I used were:

dotnet new console
dotnet publish -c Release --runtime osx.10.12-x64 --self-contained true

and I have the same problem, my 'MyApp' really isn't a self contained app. In fact, I have discovered that 'MyApp' needs at minimum the following files within the directory it lives:

MyStatic     ----> my 'exe'                   
MyStatic.dll                    
libhostpolicy.dylib
MyStatic.deps.json              
MyStatic.runtimeconfig.dev.json

Any self-contained binary shouldn't require all this junk to run stand-alone.

Vaivode answered 14/3, 2019 at 23:36 Comment(5)
Don't call it junk. It is self-contained in the sense that it doesn't require the runtime to be installed on the target system. If you want a single exe, you can always build a wrapper around it.Kermit
@Marcell Why can't we have the wrapper generated automatically? Is there an easy to way to package that directory to appear as a single file or something close to that?Lohengrin
@Lohengrin i agree, they should have followed the convention that almost every other MacOS app usesMarikomaril
Just add <PropertyGroup> <PublishSingleFile>true</PublishSingleFile> </PropertyGroup>, and all dependencies etc will be bundled in one file.Gratuitous
you can now configure this in the ide in visual studio under "advanced" in your publish profile.Spruill

© 2022 - 2024 — McMap. All rights reserved.