Created
April 20, 2015 16:51
-
-
Save drvink/37782838e3611b5b7a83 to your computer and use it in GitHub Desktop.
F# inline MSBuild tasks via FSharp.Compiler.CodeDom
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Example | |
open System | |
open System.IO | |
open System.Runtime.InteropServices | |
open System.Text | |
open Microsoft.Build.Framework | |
open Microsoft.Build.Utilities | |
module internal Externs = | |
[<DllImport("shlwapi.dll", | |
EntryPoint = "AssocQueryString", | |
CharSet = CharSet.Auto)>] | |
extern int AssocQueryString(int flags, | |
int str, | |
string pszAssoc, | |
string pszExtra, | |
[<Out>] StringBuilder pszOut, | |
[<In; Out>] int& pcchOut) | |
type FindExecutable() = | |
inherit Task() | |
[<Literal>] | |
static let ASSOCF_INIT_BYEXENAME = 0x00000002 | |
[<Literal>] | |
static let ASSOCF_NOTRUNCATE = 0x00000020 | |
[<Literal>] | |
static let ASSOCSTR_EXECUTABLE = 2 | |
static let windir = | |
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Windows) | |
static let sysdir = System.Environment.SystemDirectory | |
static let find_in_paths name paths = | |
let f p = | |
let full_path = Path.Combine(p, name) | |
File.Exists(full_path) | |
match Array.tryFind f paths with | |
| None -> failwith "file not found" | |
| Some x -> x | |
static let find_executable name paths = | |
if File.Exists(name) then Path.GetFullPath(name) else | |
let path = Path.Combine(windir, name) | |
if File.Exists(path) then Path.GetFullPath(path) else | |
let path = Path.Combine(sysdir, name) | |
if File.Exists(path) then Path.GetFullPath(path) else | |
let res = | |
try | |
let envpaths = | |
let ep = System.Environment.GetEnvironmentVariable("PATH") | |
if ep <> null then ep else "" | |
if String.Empty <> envpaths | |
then find_in_paths name (envpaths.Split(';')) | |
else "" | |
with | |
| _ -> "" | |
if String.Empty <> res then res else | |
let extrapaths = if paths <> null | |
then paths | |
else [| TaskItem() :> ITaskItem |] | |
if 0 <> extrapaths.Length then | |
let strpaths = extrapaths |> Array.map (fun l -> l.ItemSpec) | |
find_in_paths name strpaths | |
else failwith "file not found" | |
let mutable _name : string = null | |
let mutable _additional_paths : ITaskItem [] = null | |
let mutable _location : string = null | |
[<Required>] | |
member x.Name with get() = _name and set v = _name <- v | |
member x.AdditionalPaths with get () = _additional_paths | |
and set v = _additional_paths <- v | |
[<Output>] | |
member x.Location with get () = _location and private set v = _location <- v | |
override this.Execute() = | |
let res = | |
try | |
this.Location <- find_executable this.Name this.AdditionalPaths | |
Some true | |
with | |
| _ -> None | |
match res with | |
| Some x -> x | |
| None -> | |
let mutable sz = 0 | |
let hr = | |
Externs.AssocQueryString( | |
ASSOCF_INIT_BYEXENAME ||| ASSOCF_NOTRUNCATE, | |
ASSOCSTR_EXECUTABLE, | |
this.Name, | |
null, | |
null, | |
&sz) | |
if 0 > hr then | |
this.Location <- "" | |
false | |
else | |
let sb = StringBuilder(sz + 1) | |
Externs.AssocQueryString( | |
ASSOCF_INIT_BYEXENAME ||| ASSOCF_NOTRUNCATE, | |
ASSOCSTR_EXECUTABLE, | |
this.Name, | |
null, | |
sb, | |
&sz) |> ignore | |
this.Location <- sb.ToString() | |
true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
C:\Users\msjp_000\Documents\Visual Studio 2013\Projects\tmp>msbuild Test.proj | |
Microsoft (R) Build Engine version 12.0.31101.0 | |
[Microsoft .NET Framework, version 4.0.30319.34209] | |
Copyright (C) Microsoft Corporation. All rights reserved. | |
Build started 2015/04/21 1:49:09. | |
Project "C:\Users\msjp_000\Documents\Visual Studio 2013\Projects\tmp\Test.proj" on node 1 (default targets). | |
Build: | |
C:\Windows\notepad.exe | |
Done Building Project "C:\Users\msjp_000\Documents\Visual Studio 2013\Projects\tmp\Test.proj" (default targets) . | |
Build succeeded. | |
0 Warning(s) | |
0 Error(s) | |
Time Elapsed 00:00:00.68 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
<UsingTask TaskName="Example.FindExecutable" | |
TaskFactory="CodeTaskFactory" | |
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll"> | |
<ParameterGroup> | |
<Name ParameterType="System.String" Required="true" /> | |
<AdditionalPaths ParameterType="Microsoft.Build.Framework.ITaskItem[]" /> | |
<Location ParameterType="System.String" Output="true" /> | |
</ParameterGroup> | |
<Task> | |
<Code Type="Class" Language="f#" Source="$(MSBuildThisFileDirectory)FindExecutable.fs" /> | |
</Task> | |
</UsingTask> | |
<Target Name="Build"> | |
<Example.FindExecutable Name="notepad.exe" ContinueOnError="WarnAndContinue"> | |
<Output PropertyName="NotepadPath" TaskParameter="Location" /> | |
</Example.FindExecutable> | |
<Message Text="$(NotepadPath)" Importance="High" /> | |
</Target> | |
</Project> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment