views:

245

answers:

3

Here's my test code to isolate the problem:

open Microsoft.FSharp.Metadata
[<EntryPoint>]
let main args =
  let core = FSharpAssembly.FromFile @"C:\Program Files\FSharp-2.0.0.0\\bin\FSharp.Core.dll"
  let core2 = FSharpAssembly.FSharpLibrary
  let core3 = System.AppDomain.CurrentDomain.GetAssemblies() 
              |> Seq.find (fun a -> a.FullName.Contains "Core") 
              |> FSharpAssembly.FromAssembly

  core.Entities |> Seq.iter (printfn "%A")
  0

All three lets should give me the same FSharpAssembly. Instead, all 3 throw an exception that FSharp.Core is not an F# assembly (details below, re-formatted for readability). Two more clues:

  1. Using the core3 method, I get the same error for the test F# assembly itself
  2. I don't get the error at FSI after doing #r "@C:\Program Files...\FSharp.Powerpack.Metadata.dll".

Any ideas? I'm using Visual Studio 2008, F# 2.0 and F# Powerpack 2.0.0.0 (May 20, 2010) release on an oldish XP VM, I think it's updated to SP3 though.

(I got the error this morning with Powerpack 1.9.9.9, so I upgraded to 2.0.0.0. I thought that if 1.9.9.9 doesn't recognise F#'s 2.0.0.0's assemblies, then maybe bugfixes in Powerpack 2.0.0.0 would help.)

Unhandled Exception: System.TypeInitializationException: 
  The type initializer for 'Microsoft.FSharp.Metadata.AssemblyLoader' threw an
    exception. 
  ---> System.TypeInitializationException: The type initializer for
    '<StartupCode$FSharp-PowerPack-Metadata>.$Metadata' threw an exception. 
  ---> System.ArgumentException: could not produce an FSharpAssembly 
    object for the assembly 'FSharp.Core' because 
    this is not an F# assembly
Parameter name: name
   at Microsoft.FSharp.Metadata.AssemblyLoader.Add(String name,Assembly assembly)
   at <StartupCode$FSharp-PowerPack-Metadata>.$Metadata..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.FSharp.Metadata.AssemblyLoader..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.FSharp.Metadata.AssemblyLoader.Get(Assembly assembly)
   at Microsoft.FSharp.Metadata.FSharpAssembly.FromAssembly(Assembly assembly)
   at Program.main(String[] args) in 
     C:\Documents an...\FSMetadataTest\Program.fs:line 11
Press any key to continue . . .
+1  A: 

This is really confusing - I tried running the code using the exact scenario you described (F# 2.0 in Visual Studio 2008, with the latest PowerPack 2.0 from CodePlex) and it works without any exceptions.

You could also make sure that you have the latest release of F# for Visual Studio 2008 (that was released shortly after final version of Visual Studio 2010), because FSharp.Core.dll that you're loading needs to be compatible with FSharp.PowerPack.Metadata.dll. Also, is the Metadata library able to load other dll libraries that you compile with the current version of F# compiler?

EDIT If this doesn't work then (I'd think) the Metadata library is probably out of sync with the F# compiler:

let a = System.Reflection.Assembly.GetExecutingAssembly()
     |> FSharpAssembly.FromAssembly
printf "%A" a.Entities.Count

If the following doesn't work then that would be really confusing - no matter what version, the library should be able to read itself!

let a = FSharpAssembly.FromFile @"C:\...\bin\FSharp.PowerPack.Metadata.dll"
printf "%A" a.Entities.Count
Tomas Petricek
To answer your last question: no. Not when I try `FromAssembly` on the currently loaded assembly, at least. I will check for a newer version of F#.
Nathan Sanders
@Nathan: You could also try whether the `Metadata` library is able to read itself - I'd think that this should work.
Tomas Petricek
Good point. I will check that too. I re-installed F#, didn't make a difference, so I am running Windows update, getting SP2 of .NET 2.0 and 3.0. Not that I expect it to help, but who knows.
Nathan Sanders
Well, this is annoying: both of your snippets fail when `Metadata` attempts to load an FSharpAssembly for `FSharp.Core`. So apparently you can't avoid loading `FSharp.Core`, even when you request to load something else.
Nathan Sanders
@Nathan: Yeah, I guess that `FSharp.Core` is loaded in the initializer of the module (so it is loaded eagerly when you access anything from `Metadata`). That's a bit unfortunate, but probably needed...
Tomas Petricek
+2  A: 

I found the immediate cause of the problem by reading the Powerpack source on Codeplex. Powerpack.Metadata always loads FSharp.Core before any other assembly, so if it's broken, it blocks the rest.

From my research, most other F# assemblies carry their metadata with them in a manifest resource stream named "FSharpSignatureData.Package.Name"--that's where the Metadata library looks first. But FSharp.Core doesn't. (At least, on my XP VM the 2.0.0.0 download for VS 2008 does not.) So the Powerpack Metadata looks for a file called FSharp.Core.sigdata in the same directory it expects to find FSharp.Core.dll.

The code looks four places for this directory name:

  1. ConfigurationSettings.AppSettings.["fsharp-core-referenceassembly-location"].
  2. The registry key @"SOFTWARE\Microsoft\.NETFramework\" + MSCorLibRunningRuntimeVersion + @"\AssemblyFoldersEx\Microsoft Visual F# 4.0", where MSCorLibRunningRuntimeVersion is determined by reflection--in my case it's "v2.0.50727". I think this only applies if you have VS 2010 and hence F# 4.0
  3. The registry key @"Software\Microsoft\.NETFramework\AssemblyFolders\Microsoft.FSharp-" + FSharpTeamVersionNumber, where FSharpTeamVersionNumber is hard-coded as "1.9.9.9", at least in the codeplex version. I don't have the source for the May 2010 release installed on my machine.
  4. System.AppDomain.CurrentDomain.BaseDirectory. This is the "bin/Debug" directory of your local project, unless you are in fsi. Then it's some variant of C:\Program Files\FSharp-2.0.0.0\bin.

For me, all four fail:

  1. I don't have an app config file. I don't know the .NET ecosystem very well, so I don't know how to create one.
  2. I don't have VS 2010 installed.
  3. I have a "Microsoft.FSharp-2.0.0.0" key in the right place, but no "Microsoft.FSharp-1.9.9.9" key.
  4. My local project doesn't have a copy of FSharp.Core.sigdata in the bin/Debug directory.

Now that I know what's going on, I have a number of fixes (besides using a real Windows machine instead of an ancient VM, which I plan to fix in about a month). I should probably learn about .NET's config files at some point anyway, so that's probably the best medium-term solution. Creating the registry key "Microsoft.FSharp-1.9.9.9" or copying FSharp.Core.sigdata to the bin/Debug directory is a super easy short-term fix.

I still don't know if this bug should be widespread or if it's just caused by my upgrades from VS 2005->2008 and multiple F# previews. From reading the codeplex source, it seems to me that other people without VS 2010 should also have this problem, just because FSharpTeamVersionNumber is hard-coded as 1.9.9.9, and hasn't been updated to match the registry key, 2.0.0.0. But maybe the order that I upgraded to F# 2.0 and the F# Powerpack 2.0 was incorrectly intermingled to produce this condition. Or maybe there aren't enough people using the Metadata library to expose the bug.

Nathan Sanders
+2  A: 

Nathan, great investigation, the version number was indeed not updated in original May2010 release, see http://fsharppowerpack.codeplex.com/workitem/4548 We have uploaded binaries with corrected version number.

Mitya