Commit a175cf83 authored by Dario Pinto's avatar Dario Pinto
Browse files

rename functions, add utf8 character conversions for URLs, change author and...

rename functions, add utf8 character conversions for URLs, change author and category count functions to Hashtbl, failwith message and more
parent e1ad92fd
type article = type article =
{ date : int * int * int { date : int * int * int
; title : string ; title : string
; author : string ; authors : string list
; tags : string list ; tags : string list
; category : string ; category : string
; content : string ; content : string
; url : string ; url : string
} }
let normalize_url target =
String.map
(function
| ' ' -> '_'
| c -> c |> Char.lowercase_ascii )
(Ubase.from_utf8 target)
(** [allowed_categories] is a list of allowed categories for any article posted (** [allowed_categories] is a list of allowed categories for any article posted
on the blog *) on the blog *)
let allowed_categories = let allowed_categories =
[ "tooling" [ "Tooling"
; "blockchains" ; "Blockchains"
; "oCamlPro" ; "OCamlPro"
; "formal methods" ; "Formal Methods"
; "trainings" ; "Trainings"
; "oCaml" ; "OCaml"
; "rust" ; "Rust"
] ]
let category_urls = List.map normalize_url allowed_categories
(** [raw_articles] List of all raw text in all articles in /content/blog/ (** [raw_articles] List of all raw text in all articles in /content/blog/
subdirectory *) subdirectory *)
let raw_articles = let raw_articles =
...@@ -31,7 +40,7 @@ let raw_articles = ...@@ -31,7 +40,7 @@ let raw_articles =
(** [get_meta_value field] extract the second field of meta_data required at the (** [get_meta_value field] extract the second field of meta_data required at the
beginning of the article *) beginning of the article *)
let get_meta_value field = List.hd (List.rev (String.split_on_char ':' field)) let get_meta_value field = List.hd (List.rev (String.split_on_char '=' field))
(** [extract_date date] convert date meta_data into a [(int * int * int)] type *) (** [extract_date date] convert date meta_data into a [(int * int * int)] type *)
let extract_date date = let extract_date date =
...@@ -48,17 +57,26 @@ let article_of_string post url = ...@@ -48,17 +57,26 @@ let article_of_string post url =
Some Some
{ date = extract_date (get_meta_value date) { date = extract_date (get_meta_value date)
; title = get_meta_value title ; title = get_meta_value title
; author = get_meta_value author ; authors =
(let auth = get_meta_value author in
match String.split_on_char ',' auth with
| [ "" ] -> [ "Unspecified authors!" ]
| auth -> auth )
; tags = ; tags =
(let tags = get_meta_value tags in (let tags = get_meta_value tags in
match String.split_on_char ',' tags with match String.split_on_char ',' tags with
| [] -> [ "Unspecified tags!" ] | [ "" ] -> [ "Unspecified tags!" ]
| tags -> tags ) | tags -> tags )
; category = ; category =
begin begin
let c = get_meta_value category in let c = get_meta_value category in
if not @@ List.mem c allowed_categories then if not @@ List.mem c allowed_categories then
failwith "Category unknown"; failwith
(Format.asprintf {|Category (%s) invalid, try: %a|} c
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
Format.pp_print_string )
allowed_categories );
c c
end end
; content = String.concat "\n" r ; content = String.concat "\n" r
...@@ -86,84 +104,94 @@ let articles_data = get_article_data raw_articles ...@@ -86,84 +104,94 @@ let articles_data = get_article_data raw_articles
(** [authors] The list of authors in the current pool of available articles *) (** [authors] The list of authors in the current pool of available articles *)
let authors = let authors =
let articles_by_authors = List.sort_uniq compare @@ List.flatten
List.sort_uniq (fun a1 a2 -> compare a1.author a2.author) articles_data @@ List.map (fun article -> article.authors) articles_data
in
List.map (fun article -> article.author) articles_by_authors
(** [authors_count] A list of [(string * int)] when for each author [string], (** [authors_count] List of all authors with their corresponding count of
his/her respective number of written articles *) written articles *)
let authors_count = let authors_count =
let rec count acc current_author = function let tbl = Hashtbl.create 512 in
| [] -> (current_author, acc) List.iter
| article :: r -> (fun article ->
if article.author = current_author then List.iter
count (acc + 1) current_author r (fun author ->
else match Hashtbl.find_opt tbl author with
count acc current_author r | None -> Hashtbl.add tbl author 1
in | Some count -> Hashtbl.replace tbl author (count + 1) )
let rec map_authors_count acc = function article.authors )
| [] -> acc articles_data;
| current_author :: r -> let l = Hashtbl.fold (fun k v acc -> (k, v) :: acc) tbl [] in
map_authors_count (count 0 current_author articles_data :: acc) r List.sort (fun (k1, v1) (k2, v2) -> compare (v2, k2) (v1, k1)) l
in
List.rev (map_authors_count [] authors)
(** [categories_count] A list of [(string * int)] when for each category (** [categories_count] List of all categories with their corresponding count of
[string] its corresponding number of articles *) written articles *)
let categories_count = let categories_count =
let allowed_categories = let tbl = Hashtbl.create 512 in
List.sort (fun c1 c2 -> compare c1 c2) allowed_categories List.iter
in (fun article ->
let rec count acc category = function match Hashtbl.find_opt tbl article.category with
| [] -> (category, acc) | None -> Hashtbl.add tbl article.category 1
| article :: r -> | Some count -> Hashtbl.replace tbl article.category (count + 1) )
if article.category = category then articles_data;
count (acc + 1) category r let l = Hashtbl.fold (fun k v acc -> (k, v) :: acc) tbl [] in
else List.sort (fun (k1, v1) (k2, v2) -> compare (v2, k2) (v1, k1)) l
count acc category r
in
let rec map_categories_count acc = function
| [] -> acc
| allowed_cat :: r ->
map_categories_count (count 0 allowed_cat articles_data :: acc) r
in
List.rev (map_categories_count [] allowed_categories)
let pp_cat_count fmt _categories_count = Format.fprintf fmt "" let links_to_home_pages =
Format.sprintf
{|
<div class="row">
<h5>
<a href="/blog">Home!</a>
</h5>
<h5>
<a href="/blog/authors">All authors here!</a>
</h5>
<h5>
<a href="/blog/category">All categories here!</a>
</h5>
</div>
<hr class="featurette-divider"/>
|}
let pp_article_excerpt fmt article = let pp_article_excerpt fmt article =
let year, month, day = article.date in let year, month, day = article.date in
Format.fprintf fmt Format.fprintf fmt
{|<div class="row"> {|
<h3> <div class="row">
<a href="/blog/%s">%s</a> <h3><a href="/blog/%s">%s</a></h3>
</h3> </div>
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_person.svg"/> <img class="icon" src="/blog/assets/img/icon_person.svg"/>
Author: %s Authors: %a
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_calendar.svg"/> <img class="icon" src="/blog/assets/img/icon_calendar.svg"/>
Date: %4d-%02d-%02d Date: %4d-%02d-%02d
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_category.svg"/> <img class="icon" src="/blog/assets/img/icon_category.svg"/>
Category: %s Category: <a href="/blog/category/%s">%s</a>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_tags.svg"/> <img class="icon" src="/blog/assets/img/icon_tags.svg"/>
Tags: %a Tags: %a
</div> </div>
</div> </div>
<br/> <br />
<br/>
<a href="/blog/%s">Read more...</a> <a href="/blog/%s">Read more...</a>
<hr class="featurette-divider"/> <hr class="featurette-divider"/>
</div>
<br />|} <br />|}
article.url article.title article.author year month day article.category article.url article.title
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
(fun fmt author ->
Format.fprintf fmt {|<a href="/blog/authors/%s">%s</a>|}
(normalize_url author) author ) )
article.authors year month day
(normalize_url article.category)
article.category
(Format.pp_print_list (Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
Format.pp_print_string ) Format.pp_print_string )
...@@ -174,13 +202,13 @@ let pp_blog_posts fmt articles_data_list = ...@@ -174,13 +202,13 @@ let pp_blog_posts fmt articles_data_list =
(** [specific_article_header title author (year, month, day) category tags] (** [specific_article_header title author (year, month, day) category tags]
prints the header for a given blog post *) prints the header for a given blog post *)
let specific_article_header title author (year, month, day) category tags = let specific_article_header title authors (year, month, day) category tags =
Format.asprintf Format.asprintf
{|<h1 id="page-title">%s</h1> {|<h1 id="page-title">%s</h1>
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_person.svg"/> <img class="icon" src="/blog/assets/img/icon_person.svg"/>
Author: %s Author: %a
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_calendar.svg"/> <img class="icon" src="/blog/assets/img/icon_calendar.svg"/>
...@@ -188,7 +216,7 @@ let specific_article_header title author (year, month, day) category tags = ...@@ -188,7 +216,7 @@ let specific_article_header title author (year, month, day) category tags =
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_category.svg"/> <img class="icon" src="/blog/assets/img/icon_category.svg"/>
Category: %s Category: <a href="/blog/category/%s">%s</a>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_tags.svg"/> <img class="icon" src="/blog/assets/img/icon_tags.svg"/>
...@@ -197,112 +225,105 @@ let specific_article_header title author (year, month, day) category tags = ...@@ -197,112 +225,105 @@ let specific_article_header title author (year, month, day) category tags =
</div> </div>
<hr class="featurette-divider"/> <hr class="featurette-divider"/>
|} |}
title author year month day category title
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
(fun fmt author ->
Format.fprintf fmt {|<a href="/blog/authors/%s">%s</a>|}
(normalize_url author) author ) )
authors year month day (normalize_url category) category
(Format.pp_print_list (Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
Format.pp_print_string ) Format.pp_print_string )
tags tags
(** [sub_cat category] Displays the list of articles corresponding to the (** [given_category category] Displays the list of articles corresponding to the
request category *) request category *)
let sub_cat category = let given_category category =
let articles_by_date = let articles_by_date =
List.sort (fun a1 a2 -> compare a2.date a1.date) articles_data List.sort
(fun a1 a2 ->
match compare a2.date a1.date with
| 0 -> compare a2.title a1.title
| n -> n )
articles_data
in in
let articles_of_category = let articles_of_category =
List.filter List.filter
(fun article -> String.equal article.category category) (fun article -> String.equal (normalize_url article.category) category)
articles_by_date articles_by_date
in in
Format.asprintf let category = (List.hd articles_of_category).category in
{|<h1 id="page-title">Articles on %s</h1> Format.asprintf {|<h1 id="page-title">Articles on %s</h1>%s%a@.|} category
links_to_home_pages pp_blog_posts articles_of_category
<hr class="featurette-divider"/>
%a
|}
(String.capitalize_ascii category)
pp_blog_posts articles_of_category
(* (** [given_author ocp_author] Displays the list of articles written by a given
*
* /!\ NEED SOME PROCESSING ON AUTHOR NAMES
* REMOVE WHITESPACES FOR PROCESSING ??
*
* *)
(** [author ocp_author] Displays the list of articles written by a given
[ocp_author] *) [ocp_author] *)
let author ocp_author = let given_author ocp_author =
let articles_by_date = let articles_by_date =
List.sort (fun a1 a2 -> compare a1.author a2.author) articles_data List.sort
(fun a1 a2 ->
match compare a2.date a1.date with
| 0 -> compare a2.title a1.title
| n -> n )
articles_data
in in
let articles_of_author = let articles_of_author =
List.filter List.filter
(fun article -> String.equal article.author ocp_author) (fun article ->
List.exists
(fun author -> String.equal (normalize_url author) ocp_author)
article.authors )
articles_by_date articles_by_date
in in
Format.asprintf let author = List.hd (List.hd articles_of_author).authors in
{|<h1 id="page-title">Articles on %s</h1> Format.asprintf {|<h1 id="page-title">Articles by %s</h1>%s%a@.|} author
links_to_home_pages pp_blog_posts articles_of_author
<hr class="featurette-divider"/> (** [category_home] This is the home page for all available categories on the
%a Blog, along with number of articles of given category *)
|}
ocp_author pp_blog_posts articles_of_author
(** [category_home] List of all available categories on the Blog, along with
number of articles of given category *)
let category_home = let category_home =
Format.asprintf Format.asprintf {|<h1 id="page-title">Blog Categories</h1>%s%a@.|}
{|<h1 id="page-title">Blog Categories</h1> links_to_home_pages
<hr class="featurette-divider"/>
%a
|}
(Format.pp_print_list (Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "<br />") ~pp_sep:(fun fmt () -> Format.fprintf fmt "<br />")
(fun fmt (category, count) -> (fun fmt (category, count) ->
Format.fprintf fmt Format.fprintf fmt
{|<h3><a href="/blog/category/%s">%s</a> (%d articles)</h3>|} {|<h3><a href="/blog/category/%s">%s</a> (%d %s)</h3>|}
category (normalize_url category) category count
(String.capitalize_ascii category) ( if count > 1 then
count ) ) "articles"
else
"article" ) ) )
categories_count categories_count
(** [authors_home] List of all available authors on the Blog, along with number (** [authors_home] This is the home page of all available authors on the Blog,
of articles for a given author *) along with number of articles for a given author *)
let authors_home = let authors_home =
Format.asprintf Format.asprintf {|<h1 id="page-title">Blog Authors</h1>%s%a@.|}
{|<h1 id="page-title">Blog Authors</h1> links_to_home_pages
<hr class="featurette-divider"/>
%a
|}
(Format.pp_print_list (Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "<br /> ") ~pp_sep:(fun fmt () -> Format.fprintf fmt "<br /> ")
(fun fmt (author, count) -> (fun fmt (author, count) ->
Format.fprintf fmt Format.fprintf fmt
{|<h3><a href="/blog/authors/%s">%s (%d articles)</h3>|} author {|<h3><a href="/blog/authors/%s">%s</a> (%d %s)</h3>|}
author count ) ) (normalize_url author) author count
( if count > 1 then
"articles"
else
"article" ) ) )
authors_count authors_count
(** [home_page] this is the home page for the blog, articles appear as excerpts (** [home_page] this is the home page for the blog, articles appear as excerpts
from most recent to oldest *) from most recent to oldest *)
let home_page = let home_page =
let articles_by_date = let articles_by_date =
List.sort (fun a1 a2 -> compare a2.date a1.date) articles_data List.sort
(fun a1 a2 ->
match compare a2.date a1.date with
| 0 -> compare a2.title a1.title
| n -> n )
articles_data
in in
Format.asprintf Format.asprintf {|<h1 id="page-title">Blog</h1>%s%a@.|} links_to_home_pages
{|<h1 id="page-title">Blog</h1>
<div class="row">
<h5>
<a href="/blog/authors">All authors here!</a>
</h5>
<h5>
<a href="/blog/category">All categories here!</a>
</h5>
</div>
<hr class="featurette-divider"/>
%a
|}
pp_blog_posts articles_by_date pp_blog_posts articles_by_date
title:Generating static and portable executables with OCaml title=Generating static and portable executables with OCaml
authors:Louis Gesbert authors=Louis Gesbert
date:2021-09-02 date=2021-09-02
category:tooling category=Tooling
tags:static,portable,binaries tags=static,portable,binaries
Distributing OCaml software on opam is great (if I dare say so myself), but sometimes you need to provide your tools to an audience outside of the OCaml community, or just without recompilations or in a simpler way. Distributing OCaml software on opam is great (if I dare say so myself), but sometimes you need to provide your tools to an audience outside of the OCaml community, or just without recompilations or in a simpler way.
However, just distributing the locally generated binaries requires that the users have all the needed shared libraries installed, and a compatible libc. It&#8217;s not something you can assume in general, and even if you don&#8217;t need any C shared library or are confident enough it will be installed everywhere, the libc issue will arise for anyone using a distribution based on a different kind, or a little older than the one you used to build. However, just distributing the locally generated binaries requires that the users have all the needed shared libraries installed, and a compatible libc. It&#8217;s not something you can assume in general, and even if you don&#8217;t need any C shared library or are confident enough it will be installed everywhere, the libc issue will arise for anyone using a distribution based on a different kind, or a little older than the one you used to build.
......
title=opam 2.0.9 release
authors=rjbou
date=2021-08-05
category=Tooling
tags=opam,release
<div class="row">
<div class="col-lg-12" align="center">
<img src="/blog/assets/img/logo_opam_blue.png" class="img-fluid rounded"/>
</div>
</div>
<br />
<em>
Feedback on this post is welcomed on <a href="https://discuss.ocaml.org/t/ann-opam-2-1-0/8255">Discuss</a>!
</em>
We are pleased to announce the minor release of <a href="https://github.com/ocaml/opam/releases/tag/2.0.9">opam 2.0.9</a>.
This new version contains some <a href="https://github.com/ocaml/opam/pull/4547">back-ported</a> fixes.
<h2 class="page-subtitle">
New features
</h2>
<ul>
<li>
Back-ported ability to load upgraded roots read-only; allows applications compiled with opam-state 2.0.9 to load a root which has been upgraded to opam 2.1 [<a href="https://github.com/ocaml/opam/issues/4636">#4636</a>]
</li>
<li>
macOS sandbox now supports <code>OPAM_USER_PATH_RO</code> for adding a custom read-only directory to the sandbox [<a href="https://github.com/ocaml/opam/issues/4589">#4589</a>, <a href="https://github.com/ocaml/opam/issues/4609">#4609</a>]
</li>
<li>
<code>OPAMROOT</code> and <code>OPAMSWITCH</code> now reflect the <code>--root</code> and <code>--switch</code> parameters in the package build [<a href="https://github.com/ocaml/opam/issues/4668">#4668</a>]
</li>
<li>
When built with opam-file-format 2.1.3+, opam-format 2.0.x displays better errors for newer opam files [<a href="https://github.com/ocaml/opam/issues/4394">#4394</a>]
</li>
</ul>
<h2 class="page-subtitle">
Bug fixes
</h2>
<ul>
<li>
Linux sandbox now mounts <em>host</em> <code>$TMPDIR</code> read-only, then sets the <em>sandbox</em> <code>$TMPDIR</code> to a new separate tmpfs. <strong>Hardcoded <code>/tmp</code> access no longer works if <code>TMPDIR</code> points to another directory</strong> [<a href="https://github.com/ocaml/opam/issues/4589">#4589</a>]</li><li>Stop clobbering <code>DUNE_CACHE</code> in the sandbox script [<a href="https://github.com/ocaml/opam/issues/4535">#4535</a>, fixing <a href="https://github.com/ocaml/dune/issues/4166">ocaml/dune#4166</a>]
</li>
<li>
Ctrl-C now correctly terminates builds with bubblewrap; sandbox now requires bubblewrap 0.1.8 or later [<a href="https://github.com/ocaml/opam/issues/4400">#4400</a>]
</li>
<li>
Linux sandbox script no longer makes <code>PWD</code> read-write on remove actions [<a href="https://github.com/ocaml/opam/issues/4589">#4589</a>]
</li>
<li>
Lint W59 and E60 no longer trigger for packages flagged <code>conf</code> [<a href="https://github.com/ocaml/opam/issues/4549">#4549</a>]
</li>
<li>
Reduce the length of temporary file names for pin caching to ease pressure on Windows [<a href="https://github.com/ocaml/opam/issues/4590">#4590</a>]
</li>
<li>
Security: correct quoting of arguments when removing switches [<a href="https://github.com/ocaml/opam/issues/4707">#4707</a>]
</li>
<li>
Stop advertising the removed option <code>--compiler</code> when creating local switches [<a href="https://github.com/ocaml/opam/issues/4718">#4718</a>]
</li>
<li>
Pinning no longer fails if the archive&#8217;s opam file is malformed [<a href="https://github.com/ocaml/opam/issues/4580">#4580</a>]
</li>
<li>
Fish: stop using deprecated <code>^</code> syntax to fix support for Fish 3.3.0+ [<a href="https://github.com/ocaml/opam/issues/4736">#4736</a>]
</li>
</ul>
<hr class="featurette-divider"/>
Installation instructions (unchanged):
<ol>
<li>