Shell.Exec in FAKE - f#

I found this function Exec here http://fsharp.github.io/FAKE/apidocs/fake-processhelper-shell.html.
Target "UpdateTools" (fun _ ->
Exec "cmd"
)
But I keep getting this error, when I try to run it: "The value or constructor 'Exec' is not defined".
I'm new to FAKE and have not used F#, so forgive me if this should be obvious.
Can someone tell me why this api is not accessible like that?

The documentation is documenting class Shell. That means, you need to call it like:
Target "UpdateTools" (fun _ ->
ignore(Shell.Exec "cmd")
)
or, if you need to work with the error code further:
Target "UpdateTools" (fun _ ->
let errorCode = Shell.Exec "cmd"
//do something with the error code
()
)
Hope it is a bit clearer now.

Related

How can I get Dialyzer to accept a call to a function that intentionally throws?

I have a function that intentionally throws when its first argument is the atom throw.
A simplified version of this code is:
-module(sample).
-export([main/1, throw_or_ok/1]).
main(_Args) ->
throw_or_ok(throw).
throw_or_ok(Action) ->
case Action of
throw -> throw("throwing");
ok -> ok
end.
Dialyzer errors on the call to throw_or_ok:
sample.erl:7: The call sample:throw_or_ok
('throw') will never return since it differs in the 1st argument from the success typing arguments:
('ok')
Adding specs doesn't help, the error message is the same:
-module(sample).
-export([main/1, throw_or_ok/1]).
-spec main(_) -> no_return().
main(_Args) ->
throw_or_ok(throw).
-spec throw_or_ok(throw) -> no_return(); (ok) -> ok.
throw_or_ok(Action) ->
case Action of
throw -> throw("throwing");
ok -> ok
end.
How can I get Dialyzer to accept calls to throw_or_ok/1 that are guaranteed to throw?
Unfortunately there is currently no clean way to mark this as acceptable for Dialyzer via specs.
Perhaps you can use an ignore warning annotation, however.
Looks like if will put throw it will never return, if will put ok the pattern will never match with throw. See topic with similar issue. The logic of main/1 need to change, eg:
main(Args) ->
MaybeOk = case Args of
0 -> throw;
_ -> ok
end,
throw_or_ok(MaybeOk).
OR
main(_Args) ->
throw_or_ok(_Args).

Can't get OpenCover to work in fake

EDITED to show the ignore return as pointed out by Fyodor and the resulting error
I have a .fsx file with several targets that work as expected, but I can't get a target for OpenCover to work. This is what I have for the Target code:
Target "Coverage" (fun _ ->
OpenCover
(fun p -> { p with ExePath = "./packages/OpenCover.4.6.519/tools/OpenCover.Console.exe"
TestRunnerExePath = "./packages/Machine.Specifications.Runner.Console.0.10.0-Unstable0005/tools/mspec-clr4.exe"
Output = reportDir + "MspecOutput.xml"
Register = "-register:user"
}
)
testDir ## "FakeTest2UnitTesting.dll" + "--xml " + reportDir + "MspecOutput.xml" |> ignore
)
But I now get the following build error:
build.fsx(45,3): error FS0039: The value or constructor 'OpenCover' is not defined. Maybe you want one of the following:
OpenCoverHelper
NCover
I don't know what I am doing wrong. Can someone show me how to use the OpenCoverHelper from the FAKE API?
Thanks
After a lot of playing around an googling, I finally came up with the solution. The basic problem was that I didn't open the OpenCoverHelper. I made the assumption that it was included in FAKE as it is in the Api and there was no documentation saying anything else. So, here is the code I use:
// include Fake lib
#r #"packages/FAKE.4.61.2/tools/FakeLib.dll"
open Fake
open Fake.OpenCoverHelper
Target "Coverage" (fun _ ->
OpenCover (fun p -> { p with
ExePath = "./packages/OpenCover.4.6.519/tools/OpenCover.Console.exe"
TestRunnerExePath = "./packages/Machine.Specifications.Runner.Console.0.10.0-Unstable0005/tools/mspec-clr4.exe"
Output = "./report/MspecOutput.xml"
Register = RegisterUser
})
"./test/FakeTest2UnitTesting.dll + --xml ./report/MspecOutput.xml"
)
Hopefully this will help someone in the future.

Error Installing a Windows Service (in F#)

my question is the following:
When I try to install my Windows Service I get the following error:
snippet:
...
No public installers with the RunInstallerAttribute.Yes attribute could be found in the <path to exe> assembly.
...
I follow this tutorial
I have one Program.fs file containing:
[<RunInstaller(true)>]
type public FSharpServiceInstaller() =
inherit Installer()
do
< some logic, doesn't really matter >
This should be sufficient, as a matter of fact, I don't even think I need to add the public keyword to the type definition. Installing this executable with InstallUtil.exe gives me the same error as installing it using the following code:
[<EntryPoint>]
let main args =
if Environment.UserInteractive then
let parameter = String.Concat(args);
match parameter with
| "-i" -> ManagedInstallerClass.InstallHelper [| Assembly.GetExecutingAssembly().Location |]
| "-u" -> ManagedInstallerClass.InstallHelper [| "/u"; Assembly.GetExecutingAssembly().Location |]
| _ -> printf "Not allowed!\n"
else
ServiceBase.Run [| new CreditToolsService() :> ServiceBase |];
0
I have tried running this script in PowerShell, cmd and Visual Studio CLI as both administrator and my normal account but I keep getting the same error. If anyone knows what I'm doing wrong I would really appreciate some help.
OK, so here goes...
I've looked at the code provided by user1758475 and just randomly started copy pasting solutions into an application. Don Symes's solution "just worked" and I finally figured out why: I did not (and he does) have a namespace declaration, in my source. Seems like this was the culprit! After I added the namespace the installer worked like a charm.
As Curt Nichols pointed out, the installer should not be in a module because a module effectively hides the type from the calling code.
Thank you for help in figuring this out.
For those of you who want to see a working example:
namespace FileWatcher
open System
open System.Reflection
open System.ComponentModel
open System.Configuration.Install
open System.ServiceProcess
open System.IO
open System.Configuration
type FileWatcherService() =
inherit ServiceBase(ServiceName = "FileWatcher")
let createEvent = fun (args: FileSystemEventArgs) ->
printf "%s has been %s\n" args.FullPath (args.ChangeType.ToString().ToLower())
|> ignore
override x.OnStart(args) =
let fsw = new FileSystemWatcher ()
fsw.Path <- "C:\TEMP"
fsw.NotifyFilter <- NotifyFilters.LastAccess ||| NotifyFilters.LastWrite ||| NotifyFilters.FileName ||| NotifyFilters.DirectoryName ||| NotifyFilters.CreationTime
fsw.Filter <- "*.txt"
fsw.EnableRaisingEvents <- true
fsw.IncludeSubdirectories <- true
fsw.Created.Add(createEvent)
override x.OnStop() =
printf "Stopping the FileWatcher service"
[<RunInstaller(true)>]
type public FSharpServiceInstaller() =
inherit Installer()
do
// Specify properties of the hosting process
new ServiceProcessInstaller
(Account = ServiceAccount.LocalSystem)
|> base.Installers.Add |> ignore
// Specify properties of the service running inside the process
new ServiceInstaller
( DisplayName = "AAA FileWatcher Service",
ServiceName = "AAAFileWatcherService",
StartType = ServiceStartMode.Automatic )
|> base.Installers.Add |> ignore
module Program =
[<EntryPoint>]
let main args =
printf "starting the application...\n"
if Environment.UserInteractive then
let parameter = String.Concat(args);
match parameter with
| "-i" -> ManagedInstallerClass.InstallHelper [| Assembly.GetExecutingAssembly().Location |]
| "-u" -> ManagedInstallerClass.InstallHelper [| "/u"; Assembly.GetExecutingAssembly().Location |]
| _ -> printf "Not allowed!\n"
else
ServiceBase.Run [| new FileWatcherService() :> ServiceBase |];
0
Working live production example at https://github.com/zbilbo/TB4TG/blob/master/TourneyBot.Service/Installer.fs
Think it needs to be installed with InstallUtil.exe though.
Possibly not the finest moment in coding, but that specific service code is from Don Syme more or less: http://blogs.msdn.com/b/dsyme/archive/2011/05/31/a-simple-windows-service-template-for-f.aspx, so it is probably fine, but the rest of the "surrounding" code on that repository may not be idiomatic ;-)
Don Symes blog also explains a lot more so it should be easily to adept it to your needs. It also links to a Win Service Template on VS Gallery: http://blogs.msdn.com/b/mcsuksoldev/archive/2011/05/31/f-windows-application-template-for-windows-service.aspx

What am I doing wrong with erl_parse:parse_form?

I wrote a Hello-World module and compiled it successfully. And then I tried to learn the things under the hood by using erl_scan and erl_parse.
-module(useless).
-export([hello/0]).
hello() -> io:format("hello world\n").
and I type in the erl shell
{ok, S} = file:read_file("useless.erl").
and
{ok, T, _} = erl_scan:string(binary_to_list(S)).
It works fine apparently. But when I try erl_parse:parse_form(T).
It gives {error,{2,erl_parse,["syntax error before: ","'-'"]}}
What am I doing wrong?
Edit:
the module compile is helpful.
Also this is cool.
The function erl_parse:parse_form/1 works on one form only. So you must split the result of erl_scan:string/1 into individual forms first.
you can use erl_scan:tokens to achieve this (this code wors, but I am not sure I use the function in the right way):
-module(useless).
-export([eval/1]).
eval(File) ->
{ok, B} = file:read_file(File),
Forms = scan(erl_scan:tokens([],binary_to_list(B),1),[]),
F = fun(X) -> {ok,Y} = erl_parse:parse_form(X), Y end,
[F(X) || X <- Forms].
scan({done,{ok,T,N},S},Res) ->
scan(erl_scan:tokens([],S,N),[T|Res]);
scan(_,Res) ->
lists:reverse(Res).
Robert suggestion is to use the re-entrant feature of erl_scan:tokens/3.
The docs on this function are not explicit and it took me some time to understand that the initial string needed to be closed by eof. (if not the tokens function runs an endless loop).
Here is the code I finally came up with, I hope that will help others.
eval(File)->
{ok, Data} = file:read_file(File),
String=erlang:binary_to_list(Data),
scan(String).
scan(String) when is_list(String)->
scan(String++eof,[]). %% appended eof
scan({done, Result, LeftOverChars},Acc)->
scan_done(Result,LeftOverChars,Acc);
scan({more, Continuation},Acc)->
scan(erl_scan:tokens(Continuation,[],1),Acc);
scan(String,Acc) when is_list(String)->
scan(erl_scan:tokens([],String,1),Acc).
scan_done({error,ErrorMsg,_Location},_LeftOverChars,_Acc)->
ErrorMsg;
scan_done({eof,Location},LeftOverChars,Acc)->
Acc;
scan_done({ok,Tokens,Location},LeftOverChars,Acc)->
case erl_parse:parse_form(Tokens) of
{ok,R}->scan(LeftOverChars,Acc++[R]);
{error,R}->scan(LeftOverChars,R)
end.

syntax function into Erlang

I have a module which written into notepad:
-module(hhfuns).
-compile(export_all).
one() -> 1.
two() -> 2.
add(X,Y) - X() + Y().
and I save it as hhfuns.erl. When I run Ok then I called:
hhfuns:add(myfun hhfun:one/0, myfun hhfun:two/0).
this command make syntax error. But when I changed myfun to fun it worked.maybe it's a basic syntax but I'm new at Erlang, so please explain for me why.
When you are passing function from outside module as a parameter you should use syntax fun Module:Function/Arity. Correct version for you will be hhfuns:add(fun hhfun:one/0, fun hhfun:two/0). fun here is a required keyword and you can not use myfun here instead.
In fact myfun is not an Erlang keyword, whereas fun is.

Resources