fslaborg / FSharp.Charting

Charting library suitable for interactive F# scripting
http://fslab.org/FSharp.Charting/
Other
216 stars 66 forks source link

On OSX you need explicit DLL references (instead of using the script in the nuget package) #29

Open ghost opened 10 years ago

ghost commented 10 years ago

Referencing the FSharp.Charting.Gtk.fsx script in the nuget package doesn't work because of a bug in F# Interactive's resolution of DLLs w.r.t. relative directory #I paths when executing on Mono (or when executing on OSX/Linux).

This means you have to reference the DLLs explicitly, e.g. like this, and you have to remove the references in the script.

#r "../gtk-sharp-2.0/gtk-sharp.dll"
#r "../gtk-sharp-2.0/gdk-sharp.dll"
#r "../gtk-sharp-2.0/atk-sharp.dll"
#r "../gtk-sharp-2.0/glib-sharp.dll"

#r "FSharp.Charting.Gtk.0.90.5/lib/net40/OxyPlot.dll"
#r "FSharp.Charting.Gtk.0.90.5/lib/net40/OxyPlot.GtkSharp.dll"
#r "FSharp.Charting.Gtk.0.90.5/lib/net40/FSharp.Charting.Gtk.dll"

#load "FSharp.Charting.Gtk.0.90.5/FSharp.Charting.Gtk.fsx"
ghost commented 10 years ago

This is external, and fixed in F# 3.0 tag 3.0.31. Leaving this open until we verify it.

vkz commented 10 years ago

I'm experiencing the same issue on OSX. gist shows my output and system information. I run F# 3.0 but have no idea how to check its tag.

PS: @dsyme solution worked but I have to admit this took me hours to arrive at this issue. Somehow I feel that starting F# programming on OSX is not very n00b friendly. If you can think of a decent tutorial that would take me through the basic scaffolding I'd be very grateful, preferably without the Visual Studio friendly magic that Xamarin does. All I want for now is to be able to write scripts from your Expert F#, reference external libraries and run them.

vkz commented 10 years ago

Trying to run http://fsharp.github.io/FSharp.Charting/ReferencingTheLibrary.html now throws an exception

System.TypeInitializationException: An exception was thrown by the type initializer for Gtk.Widget ---> System.DllNotFoundException: gtksharpglue-2
  at (wrapper managed-to-native) Gtk.Widget:gtksharp_gtk_widget_get_requisition_offset ()
  at Gtk.Widget..cctor () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---

which FSharp.Charting.Gtk.0.90.5/FSharp.Charting.Gtk.fsx is trying to fix by

// Workaround bug http://stackoverflow.com/questions/13885454/mono-on-osx-couldnt-find-gtksharpglue-2-dll
if Environment.OSVersion.Platform = System.PlatformID.MacOSX then 
    let prevDynLoadPath = Environment.GetEnvironmentVariable("DYLD_FALLBACK_LIBRARY_PATH")
    let newDynLoadPath =  "/Library/Frameworks/Mono.framework/Versions/Current/lib" + (match prevDynLoadPath with null -> "" | s -> ":" + s) + ":/usr/lib"
    System.Environment.SetEnvironmentVariable("DYLD_FALLBACK_LIBRARY_PATH", newDynLoadPath)

which unfortunately doesn't seem to work for me

vkz commented 10 years ago

I think this means I have gtk# installed (using mono that ships with Xamarin):

vkz-air:~ vkz$ locate gtksharpglue
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.la
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so.dSYM
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so.dSYM/Contents
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so.dSYM/Contents/Info.plist
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so.dSYM/Contents/Resources
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so.dSYM/Contents/Resources/DWARF
/Library/Frameworks/Mono.framework/Versions/3.2.6/lib/libgtksharpglue-2.so.dSYM/Contents/Resources/DWARF/libgtksharpglue-2.so
evolvedmicrobe commented 10 years ago

Does anyone know who distributes the NuGet package? It currently is out of date (and on OSX not functional), compared to what is on github.

jmchapman commented 9 years ago

What is the status of this ("This is external, and fixed in F# 3.0 tag 3.0.31. Leaving this open until we verify it.") now in, mono 3.8?

tpetricek commented 9 years ago

@jmchapman BTW - I think FSharp.Charting is not very good as a cross-platform library. We're trying to fix that with Foogle Charts, but it is still in its early days: http://github.com/fsprojects/Foogle.Charts/ (but I hope there will be a lot more in 2-6 months).

marktab commented 9 years ago

@tpetricek is this issue related to these missing references? 2015-07-05_23-38-48

tpetricek commented 9 years ago

I'm actually not sure what is the status of the FSharp.Charting.Gtk project. @dsyme contributed to it, so perhaps he can comment? (But I'm afraid it might not build at all on Windows.)

essenciary commented 8 years ago

I confirm the issue on OS X 10.10.4

The basic example in the docs:

#load "packages/FSharp.Charting.Gtk/FSharp.Charting.Gtk.fsx" open FSharp.Charting Chart.Line([ for x in 0 .. 10 -> x, x*x ])

crashes with Error: An exception was thrown by the type initializer for Gtk.Widget

essenciary commented 8 years ago

Actually, sending the lines to F# interactive, it seems it crashes at the 2nd line:

> #load "packages/FSharp.Charting.Gtk/FSharp.Charting.Gtk.fsx";;
[Loading /Users/adrian/Dropbox/Projects/_learning_/_fs_/packages/FSharp.Charting.Gtk/FSharp.Charting.Gtk.fsx]

namespace FSI_0002.FSharp.Charting
  val verifyMac : unit -> bool
  val isMac : bool
  module FsiAutoShow = begin
  end

> open FSharp.Charting;;
* Assertion at mini-exceptions.c:540, condition `class' not met
essenciary commented 8 years ago

Looks like it can't open FSharp.Charting since anything there will yield the same result.

> open FSharp.Chartingggg;;
* Assertion at mini-exceptions.c:540, condition `class' not met
evolvedmicrobe commented 8 years ago

@essenciary Can you try editing FSharp.Charting.Gtk.fsx to change these lines:

member x.Invoke f = 
     let res = ref None
     let evt = new System.Threading.AutoResetEvent(false)
     Gtk.Application.Invoke(new System.EventHandler(fun _ _ ->
       res := Some(f())
       evt.Set() |> ignore ))
     evt.WaitOne() |> ignore
     res.Value.Value 

Into this:

 member x.Invoke f = 
     f()

And seeing if it will then work?

essenciary commented 8 years ago

@evolvedmicrobe Thanks a lot, making progress but still failing.

#load "packages/FSharp.Charting.Gtk/FSharp.Charting.Gtk.fsx";;
[Loading /Users/adrian/Dropbox/Projects/_learning_/_fs_/packages/FSharp.Charting.Gtk/FSharp.Charting.Gtk.fsx]

namespace FSI_0002.FSharp.Charting
  val verifyMac : unit -> bool
  val isMac : bool
  module FsiAutoShow = begin
  end

> open FSharp.Charting;;
> Chart.Line([ for x in 0 .. 10 -> x, x*x ]);;
Binding session to '/Users/adrian/Dropbox/Projects/_learning_/_fs_/OxyPlot.dll'...
Binding session to '/Users/adrian/Dropbox/Projects/_learning_/_fs_/OxyPlot.GtkSharp.dll'...
val it : ChartTypes.GenericChart =
  Error: An exception was thrown by the type initializer for Gtk.Widget
> 
evolvedmicrobe commented 8 years ago

Eck, GTK. The joys of unmanaged code.

So I can't reproduce that error here, do you have any more information from the stack trace?

One theory is that you might have a System.DllNotFoundException. FSharp.Charting needs to load a lot of unmanaged libraries in order to render the plot with gtk, these can normally be found in the locations set by this environmental variable:

export DYLD_FALLBACK_LIBRARY_PATH=/LIBRARY/Frameworks/Mono.framework/Versions/Current/lib:/lib:/usr/lib

However, there are some major gotchas with setting this variable.

Gotcha #1 - This variable has to be set before you try and load ANY unmanaged library. Unmanaged libraries are loaded with a call to "dlopen" but depending on versions and reasons I cannot divine, this function appears to only load the environmental variables once. That is, if you try to set this after loading any library, as is done in F# charting with the code below, it may be useless. dlopen may have already read the variable once and then keeps its copy.

// Workaround bug http://stackoverflow.com/questions/13885454/mono-on-osx-couldnt-find-gtksharpglue-2-dll
if Environment.OSVersion.Platform = System.PlatformID.MacOSX then 
    let prevDynLoadPath = Environment.GetEnvironmentVariable("DYLD_FALLBACK_LIBRARY_PATH")
    let newDynLoadPath =  "/Library/Frameworks/Mono.framework/Versions/Current/lib" + (match prevDynLoadPath with null -> "" | s -> ":" + s) + ":/usr/lib"
    System.Environment.SetEnvironmentVariable("DYLD_FALLBACK_LIBRARY_PATH", newDynLoadPath)

So the F# code above can fail if the environmental variable has already been read by dlopen (again, this is undefined and may vary from OS versions per my current understanding)

Gotcha #2 - Environmental variables you set in your .bash_profile will not be set when apps are opened. This was a recent Mac change, it didn't use to be this way. However, now from terminal you must explicitly set the variable if you want it to be set in any process started from a .app GUI, a la

launchctl setenv DYLD_FALLBACK_LIBRARY_PATH $DYLD_FALLBACK_LIBRARY_PATH
open -n open -n /Applications/Xamarin\ Studio.app/

I would try to see if you can figure out what the problem is, if it's the dll load issue, setting the relevant environmental variables before loading fsharpi may help. If you have Xamarin studio installed, I might also try just creating an empty C# GTK project so that you can run it and verify a window pops up (this will confirm the GTK installation). With a stack trace, the environmental variable problem ruled out and a confirmation of a working GTK installation, it should narrow it down quite a bit.

essenciary commented 8 years ago

@evolvedmicrobe

Per your comment, I gave Xamarin a try. Till now I've been running the code via Atom or directly in the terminal (cause Xamarin crashes when trying to create a new FSharp Script file - but that's for another day).

Opening the script file in Xamarin with the NuGet dependencies already installed, using the modified x.Invoke and running it in F# Interactive just worked (™).

Running it in Xamarin with the original x.Invoke does break.

So then:

  1. changing x.Invoke was key - thanks a million
  2. looks like I'm stuck in the middle of a dependency hell. I've spent over 10 hours in the last week trying to get this example to work mapping DLLs and building GTK. I'd love a stack that just works 100% but since I'm into web dev, I don't mind running GTK dependent scripts in Xamarin once in a blue moon.

If this issue is relevant in regards to refactoring x.Invoke I'm happy to dig into it and provide any additional information.

Thanks again for your support, much appreciated!

evolvedmicrobe commented 8 years ago

@essenciary Glad you got it working! It looks like your problem has identified two key issues.

Issue #1 - The environmental variables cannot always be set at runtime to load static libraries

Because dlopen may read the DY/LD_LIBRARY_PATH environmental variables before they are modified by the F# code to point to the appropriate path (by System.Environment.SetEnvironmentVariable), attempting to set these variables in F# is not a robust solution as it may fail if the variable has already been read by the library loading functions. As we cannot alter the environmental variables of the parent process before the child F# process is called, perhaps it would be better to simply check for the correct environmental variable and fail with a useful error message if it is not set. That is, we implement a consistent and useful failure rather than an occasionally successful and confusing work around.

Issue #2 - GTK SIGSEGV on Gtk.Application.Invoke function call

Calling Gtk.Application.Invoke to alter the state of a GUI rather than running it on the current thread is supposed to be hygenic and reflect best practices. However, clearly in this case the call to Gtk.Application.Invoke causes havoc and a low-level error that shuts down the runtime. I am not sure what caused this, but when you mentioned digging in is this something you might be able to investigate? (This never used to be a problem, so I suspect something has changed)

essenciary commented 8 years ago

@evolvedmicrobe Nigel, thanks. Sorry for my late reply, holidays.

I'll take a look but to be honest, I don't think I'm ready for such a task. I'm pretty new to F# and have no knowledge of Mono/GTK/etc. Coming from a PHP/Ruby/Elixir type of stack.