How to pass projects in order in FAKE - f#-fake

I need to build vb6 projects in order. How should I pass them to Vb6Make?
let projs = [
"a.vbp"
"b.vbp"
"c.vbp"
]
Target "VB6" (fun _ ->
!! projs // <- ?
|> Vb6Make
)

!! and ++ can be used for single files files as well. And FullName is important for directories.
let vb6dir = FullName "./bin"
let tempdir = FullName "./temp"
let projs =
!! "src\a\a.vbp"
++ "src\b\b.vbp"
++ "src\c\c.vbp"
Target "BuildVb6" (fun _ ->
projs
|> Fake.Vb6Helper.Vb6Make(fun c ->
{ c with
Logdir = tempdir
Outdir = vb6dir })
)

Related

How to load a module inside a custom module in Ejabberd?

I'm creating a custom module which uses some exported functions from mod_shared_roster module. Here is the code.
-module(mod_shared_roster_extra).
-author('https://github.com/eric-mendoza').
-behaviour(gen_mod).
-include("logger.hrl").
-include("translate.hrl").
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2, mod_doc/0]).
% Commands API
-export([
% Shared roster
srg_display_group_add/4, srg_display_group_del/4, srg_set_opts/4
]).
-include("ejabberd_commands.hrl").
-include("mod_roster.hrl").
-include("mod_privacy.hrl").
-include("ejabberd_sm.hrl").
-include_lib("xmpp/include/xmpp.hrl").
-include("mod_shared_roster.hrl").
%%%
%%% gen_mod
%%%
start(_Host, _Opts) ->
ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_commands:unregister_commands(get_commands_spec());
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[{mod_shared_roster, hard}].
%%%
%%% Register commands
%%%
get_commands_spec() ->
[
#ejabberd_commands{name = srg_display_group_add, tags = [shared_roster_group],
desc = "Add group id to the Shared Roster Group display list",
module = ?MODULE, function = srg_display_group_add,
args = [{displaygroup, binary}, {displaygrouphost, binary}, {group, binary}, {grouphost, binary}],
args_example = [<<"group1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
args_desc = ["Group to be added in display list", "Group server name", "Group to modify display list identifier", "Group server name"],
result = {res, rescode}},
#ejabberd_commands{name = srg_display_group_del, tags = [shared_roster_group],
desc = "Delete group id from the Shared Roster Group",
module = ?MODULE, function = srg_display_group_del,
args = [{displaygroup, binary}, {displaygrouphost, binary}, {group, binary}, {grouphost, binary}],
args_example = [<<"group1">>, <<"myserver.com">>, <<"group3">>, <<"myserver.com">>],
args_desc = ["Group to be removed from display list", "Group server name", "Group to modify display list identifier", "Group server name"],
result = {res, rescode}},
#ejabberd_commands{name = srg_set_opts, tags = [shared_roster_group],
desc = "Update Shared Roster Group options (name and description)",
module = ?MODULE, function = srg_set_opts,
args = [{name, binary}, {description, binary}, {group, binary}, {grouphost, binary}],
args_example = [<<"Group Test Name">>, <<"Group used for testing.">>, <<"group3">>, <<"myserver.com">>],
args_desc = ["Shared roster group name", "Shared Roster Group description", "Group identifier", "Group server name"],
result = {res, rescode}}
].
%%%
%%% Shared Roster Groups
%%%
to_list([]) -> [];
to_list([H|T]) -> [to_list(H)|to_list(T)];
to_list(E) when is_atom(E) -> atom_to_list(E);
to_list(E) -> binary_to_list(E).
srg_display_group_add(NewGroup, NewGroupHost, Group, GroupHost) ->
?DEBUG("Adding group to display list.", []),
Opts = mod_shared_roster:get_group_opts(Group, GroupHost),
mod_shared_roster:set_group_opts(GroupHost, Group, Opts),
ok.
srg_display_group_del(DeleteGroup, DeleteGroupHost, Group, GroupHost) ->
?DEBUG("Removing group from display list.", []),
Opts = mod_shared_roster:get_group_opts(Group, GroupHost),
%% mod_shared_roster:set_group_opts(GroupHost, Group, Opts),
ok.
srg_set_opts(Label1, Description1, Group, GroupHost) ->
?DEBUG("Setting group options -> Label: ~p, Description: ~p~n", [Label1, Description1]),
Opts = mod_shared_roster:get_group_opts(Group, GroupHost),
Label = if Label1 == <<"">> -> [];
true -> [{label, Label1}]
end,
Description = if Description1 == <<"">> -> [];
true -> [{description, Description1}]
end,
Displayed1 = get_opt(Opts, displayed_groups, []),
Displayed = if Displayed1 == [] -> [];
true -> [{displayed_groups, Displayed1}]
end,
?DEBUG("Options: ~p~n", [Label ++ Description ++ Displayed]),
mod_shared_roster:set_group_opts(GroupHost, Group, Label ++ Description ++ Displayed),
ok.
mod_options(_) -> [].
get_opt(Opts, Opt, Default) ->
case lists:keysearch(Opt, 1, Opts) of
{value, {_, Val}} -> Val;
false -> Default
end.
mod_doc() ->
#{desc =>
?T("This module provides additional administrative commands for shared rosters.")}.
Module installs correctly, but when I call the method srg_set_opts which uses mod_shared_roster:get_group_opts I get the following error:
error: {module_not_loaded, mod_shared_roster, <<"srg_group10">>}
I've seen other modules as guide, and the only difference I've noticed is those modules dependencies are contained in the same directory. But, how could I achieve that with a custom module? I'm not experienced with Erlang, so it could be just a simple import.
I'm using ejabberd 20.04 installed via docker-ejabberd, and used Ejabberd module docs for Docker to install the module.
Opts = mod_shared_roster:get_group_opts(Group, GroupHost),
I think the arguments of that function are the Host and then the Group, but you provide them in the reverse order.

Menu variable not in scope Haskell

I am trying to create a menu which read the data from a textfile. But I get three errors that variable not in scope despite having at the start of the IO(). Am I not reading the txt.file properly and is the way I creating the menu wrong?
errors
Variable not in scope: spaDatabase :: [Spa]
--line of error for spaDatabase
let updatedDB = (addSpa rid br ar (read st) spaDatabase)
Variable not in scope: spaDB
Variable not in scope: updatedDB :: [Spa]
--line of error for updatedDB
2 -> putStrLn (spaListStr updatedDB) >> menu spaDB
My code
main :: IO()
main = do
contents <- readFile "spa.txt"
let spaDatabase = (read contents :: [Spa])
menu spaDatabase
putStrLn ""
where menu spaDatabase = do
putStrLn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
putStrLn "\nPlease select an option:"
putStrLn "1: Add a new spa to the database "
putStrLn "2: Show all spa "
putStr "\nSelected option: "
putStrLn ""
option <- getLine
putStrLn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
output :: Int -> IO ()
output option = do
case option of 1 -> do putStrLn "Enter Spa ID: "
rid <- getLine
putStrLn "Enter Spa Brand Name: "
br <- getLine
putStrLn "Enter Spa Area: "
ar <- getLine
putStrLn "Enter Spa Stars: "
st <- getLine
let updatedDB = (addSpa rid br ar (read st) spaDatabase)
putStrLn (spaListStr updatedDB)
writeFile "spa.txt" (spaListStr updatedDB)
2 -> putStrLn (spaListStr updatedDB) >> menu spaDB
where is part of a definition, not of expression. It belongs to the main's definition so its scope goes above do's. Your originally posted code
main = do { .....
let c = .....
.....
}
where
a = ....
b = ....c....
.....
is equivalent to
main = let { -- where's translation
a = ....
b = ....c....
.....
}
in
do {
.....
let c = .....
.....
}
You're using c in b = ....c.... before it is introduced with let c = ....., i.e. outside of its scope region. That's an error, "variable not in scope".
update. let in do blocks is just a shortcut:
do {
.....
let c = .....
.....
}
is the same as
do {
.....
let c = .....
in do {
.....
}
}
The working code could be structured, as also #chi proposed in the comments, as
main = do { .....
let c = .....
.....
let { a = ....
b = ....c....
}
.....
}
That way a and b are in c's scope, so may use c as part of their definitions.

How do you specify Fake Target inputs and output?

In the build systems that I'm familiar with (make and msbuild) there's a way to specify the inputs and outputs for a target. If the time stamps on the input files are earlier than those on the outputs the task is skipped. I can't find something similar in FAKE.
For example if I wanted to translate this Makefile to Fake
a.exe: a.fs
fsharpc a.fs -o a.exe
it might look like:
Target "a.exe" (fun _ -> ["a.fs"] |> FscHelper.compile [...])
However, when I run the build command it will always execute the compiler and produce a new a.exe regardless the modification time on a.fs. Is there a simple way to get the same behavior as the makefile?
You could use =?>and provide a function that returns true or false if the task should run.
let fileModified f1 f2 =
FileInfo(f1).LastWriteTime > FileInfo(f2).LastWriteTime
and then in target dependencies
=?> ("a.exe", fileModified "a.fs" "a.exe")
A more complete code example to flesh out Lazydevs answer:
#r "packages/FAKE/tools/FakeLib.dll"
open Fake
open System.IO
Target "build" (fun _ ->
trace "built"
)
let needsUpdate f1 f2 =
let lastWrite files =
files
|> Seq.map (fun f -> FileInfo(f).LastWriteTime)
|> Seq.max
let t1 = lastWrite f1
let t2 = lastWrite f2
t1 > t2
let BuildTarget name infiles outfiles fn =
Target name (fn infiles)
name =?> ("build", needsUpdate infiles outfiles)
BuildTarget "compile" ["Test2.fs"; "Test1.fs"] ["Test2.dll"] (fun files _ ->
files
|> FscHelper.compile [
FscHelper.Target FscHelper.TargetType.Library
]
|> function 0 -> () | c -> failwithf "compile error"
)
RunTargetOrDefault "build"

How to get query parameters in Elm?

In my Elm program, I'd like to initialize my model based on the query string.
For example, if the query string is ?w=3&h=5 I'd like to have:
initialModel =
{ width = 3
, height = 5
}
Is that possible to achieve this in Elm, or the only way to do this is to get the query parameters in Javascript and pass them via a port?
Elm 0.19
For elm 0.19 the below concept is the same. Both of these packages still exist but have been moved and relabeled as the official elm/url and elm/browser libraries.
Elm 0.18
This example uses evancz/url-parser and elm-lang/navigation. There are a few kinks that aren't straightforward in the documentation, but I've explained them briefly below. The example should speak for itself.
module Main exposing (..)
import Html as H exposing (..)
import Navigation exposing (Location)
import UrlParser as UP exposing ((</>), (<?>), top, parsePath, oneOf, s, stringParam, Parser)
import Maybe.Extra as MaybeExtra exposing (unwrap)
type Route
= UrlRoute (Maybe String) (Maybe String)
| NotFoundRoute
type Msg
= UrlParser Navigation.Location
type alias Model =
{ location : Route
, w : String
, h : String
}
type alias SearchParams =
{ w : Maybe String, h : Maybe String }
main =
Navigation.program UrlParser
{ init = init
, view = view
, update = update
, subscriptions = (\_ -> Sub.none)
}
init : Location -> ( Model, Cmd Msg )
init location =
let
currentPath =
parseLocation location
in
( initialModel currentPath
, Cmd.none
)
parseLocation : Location -> Route
parseLocation location =
case (parsePath matchers location) of
Just route ->
route
Nothing ->
NotFoundRoute
matchers : Parser (Route -> a) a
matchers =
UP.map UrlRoute (UP.s "index" <?> UP.stringParam "w" <?> UP.stringParam "h")
initialModel : Route -> Model
initialModel route =
{ location = route
, w = MaybeExtra.unwrap "" (\x -> Maybe.withDefault "" x.w) (parseParams route)
, h = MaybeExtra.unwrap "" (\x -> Maybe.withDefault "" x.h) (parseParams route)
}
parseParams : Route -> Maybe SearchParams
parseParams route =
case route of
UrlRoute w h ->
Just { w = w, h = h }
NotFoundRoute ->
Nothing
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
UrlParser location ->
( model
, Cmd.none
)
view : Model -> Html msg
view model =
div []
[ h1 [] [ text "URL Info" ]
, div [] [ text ("W is: " ++ model.w) ]
, div [] [ text ("H is: " ++ model.h) ]
]
The "trick" is to create another type alias to place your query params inside of. In the above example I've created the type SearchParams. After creating this type we just use an initialModel that takes in the currentPath.
From there, our model can extract the query params with Maybe.withDefault (it needs to be a Maybe type because the params may not be there). Once we have our data in the model we just print it out in the view.
Hope this helps!
There is no built-in core library way to access the URL. You can use ports and the community library jessitron/elm-param-parsing.
If you also want to set the URL, you can again use ports, or you can use the History API, for which there are bindings in TheSeamau5/elm-history.
Unfortunately jessitron/elm-param-parsing doesn't work with Elm 0.18.
Use elm-lang/navigation package:
http://package.elm-lang.org/packages/elm-lang/navigation/latest/Navigation
https://github.com/elm-lang/navigation/tree/2.1.0
especially this function:
program
: (Location -> msg)
-> { init : Location -> (model, Cmd msg), update : msg -> model -> (model, Cmd msg), view : model -> Html msg, subscriptions : model -> Sub msg }
-> Program Never model msg
In the second parameter you can see "init : Location -> (model, Cmd msg)". This should handle reading of initial URL. To complement that, first parameter is a function which gets called every time URL changes.
(I am aware it's an old question, but this link popped out when I was looking for the solution to the same problem and accepted answer didn't help)

Fake: What is the difference between fileset selectors: !!, ++ and !+

There are different fileset selectors in FAKE. What is the difference between these: !!, ++, !+?
I found these examples:
Example 1:
// Filesets
let appReferences =
!! "src/app/**/*.csproj"
++ "src/app/**/*.fsproj"
Example 2:
// files
let appReferences =
!+ "./src/**/*.fsproj"
|> Scan
Example 3:
let filesToZip =
!+ (buildDir + "/**/*.*")
-- "*.zip"
|> Scan
!+ "./src/**/*.fsproj"
|> Scan
is obsolete and replaced by !!
-- "*.zip"
excludes files from the fileset above.