Skip to content

Commit

Permalink
Merge pull request #2131 from fsharp/process_api_hook
Browse files Browse the repository at this point in the history
Process api hook
  • Loading branch information
matthid authored Oct 10, 2018
2 parents 718e065 + 0f26575 commit e9b8834
Show file tree
Hide file tree
Showing 23 changed files with 1,869 additions and 705 deletions.
6 changes: 6 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ If available, link to an existing issue this PR fixes. For example:

## TODO

Feel free to open the PR and ask for help

- [] New (API-)documentation for new features exist (Note: API-docs are enough, additional docs are in `help/markdown`)
- [] unit or integration test exists (or short reasoning why it doesn't make sense)

> Note: Consider using the `CreateProcess` API which can be tested more easily, see https://github.com/fsharp/FAKE/pull/2131/files#diff-4fb4a77e110fbbe8210205dfe022389b for an example (the changes in the `DotNet.Testing.NUnit` module)
- [] boy scout rule: "leave the code behind in a better state than you found it" (fix warnings, obsolete members or code-style in the places you worked in)
- [] (if new module) the module has been linked from the "Modules" menu, edit `help/templates/template.cshtml`, linking to the API-reference is fine.
- [] (if new module) the module is in the correct namespace
- [] (if new module) the module is added to Fake.sln (`dotnet sln Fake.sln add src/app/Fake.*/Fake.*.fsproj`)
Expand Down
23 changes: 17 additions & 6 deletions src/app/Fake.Core.Context/Context.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ type internal RuntimeContextWrapper(t: RuntimeContext) =
inherit System.MarshalByRefObject()
#endif
member x.Type = t
override x.ToString() =
match t with
| Fake f -> sprintf "Wrapper(ScriptFile=%s)" f.ScriptFile
| UnknownObj o -> sprintf "Wrapper(UnknownObj=%O)" o
| Unknown -> sprintf "Wrapper(Unknown)"


#if USE_ASYNC_LOCAL
open System.Threading
Expand All @@ -54,6 +60,7 @@ let private getDataDict() =
#endif

let private setContext (name:string) (o : obj) : unit =
//printfn "set context '%s' -> %A, threadId '%d'" name o System.Threading.Thread.CurrentThread.ManagedThreadId
#if USE_ASYNC_LOCAL
let d = getDataDict()
d.AddOrUpdate(name, o, fun _ old -> o) |> ignore
Expand All @@ -62,15 +69,17 @@ let private setContext (name:string) (o : obj) : unit =
#endif

let private getContext (name:string) : obj =
let result =
#if USE_ASYNC_LOCAL
let d = getDataDict()
match d.TryGetValue(name) with
| true, v -> v
| false, _ -> null
let d = getDataDict()
match d.TryGetValue(name) with
| true, v -> v
| false, _ -> null
#else
System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(name)
System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(name)
#endif

//printfn "get context '%s' -> '%A', threadId '%d'" name result System.Threading.Thread.CurrentThread.ManagedThreadId
result
let private fake_ExecutionType = "fake_context_execution_type"

let getExecutionContext () =
Expand All @@ -81,6 +90,8 @@ let getExecutionContext () =

let setExecutionContext (e:RuntimeContext) = setContext fake_ExecutionType (new RuntimeContextWrapper(e))

let removeExecutionContext () = setContext fake_ExecutionType null

let getFakeExecutionContext (e:RuntimeContext) =
match e with
| RuntimeContext.UnknownObj _
Expand Down
69 changes: 43 additions & 26 deletions src/app/Fake.Core.Process/CmdLineParsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module internal CmdLineParsing =
let escapeCommandLineForShell (cmdLine:string) =
sprintf "'%s'" (cmdLine.Replace("'", "'\\''"))
let windowsArgvToCommandLine args =
let windowsArgvToCommandLine shorten args =
let escapeBackslashes (sb:System.Text.StringBuilder) (s:string) (lastSearchIndex:int) =
// Backslashes must be escaped if and only if they precede a double quote.
[ lastSearchIndex .. -1 .. 0]
Expand All @@ -14,25 +14,29 @@ module internal CmdLineParsing =

let sb = new System.Text.StringBuilder()
for (s:string) in args do
sb.Append('"') |> ignore
// Escape double quotes (") and backslashes (\).
let mutable searchIndex = 0

// Put this test first to support zero length strings.
let mutable quoteIndex = 0
while searchIndex < s.Length && quoteIndex >= 0 do
if shorten && s.Length > 0 && s.IndexOfAny([|' '; '\"'; '\\'; '\t'|]) < 0 then
sb.Append s |> ignore
sb.Append " " |> ignore
else
sb.Append('"') |> ignore
// Escape double quotes (") and backslashes (\).
let mutable searchIndex = 0

// Put this test first to support zero length strings.
let mutable quoteIndex = 0
while searchIndex < s.Length && quoteIndex >= 0 do

quoteIndex <- s.IndexOf('"', searchIndex)
if quoteIndex >= 0 then
sb.Append(s, searchIndex, quoteIndex - searchIndex) |> ignore
escapeBackslashes sb s (quoteIndex - 1)
sb.Append('\\') |> ignore
sb.Append('"') |> ignore
searchIndex <- quoteIndex + 1

sb.Append(s, searchIndex, s.Length - searchIndex) |> ignore
escapeBackslashes sb s (s.Length - 1)
sb.Append(@""" ") |> ignore
quoteIndex <- s.IndexOf('"', searchIndex)
if quoteIndex >= 0 then
sb.Append(s, searchIndex, quoteIndex - searchIndex) |> ignore
escapeBackslashes sb s (quoteIndex - 1)
sb.Append('\\') |> ignore
sb.Append('"') |> ignore
searchIndex <- quoteIndex + 1
sb.Append(s, searchIndex, s.Length - searchIndex) |> ignore
escapeBackslashes sb s (s.Length - 1)
sb.Append(@""" ") |> ignore

sb.ToString(0, System.Math.Max(0, sb.Length - 1))

Expand Down Expand Up @@ -100,23 +104,27 @@ module internal CmdLineParsing =
results.ToArray()

let toProcessStartInfo args =
let cmd = windowsArgvToCommandLine args
let cmd = windowsArgvToCommandLine true args
if Environment.isMono && Environment.isLinux then
// See https://bugzilla.xamarin.com/show_bug.cgi?id=19296
cmd.Replace("\\$", "\\\\$").Replace("\\`", "\\\\`")
else cmd

type FilePath = string

/// Helper functions for proper command line parsing
module Args =
let toWindowsCommandLine args = CmdLineParsing.windowsArgvToCommandLine args
/// Convert the given argument list to a conforming windows command line string, escapes parameter in quotes if needed (currently always but this might change).
let toWindowsCommandLine args = CmdLineParsing.windowsArgvToCommandLine true args
/// Escape the given argument list according to a unix shell (bash)
let toLinuxShellCommandLine args =
System.String.Join(" ", args |> Seq.map CmdLineParsing.escapeCommandLineForShell)

/// Read a windows command line string into its arguments
let fromWindowsCommandLine cmd = CmdLineParsing.windowsCommandLineToArgv cmd


/// Represents a list of arguments
type Arguments =
{ Args : string array }
internal { Args : string array }
static member Empty = { Args = [||] }
/// See https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
static member OfWindowsCommandLine cmd =
Expand All @@ -126,6 +134,15 @@ type Arguments =
member x.ToWindowsCommandLine = Args.toWindowsCommandLine x.Args
member x.ToLinuxShellCommandLine = Args.toLinuxShellCommandLine x.Args

static member OfArgs args = { Args = args }
/// Create a new arguments object from the given list of arguments
static member OfArgs (args:string seq) = { Args = args |> Seq.toArray }
/// Create a new arguments object from a given startinfo-conforming-escaped command line string.
static member OfStartInfo cmd = Arguments.OfWindowsCommandLine cmd
member internal x.ToStartInfo = CmdLineParsing.toProcessStartInfo x.Args
/// Create a new command line string which can be used in a ProcessStartInfo object.
member x.ToStartInfo = CmdLineParsing.toProcessStartInfo x.Args

module Arguments =
let withPrefix s (a:Arguments) =
Arguments.OfArgs(Seq.append s a.Args)
let append s (a:Arguments) =
Arguments.OfArgs(Seq.append a.Args s)
Loading

0 comments on commit e9b8834

Please sign in to comment.