Skip to content

Commit

Permalink
feature/fsharp bindings (#7)
Browse files Browse the repository at this point in the history
* Introduce F# CE bindings through Proc.Fs

* update build scripts

* update build scripts

* update build scripts

* update build scripts

* update tests

* update waittime

* update waittime

* update build tools

* mark ScratchPad.Fs as unpackable

* ensure we check for build info in InformationalVersion

* update minver reference
  • Loading branch information
Mpdreamz authored Jan 5, 2024
1 parent 53bdb39 commit fa4678d
Show file tree
Hide file tree
Showing 22 changed files with 335 additions and 65 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:

jobs:
build:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
Expand All @@ -30,7 +30,7 @@ jobs:
dotnet-version: '3.1.201'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.100'
dotnet-version: '8.0.100'
source-url: https://nuget.pkg.github.com/nullean/index.json
env:
NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MinVerDefaultPreReleasePhase>canary</MinVerDefaultPreReleasePhase>
<MinVerDefaultPreReleaseIdentifiers>canary.0</MinVerDefaultPreReleaseIdentifiers>
<MinVerMinimumMajorMinor>0.2</MinVerMinimumMajorMinor>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MinVer" Version="2.3.1">
<PackageReference Include="MinVer" Version="4.3.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Expand Down
10 changes: 5 additions & 5 deletions build/scripts/CommandLine.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ with
interface IArgParserTemplate with
member this.Usage =
match this with
| Clean _ -> "clean known output locations"
| Build _ -> "Run build"
| Test _ -> "Runs build then tests"
| Release _ -> "runs build, tests, and create and validates the packages shy of publishing them"
| Publish _ -> "Runs the full release"
| Clean -> "clean known output locations"
| Build -> "Run build"
| Test -> "Runs build then tests"
| Release -> "runs build, tests, and create and validates the packages shy of publishing them"
| Publish -> "Runs the full release"

| SingleTarget _ -> "Runs the provided sub command without running their dependencies"
| Token _ -> "Token to be used to authenticate with github"
Expand Down
2 changes: 1 addition & 1 deletion build/scripts/Paths.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let MainTFM = "netstandard2.0"
let SignKey = "96c599bbe3e70f5d"

let ValidateAssemblyName = true
let IncludeGitHashInInformational = false
let IncludeGitHashInInformational = true
let GenerateApiChanges = true

let Root =
Expand Down
2 changes: 1 addition & 1 deletion build/scripts/Targets.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let private restoreTools = lazy(exec "dotnet" ["tool"; "restore"])
let private currentVersion =
lazy(
restoreTools.Value |> ignore
let r = Proc.Start("dotnet", "minver", "-d", "canary", "-m", "0.1")
let r = Proc.Start("dotnet", "minver", "-p", "canary.0", "-m", "0.1")
let o = r.ConsoleOut |> Seq.find (fun l -> not(l.Line.StartsWith("MinVer:")))
o.Line
)
Expand Down
2 changes: 1 addition & 1 deletion build/scripts/scripts.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
"isRoot": true,
"tools": {
"minver-cli": {
"version": "2.3.1",
"version": "4.3.0",
"commands": [
"minver"
]
},
"release-notes": {
"version": "0.3.0",
"version": "0.6.0",
"commands": [
"release-notes"
]
},
"nupkg-validator": {
"version": "0.4.0",
"version": "0.6.0",
"commands": [
"nupkg-validator"
]
},
"assembly-differ": {
"version": "0.13.0",
"version": "0.15.0",
"commands": [
"assembly-differ"
]
Expand Down
19 changes: 19 additions & 0 deletions examples/ScratchPad.Fs/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
open Proc.Fs

let uname = shell {
exec "dotnet" "--version"
exec "uname"
}

let dotnetVersion = exec {
binary "dotnet"
args "--help"
filter (fun l -> l.Line.Contains "clean")
}

printfn "Found lines %i" dotnetVersion.Length

let dotnetRestoreHelp = exec {
binary "dotnet"
invoke_args ["restore"; "--help"]
}
16 changes: 16 additions & 0 deletions examples/ScratchPad.Fs/ScratchPad.Fs.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Proc.Fs\Proc.Fs.fsproj"/>
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion examples/ScratchPad/ScratchPad.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion examples/ScratchPad/TestBinary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static StartArguments TestCaseArguments(string testcase) =>

private static string GetDll()
{
var dll = Path.Combine("bin", GetRunningConfiguration(), "net5.0", _procTestBinary + ".dll");
var dll = Path.Combine("bin", GetRunningConfiguration(), "net8.0", _procTestBinary + ".dll");
var fullPath = Path.Combine(GetWorkingDir(), dll);
if (!File.Exists(fullPath)) throw new Exception($"Can not find {fullPath}");

Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "5.0.100",
"version": "8.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
}
Expand Down
14 changes: 14 additions & 0 deletions proc.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{E4B3DD3A-E36C-46D6-B35E-D824EB9B3C06}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Proc.Fs", "src\Proc.Fs\Proc.Fs.fsproj", "{5EA4E26F-F623-473D-9CD7-E590A3E54239}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ScratchPad.Fs", "examples\ScratchPad.Fs\ScratchPad.Fs.fsproj", "{C33E3F7C-0C2A-4DD2-91E4-328866195997}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -67,6 +71,14 @@ Global
{D6997ADC-E933-418E-831C-DE1A78897493}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6997ADC-E933-418E-831C-DE1A78897493}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6997ADC-E933-418E-831C-DE1A78897493}.Release|Any CPU.Build.0 = Release|Any CPU
{5EA4E26F-F623-473D-9CD7-E590A3E54239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EA4E26F-F623-473D-9CD7-E590A3E54239}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EA4E26F-F623-473D-9CD7-E590A3E54239}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EA4E26F-F623-473D-9CD7-E590A3E54239}.Release|Any CPU.Build.0 = Release|Any CPU
{C33E3F7C-0C2A-4DD2-91E4-328866195997}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C33E3F7C-0C2A-4DD2-91E4-328866195997}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C33E3F7C-0C2A-4DD2-91E4-328866195997}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C33E3F7C-0C2A-4DD2-91E4-328866195997}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -78,5 +90,7 @@ Global
{D3AFFBE0-8F40-42C7-8A6A-BCCD2EDF4A3E} = {0F267D58-B5AA-4D04-B346-12B283E37B68}
{E7AD2461-309A-4BA7-A6EB-5C1F4F882BC2} = {E4B3DD3A-E36C-46D6-B35E-D824EB9B3C06}
{D6997ADC-E933-418E-831C-DE1A78897493} = {E89606EC-111B-4151-997C-8006627F1926}
{5EA4E26F-F623-473D-9CD7-E590A3E54239} = {9C336E9A-3FC8-4F77-A5B4-1D07E4E54924}
{C33E3F7C-0C2A-4DD2-91E4-328866195997} = {E4B3DD3A-E36C-46D6-B35E-D824EB9B3C06}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion src/Proc.ControlC/Proc.ControlC.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net40" Version="1.0.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net40" Version="1.0.3" PrivateAssets="all"/>
</ItemGroup>
</Project>
183 changes: 183 additions & 0 deletions src/Proc.Fs/Bindings.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
module Proc.Fs

open System
open ProcNet
open ProcNet.Std

let execWithTimeout binary args timeout =
let opts =
ExecArguments(binary, args |> List.map (sprintf "\"%s\"") |> List.toArray)
let options = args |> String.concat " "
printfn ":: Running command: %s %s" binary options
let r = Proc.Exec(opts, timeout)

match r.HasValue with
| true -> r.Value
| false -> failwithf "invocation of `%s` timed out" binary

///executes
let exec2 (binary: string) (args: string list): int =
execWithTimeout binary args (TimeSpan.FromMinutes 10)

let private redirected (binary: string) (args: string list) : ProcessCaptureResult =
Proc.Start(binary, args |> Array.ofList)

type RunningStatus = {
LastExitCode: int
GrepOutput: Std.LineOut list option
}
type ShellBuilder() =

member t.Yield _ = None

[<CustomOperation("exec")>]
member inline this.ExecuteWithArguments(status, binary, [<ParamArray>] args: string array) =
let exitCode = exec2 binary (args |> List.ofArray)
match status with
| None ->
Some { LastExitCode = exitCode; GrepOutput = None }
| Some s ->
Some { s with LastExitCode = exitCode }

[<CustomOperation("grep")>]
member this.Grep(status, searchForRe, binary, [<ParamArray>] args: string array) =
let r = Proc.Start(binary, args)
let o =
r.ConsoleOut
|> Seq.filter (_.Line.Contains(searchForRe))
|> List.ofSeq

match status with
| None ->
Some { LastExitCode = 0; GrepOutput = Some o }
| Some s ->
Some { LastExitCode = 0; GrepOutput = Some o }

let shell = ShellBuilder()

type ExecOptions = {
Binary: string
Arguments: string list option
LineOutFilter: (LineOut -> bool) option
Find: (LineOut -> bool) option
WorkingDirectory: string option
Environment: Map<string, string> option

Timeout: TimeSpan option

ValidExitCodeClassifier: (int -> bool) option

NoWrapInThread: bool option
SendControlCFirst: bool option
WaitForStreamReadersTimeout: TimeSpan option
}

type ExecBuilder() =

let startArgs (opts: ExecOptions) =
let startArguments = StartArguments(opts.Binary, opts.Arguments |> Option.defaultValue [])
opts.LineOutFilter |> Option.iter(fun f -> startArguments.LineOutFilter <- f)
opts.Environment |> Option.iter(fun e -> startArguments.Environment <- e)
opts.WorkingDirectory |> Option.iter(fun d -> startArguments.WorkingDirectory <- d)
opts.NoWrapInThread |> Option.iter(fun b -> startArguments.NoWrapInThread <- b)
opts.SendControlCFirst |> Option.iter(fun b -> startArguments.SendControlCFirst <- b)
opts.WaitForStreamReadersTimeout |> Option.iter(fun t -> startArguments.WaitForStreamReadersTimeout <- t)
startArguments

let execArgs (opts: ExecOptions) =
let execArguments = ExecArguments(opts.Binary, opts.Arguments |> Option.defaultValue [])
opts.Environment |> Option.iter(fun e -> execArguments.Environment <- e)
opts.WorkingDirectory |> Option.iter(fun d -> execArguments.WorkingDirectory <- d)
opts.ValidExitCodeClassifier |> Option.iter(fun f -> execArguments.ValidExitCodeClassifier <- f)
execArguments

member t.Yield _ =
{
Binary = ""; Arguments = None; Find = None;
LineOutFilter = None; WorkingDirectory = None; Environment = None
Timeout = None
ValidExitCodeClassifier = None;
NoWrapInThread = None; SendControlCFirst = None; WaitForStreamReadersTimeout = None;
}

[<CustomOperation("binary")>]
member inline this.Binary(opts, binary) =
{ opts with Binary = binary }

[<CustomOperation("args")>]
member inline this.Arguments(opts, [<ParamArray>] args: string array) =
{ opts with Arguments = Some (args |> List.ofArray) }

[<CustomOperation("args")>]
member inline this.Arguments(opts, args: string list) =
{ opts with Arguments = Some args}

[<CustomOperation("workingDirectory")>]
member inline this.WorkingDirectory(opts, workingDirectory: string) =
{ opts with WorkingDirectory = Some workingDirectory }

[<CustomOperation("env")>]
member inline this.EnvironmentVariables(opts, env: Map<string, string>) =
{ opts with Environment = Some env }

[<CustomOperation("timeout")>]
member inline this.Timeout(opts, timeout) =
{ opts with Timeout = Some timeout }

[<CustomOperation("stream_reader_wait_timeout")>]
member inline this.WaitForStreamReadersTimeout(opts, timeout) =
{ opts with WaitForStreamReadersTimeout = Some timeout }

[<CustomOperation("send_control_c")>]
member inline this.SendControlCFirst(opts, sendControlCFirst) =
{ opts with SendControlCFirst = Some sendControlCFirst }

[<CustomOperation("thread_wrap")>]
member inline this.NoWrapInThread(opts, threadWrap) =
{ opts with NoWrapInThread = Some (not threadWrap) }

[<CustomOperation("filterOutput")>]
member inline this.FilterOutput(opts, find: LineOut -> bool) =
{ opts with LineOutFilter = Some find }

[<CustomOperation("validExitCode")>]
member inline this.ValidExitCode(opts, exitCodeClassifier: int -> bool) =
{ opts with ValidExitCodeClassifier = Some exitCodeClassifier }

[<CustomOperation("find")>]
member this.Find(opts, find: LineOut -> bool) =
let opts = { opts with Find = Some find }
let startArguments = startArgs opts
let result = Proc.Start(startArguments)
result.ConsoleOut
|> Seq.find find

[<CustomOperation("filter")>]
member this.Filter(opts, find: LineOut -> bool) =
let opts = { opts with Find = Some find }
let startArguments = startArgs opts
let result = Proc.Start(startArguments)
result.ConsoleOut
|> Seq.filter find
|> List.ofSeq

[<CustomOperation("invoke_args")>]
member this.InvokeArgs(opts, [<ParamArray>] args: string array) =
let opts = { opts with Arguments = Some (args |> List.ofArray) }
let execArgs = execArgs opts
Proc.Exec(execArgs)

[<CustomOperation("invoke_args")>]
member this.InvokeArgs(opts, args: string list) =
let opts = { opts with Arguments = Some args}
let execArgs = execArgs opts
Proc.Exec(execArgs)

[<CustomOperation("invoke")>]
member this.Invoke(opts) =
let execArgs = execArgs opts
Proc.Exec(execArgs)

let exec = ExecBuilder()


Loading

0 comments on commit fa4678d

Please sign in to comment.