Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process api hook #2131

Merged
merged 16 commits into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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