Search code examples
c#macosmonovisual-studio-codednx

Debugging C# Console Application in VSCode using DNU/DNX and project.json


Problem (this is all on OSX btw.):

  1. Make a new C# Console Application through yo aspnet, dnu restore, dnu build, dnx run <- all is peachy
  2. Add dependency (e.g. Newtonsoft.JSON, Akka) to project, dnu restore, dnu build, dnx run <- all is peachy
  3. Look up this site to see how it's supposed to work. https://code.visualstudio.com/Docs/editor/debugging#_mono-debugging (oh and piece together the rest by googling and through trial and error)
  4. Add using Newtonsoft.JSON in Program.cs and... actually use it.
  5. Try compiling/debugging
  6. meh...

Lets investigate:

mcs -debug Program.cs cannot compile Program.cs because it can't find the dependency installed by dnu restore which resides somewhere in ~/.dnx/packages/.... Makes kinda sense. So using mcs to compile is not the way to go?

Others say "use xbuild or whatever". But i don't wanna make .csproj files. I wanna use project.json and DNU/DNX. (and i haven't tried actually)

So what can you do? Try fiddle around with tasks.json to make dnu build work.

Here's what i got.

{
    "version": "0.1.0",
    "command": "dnu",
    "options": {
        "cwd": "/Users/meh/Development/code/HelloWorld"
    },
    "showOutput": "silent",
    "tasks": [
        {
            "taskName": "build",
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
}

It isn't pretty, but it actually gets the work done.

Now the part that i can't get to work is the debugging part.

So dnu build create the output in some directory like this /Users/meh/Development/code/HelloWorld/bin/Debug/dnx451/HelloWorld.dll

If you try to make it work like in this launch.json, which works for the simple Program.cs file generated without dependencies, you get nothing. I tried it through the console to see why it wasn't working. So why? Because mono can't find the necessary dependencies. Why? Because they're not in said directory.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "mono",
            "request": "launch",
            "program": "/Users/meh/Development/code/HelloWorld/bin/Debug/dnx451/HelloWorld.dll",
            "args": [],
            "cwd": ".",
            "runtimeExecutable": null,
            "env": {}
        },
        {
            "name": "Attach",
            "type": "mono",
            "request": "attach",
            "address": "localhost",
            "port": 5858
        }
    ]
}

If you use dnx run or whatever you have configured to run the compiled program it works of course.

So looking around dnx --help i've seen a nice dnx --debug option. If you run that on console you get this nice output:

Blah:HelloWorld meh$ dnx --debug run
Process Id: 19228
Waiting for the debugger to attach...

Thing is... i have no clue what port the debugger is waiting on. And it actually seems that mono-sgen (which ps tells me the PID belongs to) isn't actually listening on any port. I tried to check via lsof.

My fear is that dnx --debug is actually not really working as i've seen random comments on github issues here and there mentioning something.

That's it. That's how far i got.

So. Has anyone gotten any further? Has anyone even tried? Am i an idiot for trying to make console applications through DNX/DNU and use VSCode to program and debug it? Am i just doing something completely wrong?

Any takers?

Edit 1: You have to add this to project.json to get an entry point in the compiled DLL file for mono to be able to run the program.

"compilationOptions": {
    "emitEntryPoint": true
},

Edit 2: Other things i tried...

1. Look at what dnx run actually does.

And what does it do?

45588 s000  U+     0:02.11 mono /Users/meh/.dnx/runtimes/dnx-mono.1.0.0-rc1-update1/bin/Microsoft.Dnx.Host.Mono.dll run

It calls something called Microsoft.Dnx.Host.Mono.dll from inside your project directory that gets used to call all the stuff.

Sadly there's no real way to put that command into a launch.json entry to make it a) start off in the right directory and b) omit the .DLL file name from the command.

This is the best i could wrangle out of launch.json.

cd '/Users/meh/Development/code/HelloWorld/bin/Debug/dnx451';  'mono' '--debug' '--debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:61396' '/Users/meh/.dnx/runtimes/dnx-mono.1.0.0-rc1-update1/bin/Microsoft.Dnx.Host.Mono.dll' 'HelloWorld.dll' 'run'

As is evident, it's the wrong directory for the Microsoft.Dnx.Host.Mono.dll to run from and you can't make it omit the HelloWorld.dll parameter.

The other thing is. Even if you could make VSCode generate the right command line it doesn't work. Opening up a terminal and executing a command like this gets you into debugging mode:

cd '/Users/meh/Development/code/HelloWorld/';  'mono' '--debug' '--debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:61396' '/Users/meh/.dnx/runtimes/dnx-mono.1.0.0-rc1-update1/bin/Microsoft.Dnx.Host.Mono.dll' 'run'

And then attaching to the debugger from VSCode via the attach config in launch.json, as seen above (of course the port needs to match), runs the program but the debugger can't get a hook in. Guess that's because you're not really debugging HelloWorld.dll. Or dnx build doesn't output debugging symbols. Probably the last one.

2. Tell mono where to fine the dependencies.

So i dug around the man mono and found out that you can tell mono via $MONO_PATH where to look for libraries.

But making a script and slapping all the paths found in ~/.dnx/packages is no good, because in there contained are all the libraries you ever have dnu restored. Every version of them.

So how to only get the libraries your project.json references? Well i dug around dnu for a bit and found dnu publish. Which generates an output directory inside of bin that contains a number of things. One of those is a folder called packages containing all your project dependencies.

Yeay? No!

Because what dnu publish doesn't do is create a compiled .DLL file. Instead it creates a shell script that seems to on the fly compile your stuff. But whatever. We just wanted the DLLs anyways.

So what do we have. We have a dnu build to get us a nice .DLL file. And we have a dnu publish that collects the dependencies for us.

Now you can use a little shell magic to collect the paths for us in a $MONO_PATH friendly way.

ls -d $PWD/bin/output/approot/packages/*/*/lib/net45 | tr '\n' ':'

The problem again lies with launch.json. It has a nice property called env : {} but VSCode, when generating the actual command to launch the debugger, puts all the stuff you put in there in single quotes. Which gets interpreted as literal strings. So you can't actually put something like the little command above into it. sad trombone

Btw. if you open a terminal again, and export the output of the above mentioned script into $MONO_PATH and then run the mono command by hand like in the other try you can then again try to attach the VSCode debugger to it, the command runs but the debugger can't hook into it. Again i think it's either no debugger symbols or well the other thing.

 Flash:HelloWorld meh$ export MONO_PATH=$(ls -d $PWD/bin/output/approot/packages/*/*/lib/net45 | tr '\n' ':')
 Flash:HelloWorld meh$ echo $MONO_PATH 
 /Users/meh/Development/code/HelloWorld/bin/output/approot/packages/Akka/1.0.5/lib/net45:/Users/meh/Development/code/HelloWorld/bin/output/approot/packages/Microsoft.CSharp/4.0.1-beta-23516/lib/net45:/Users/meh/Development/code/HelloWorld/bin/output/approot/packages/Newtonsoft.Json/8.0.1-beta3/lib/net45:/Users/meh/Development/code/HelloWorld/bin/output/approot/packages/System.Collections/4.0.11-beta-23516/lib/net45:/Users/meh/Development/code/HelloWorld/bin/output/approot/packages/System.Linq/4.0.1-beta-23516/lib/net45:/Users/meh/Development/code/HelloWorld/bin/output/approot/packages/System.Threading/4.0.11-beta-23516/lib/net45:
 Flash:HelloWorld meh$ cd '/Users/meh/Development/code/HelloWorld/bin/Debug/dnx451';  'mono' '--debug' '--debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:61396' 'HelloWorld.dll'
 Meh
 Hello World
 {
   "blah": "blub"
 }
 From ConsoleActor: blah blub
 From ConsoleActor: blubberblub

 Flash:dnx451 meh$ 

Conclusion for now. I'm all out of ideas. And short of using gulp or other build tools, like @richardsonmarkj suggested, i don't see it happening any time soon.


Solution

  • DNX is deprecated, so this question is moot now.

    Leaving this link here that shows experimental .net core debugging with vscode.