Skip to content

Instantly share code, notes, and snippets.

@nrolland
Created August 18, 2012 15:19
Show Gist options
  • Save nrolland/3387537 to your computer and use it in GitHub Desktop.
Save nrolland/3387537 to your computer and use it in GitHub Desktop.
Script for loading dependencies in fsx file from a solution/project
//This script generates
//a file named __project.fsx, for each proejct which can be #load "__project.fsx" in script intending to use the same dependency graph as the code in VS
//a file named __solmerged.fsx, at the solution root which can be #load "__solmerged.fsx" in script intending to use the same dependency graph as the code in VS
//In both cases, this enforce that a script compiling in VS should work from within FSI
#if INTERACTIVE
#r "System.Xml"
#r "System.Xml.Linq"
#endif
open System
open System.IO
open System.Xml.Linq
let rec findsolutiondir (p:DirectoryInfo) = if (p.GetFiles("*.sln") |> Array.length > 0) then p
else findsolutiondir p.Parent
let root = findsolutiondir (DirectoryInfo(__SOURCE_DIRECTORY__))
type Dependency =
| GacLocation of String
| ProjectFile of DirectoryInfo
| DllFile of DirectoryInfo
let refsforaproject (dirproject:DirectoryInfo) = [
for fsProjFile in dirproject.GetFiles("*.fsproj") do
let getElemName name = XName.Get(name, "http://schemas.microsoft.com/developer/msbuild/2003")
let getElemValue name (parent:XElement) =
let elem = parent.Element(getElemName name)
if elem = null || String.IsNullOrEmpty elem.Value then None else Some(elem.Value)
let getAttrValue name (elem:XElement) =
let attr = elem.Attribute(XName.Get name)
if attr = null || String.IsNullOrEmpty attr.Value then None else Some(attr.Value)
let (|??) (option1: 'a Option) option2 =
if option1.IsSome then option1 else option2
let fsProjFile = dirproject.GetFiles("*.fsproj") |> Seq.head
let fsProjXml = XDocument.Load fsProjFile.FullName
let refspath =
fsProjXml.Document.Descendants(getElemName "Reference")
|> Seq.choose (fun elem -> getElemValue "HintPath" elem)
|> Seq.map (fun ref -> //if dirproject.Name.Contains("divsharp") then printfn "fulname : %A" ( DirectoryInfo(dirproject.FullName + "\\" + ref).FullName)
DllFile(DirectoryInfo(dirproject.FullName + "\\" + ref)) )
//("#r ", true, DirectoryInfo(dirproject.FullName + "\\" + ref).FullName) )
let refsgac =
fsProjXml.Document.Descendants(getElemName "Reference")
|> Seq.choose (fun elem -> if (getElemValue "HintPath" elem).IsNone then getAttrValue "Include" elem else None)
|> Seq.map (fun ref -> GacLocation(ref))
let fsFiles =
fsProjXml.Document.Descendants(getElemName "Compile")
|> Seq.choose (fun elem -> //printfn "%A" elem
getAttrValue "Include" elem)
|> Seq.map (fun fsFile -> ProjectFile( DirectoryInfo(dirproject.FullName + "\\" + fsFile)))
//#load ", true, DirectoryInfo(dirproject.FullName + "\\" + fsFile).FullName))
let projDll =
fsProjXml.Document.Descendants(getElemName "ProjectReference")
|> Seq.choose (fun elem -> getAttrValue "Include" elem)
|> Seq.map (fun projFile -> let refedPrjDir = DirectoryInfo(dirproject.FullName + "\\" + projFile).Parent
//("#r " , true, refedPrjDir.FullName + "\\bin\\Debug\\" + refedPrjDir.Name + ".dll")) //refedPrjDir.Name -> assembly name
DllFile(DirectoryInfo(refedPrjDir.FullName + "\\bin\\Debug\\" + refedPrjDir.Name + ".dll")))
yield! refspath
yield! refsgac
yield! projDll
yield! fsFiles
]
let toabsolute root rel = DirectoryInfo(root + rel).FullName
let f n = String.concat "." (Array.create (n+1) ".\\")
let writerelative root path =
let rec intwriterelative (root:DirectoryInfo) (path:DirectoryInfo) n =
if path.FullName.Contains(root.FullName + "\\" ) then f n + path.FullName.Remove(0,root.FullName.Length + 1) //most common acestor = root
else intwriterelative root.Parent path (n+1)
intwriterelative root path 0
let getprojectdir (root:DirectoryInfo) =
let rec getdirs (root:DirectoryInfo) = seq {
yield! root.GetDirectories() |> Array.filter(fun f -> f.GetFiles("*.fsproj") |> Array.length > 0 )
yield! root.GetDirectories() |> Array.map(fun d -> getdirs d) |> Seq.concat}
getdirs root
let orderKey = function | GacLocation(n) -> "A" + n
| ProjectFile(n) -> "C" + n.FullName
| DllFile(n) -> "B" + n.FullName
let tostrings rootwrite (dependencies) =
dependencies |> Seq.distinct
|> Seq.map (fun dep -> match dep with
| GacLocation(n) -> "#r \"" + n + "\""
| ProjectFile(n) -> "#load @\"" + writerelative rootwrite n + "\""
| DllFile(n) -> "#r @\"" + writerelative rootwrite n + "\"")
do
let projects = getprojectdir root |> Seq.map(fun p -> p, refsforaproject p)
projects |> Seq.iter (fun (p,ds) -> File.WriteAllLines(p.FullName+ "\\" + "__project.fsx", tostrings p (ds |> Seq.sortBy orderKey)))
let s = projects |> Seq.map ( fun (p, dep) -> seq{ yield sprintf "//project %A" p.Name
yield! dep |> Seq.sortBy orderKey |> tostrings root } )
|> Seq.concat
File.WriteAllLines(root.FullName+ "\\" + "__solmerged.fsx", s)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment