diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e82d27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +packages/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/ResourceTypeProvider.fs b/ResourceTypeProvider.fs new file mode 100644 index 0000000..07146c8 --- /dev/null +++ b/ResourceTypeProvider.fs @@ -0,0 +1,132 @@ +namespace Xamarin.Android.FSharp + +open System +open System.IO +open System.Reflection +open System.CodeDom.Compiler +open System.Collections.Generic +open Microsoft.CSharp +open FSharp.Quotations +open FSharp.Core.CompilerServices +open Microsoft.FSharp.Core.CompilerServices + +[] +type ResourceProvider(config : TypeProviderConfig) = + let mutable providedAssembly = None + let invalidate = Event() + + let compiler = new CSharpCodeProvider() + let pathToDesigner = Path.Combine(config.ResolutionFolder, "Resources") + + // watcher doesn't trigger when I specify the filename exactly + let watcher = new FileSystemWatcher(pathToDesigner, "*.fs", EnableRaisingEvents=true) + + + let generate sourceCode = + let guid = Guid.NewGuid() |> string + let asm = sprintf "ProvidedTypes%s.dll" (Guid.NewGuid() |> string) + let cp = CompilerParameters( + GenerateInMemory = false, + OutputAssembly = Path.Combine(config.TemporaryFolder, asm), + TempFiles = new TempFileCollection(config.TemporaryFolder, false), + CompilerOptions = "/nostdlib /noconfig") + + let addReference assemblyFileName = + printfn "Adding reference %s" assemblyFileName + let reference = + config.ReferencedAssemblies |> Array.tryFind(fun r -> r.EndsWith(assemblyFileName, StringComparison.InvariantCultureIgnoreCase) + && r.IndexOf("Facade") = -1) + + match reference with + | Some ref -> cp.ReferencedAssemblies.Add ref |> ignore + (Some ref, assemblyFileName) + | None -> printfn "Did not find %s in referenced assemblies." assemblyFileName + None, assemblyFileName + + + printfn "F# Android resource provider" + let android = addReference "Mono.Android.dll" + let system = addReference "System.dll" + let mscorlib = addReference "mscorlib.dll" + + let addIfMissingReference addResult = + match android, addResult with + | (Some androidRef, _), (None, assemblyFileName) -> + // When the TP is ran from XS, config.ReferencedAssemblies doesn't contain mscorlib or System.dll + // but from xbuild, it does. Need to investigate why. + let systemPath = Path.GetDirectoryName androidRef + cp.ReferencedAssemblies.Add(Path.Combine(systemPath, "..", "v1.0", assemblyFileName)) |> ignore + | _, _ -> () + + addIfMissingReference system + addIfMissingReference mscorlib + + let result = compiler.CompileAssemblyFromSource(cp, [| sourceCode |]) + if result.Errors.HasErrors then + printfn "%A" result.Errors + failwithf "%A" result.Errors + let asm = Assembly.ReflectionOnlyLoadFrom cp.OutputAssembly + + let types = asm.GetTypes() + let namespaces = + let dict = Dictionary<_,List<_>>() + for t in types do + printfn "%A" t + let namespc = if isNull t.Namespace then "global" else t.Namespace + match dict.TryGetValue(namespc) with + | true, ns -> ns.Add(t) + | _, _ -> + let ns = List<_>() + ns.Add(t) + dict.Add(namespc, ns) + dict + |> Seq.map (fun kv -> + { new IProvidedNamespace with + member x.NamespaceName = kv.Key + member x.GetNestedNamespaces() = [||] //FIXME + member x.GetTypes() = kv.Value.ToArray() + member x.ResolveTypeName(typeName: string) = null + } + ) + |> Seq.toArray + providedAssembly <- Some(File.ReadAllBytes(result.PathToAssembly), namespaces) + + do + watcher.Changed.Add(fun _ -> printfn "Invalidating resources"; invalidate.Trigger(null, null)) + let source = File.ReadAllText(Path.Combine(pathToDesigner, "Resource.designer.fs")) + + AppDomain.CurrentDomain.add_ReflectionOnlyAssemblyResolve(fun _ args -> + let name = AssemblyName(args.Name) + printfn "Resolving %s" args.Name + let existingAssembly = + AppDomain.CurrentDomain.GetAssemblies() + |> Seq.tryFind(fun a -> AssemblyName.ReferenceMatchesDefinition(name, a.GetName())) + let asm = + match existingAssembly with + | Some a -> printfn "Resolved to %s" a.Location + a + | None -> null + asm) + + generate source + + interface ITypeProvider with + [] + member x.Invalidate = invalidate.Publish + member x.GetStaticParameters(typeWithoutArguments) = [||] + member x.GetGeneratedAssemblyContents(assembly) = + match providedAssembly with + | Some(bytes, _) -> bytes + | _ -> failwith "Generate was never called" + member x.GetNamespaces() = + match providedAssembly with + | Some(_, namespaces) -> namespaces + | _ -> failwith "Generate was never called" + member x.ApplyStaticArguments(typeWithoutArguments, typeNameWithArguments, staticArguments) = null + member x.GetInvokerExpression(mb, parameters) = Expr.Call(mb :?> MethodInfo, Array.toList parameters) + member x.Dispose() = + compiler.Dispose() + watcher.Dispose() + +[] +do() \ No newline at end of file diff --git a/Runtime/Provider.fs b/Runtime/Provider.fs new file mode 100644 index 0000000..94440aa --- /dev/null +++ b/Runtime/Provider.fs @@ -0,0 +1,5 @@ +namespace Xamarin.Android.FSharp +open Microsoft.FSharp.Core.CompilerServices + +[] +() diff --git a/Runtime/Resources/Resource.designer.fs b/Runtime/Resources/Resource.designer.fs new file mode 100644 index 0000000..e69de29 diff --git a/Runtime/Xamarin.Android.FSharp.ResourceProvider.Runtime.fsproj b/Runtime/Xamarin.Android.FSharp.ResourceProvider.Runtime.fsproj new file mode 100644 index 0000000..fa8436d --- /dev/null +++ b/Runtime/Xamarin.Android.FSharp.ResourceProvider.Runtime.fsproj @@ -0,0 +1,55 @@ + + + Debug + AnyCPU + {8307D8BE-EFF7-459D-900D-8D2D83C7653B} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{F2A71F9B-5D33-465A-A702-920D77279786} + Library + Xamarin.Android.FSharp + +Xamarin.Android.FSharp.ResourceProvider.Runtime + v7.0 + Resources\Resource.designer.fs + Resource + Resources + Assets + true + + + true + full + false + ..\bin + DEBUG + prompt + None + + arm64-v8a;armeabi;armeabi-v7a;x86 + + + true + pdbonly + true + ..\bin + + prompt + true + false + true + + + + + + ..\packages\FSharp.Core.4.0.0.1\lib\portable-net45+monoandroid10+monotouch10+xamarinios10\FSharp.Core.dll + + + + + + + + + + + diff --git a/Runtime/packages.config b/Runtime/packages.config new file mode 100644 index 0000000..b4e3326 --- /dev/null +++ b/Runtime/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Xamarin.Android.FSharp.ResourceProvider.fsproj b/Xamarin.Android.FSharp.ResourceProvider.fsproj new file mode 100644 index 0000000..3b8c52f --- /dev/null +++ b/Xamarin.Android.FSharp.ResourceProvider.fsproj @@ -0,0 +1,42 @@ + + + + Debug + AnyCPU + {F0B64BEE-BBAA-4A11-94F8-DBE1A9C04D11} + Library + Xamarin.Android.FSharp + Xamarin.Android.FSharp.ResourceProvider + v4.5.1 + + + true + full + false + bin + DEBUG + prompt + + + + true + bin + + prompt + true + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + + + + + diff --git a/Xamarin.Android.FSharp.ResourceProvider.sln b/Xamarin.Android.FSharp.ResourceProvider.sln new file mode 100644 index 0000000..6a0ea2e --- /dev/null +++ b/Xamarin.Android.FSharp.ResourceProvider.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{f2a71f9b-5d33-465a-a702-920d77279786}") = "Xamarin.Android.FSharp.ResourceProvider", "Xamarin.Android.FSharp.ResourceProvider.fsproj", "{F0B64BEE-BBAA-4A11-94F8-DBE1A9C04D11}" +EndProject +Project("{f2a71f9b-5d33-465a-a702-920d77279786}") = "Xamarin.Android.FSharp.ResourceProvider.Runtime", "Runtime\Xamarin.Android.FSharp.ResourceProvider.Runtime.fsproj", "{8307D8BE-EFF7-459D-900D-8D2D83C7653B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F0B64BEE-BBAA-4A11-94F8-DBE1A9C04D11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0B64BEE-BBAA-4A11-94F8-DBE1A9C04D11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0B64BEE-BBAA-4A11-94F8-DBE1A9C04D11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0B64BEE-BBAA-4A11-94F8-DBE1A9C04D11}.Release|Any CPU.Build.0 = Release|Any CPU + {8307D8BE-EFF7-459D-900D-8D2D83C7653B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8307D8BE-EFF7-459D-900D-8D2D83C7653B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8307D8BE-EFF7-459D-900D-8D2D83C7653B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8307D8BE-EFF7-459D-900D-8D2D83C7653B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal