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 =
{ date : int * int * int
; title : string
; author : string
; authors : string list
; tags : string list
; category : string
; content : 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
on the blog *)
let allowed_categories =
[ "tooling"
; "blockchains"
; "oCamlPro"
; "formal methods"
; "trainings"
; "oCaml"
; "rust"
[ "Tooling"
; "Blockchains"
; "OCamlPro"
; "Formal Methods"
; "Trainings"
; "OCaml"
; "Rust"
]
let category_urls = List.map normalize_url allowed_categories
(** [raw_articles] List of all raw text in all articles in /content/blog/
subdirectory *)
let raw_articles =
......@@ -31,7 +40,7 @@ let raw_articles =
(** [get_meta_value field] extract the second field of meta_data required at the
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 *)
let extract_date date =
......@@ -48,17 +57,26 @@ let article_of_string post url =
Some
{ date = extract_date (get_meta_value date)
; 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 =
(let tags = get_meta_value tags in
match String.split_on_char ',' tags with
| [] -> [ "Unspecified tags!" ]
| [ "" ] -> [ "Unspecified tags!" ]
| tags -> tags )
; category =
begin
let c = get_meta_value category in
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
end
; content = String.concat "\n" r
......@@ -86,63 +104,67 @@ let articles_data = get_article_data raw_articles
(** [authors] The list of authors in the current pool of available articles *)
let authors =
let articles_by_authors =
List.sort_uniq (fun a1 a2 -> compare a1.author a2.author) articles_data
in
List.map (fun article -> article.author) articles_by_authors
List.sort_uniq compare @@ List.flatten
@@ List.map (fun article -> article.authors) articles_data
(** [authors_count] A list of [(string * int)] when for each author [string],
his/her respective number of written articles *)
(** [authors_count] List of all authors with their corresponding count of
written articles *)
let authors_count =
let rec count acc current_author = function
| [] -> (current_author, acc)
| article :: r ->
if article.author = current_author then
count (acc + 1) current_author r
else
count acc current_author r
in
let rec map_authors_count acc = function
| [] -> acc
| current_author :: r ->
map_authors_count (count 0 current_author articles_data :: acc) r
in
List.rev (map_authors_count [] authors)
let tbl = Hashtbl.create 512 in
List.iter
(fun article ->
List.iter
(fun author ->
match Hashtbl.find_opt tbl author with
| None -> Hashtbl.add tbl author 1
| Some count -> Hashtbl.replace tbl author (count + 1) )
article.authors )
articles_data;
let l = Hashtbl.fold (fun k v acc -> (k, v) :: acc) tbl [] in
List.sort (fun (k1, v1) (k2, v2) -> compare (v2, k2) (v1, k1)) l
(** [categories_count] A list of [(string * int)] when for each category
[string] its corresponding number of articles *)
(** [categories_count] List of all categories with their corresponding count of
written articles *)
let categories_count =
let allowed_categories =
List.sort (fun c1 c2 -> compare c1 c2) allowed_categories
in
let rec count acc category = function
| [] -> (category, acc)
| article :: r ->
if article.category = category then
count (acc + 1) category r
else
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 tbl = Hashtbl.create 512 in
List.iter
(fun article ->
match Hashtbl.find_opt tbl article.category with
| None -> Hashtbl.add tbl article.category 1
| Some count -> Hashtbl.replace tbl article.category (count + 1) )
articles_data;
let l = Hashtbl.fold (fun k v acc -> (k, v) :: acc) tbl [] in
List.sort (fun (k1, v1) (k2, v2) -> compare (v2, k2) (v1, k1)) l
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 year, month, day = article.date in
Format.fprintf fmt
{|<div class="row">
<h3>
<a href="/blog/%s">%s</a>
</h3>
{|
<div class="row">
<h3><a href="/blog/%s">%s</a></h3>
</div>
<div class="row">
<div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_person.svg"/>
Author: %s
Authors: %a
</div>
<div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_calendar.svg"/>
......@@ -150,20 +172,26 @@ let pp_article_excerpt fmt article =
</div>
<div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_category.svg"/>
Category: %s
Category: <a href="/blog/category/%s">%s</a>
</div>
<div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_tags.svg"/>
Tags: %a
</div>
</div>
<br/>
<br/>
<br />
<a href="/blog/%s">Read more...</a>
<hr class="featurette-divider"/>
</div>
<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
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
Format.pp_print_string )
......@@ -174,13 +202,13 @@ let pp_blog_posts fmt articles_data_list =
(** [specific_article_header title author (year, month, day) category tags]
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
{|<h1 id="page-title">%s</h1>
<div class="row">
<div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_person.svg"/>
Author: %s
Author: %a
</div>
<div class="col-lg-3">
<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 =
</div>
<div class="col-lg-3">
<img class="icon" src="/blog/assets/img/icon_category.svg"/>
Category: %s
Category: <a href="/blog/category/%s">%s</a>
</div>
<div class="col-lg-3">
<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 =
</div>
<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
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
Format.pp_print_string )
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 *)
let sub_cat category =
let given_category category =
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
let articles_of_category =
List.filter
(fun article -> String.equal article.category category)
(fun article -> String.equal (normalize_url article.category) category)
articles_by_date
in
Format.asprintf
{|<h1 id="page-title">Articles on %s</h1>
<hr class="featurette-divider"/>
%a
|}
(String.capitalize_ascii category)
pp_blog_posts articles_of_category
let category = (List.hd articles_of_category).category in
Format.asprintf {|<h1 id="page-title">Articles on %s</h1>%s%a@.|} category
links_to_home_pages pp_blog_posts articles_of_category
(*
*
* /!\ NEED SOME PROCESSING ON AUTHOR NAMES
* REMOVE WHITESPACES FOR PROCESSING ??
*
* *)
(** [author ocp_author] Displays the list of articles written by a given
(** [given_author ocp_author] Displays the list of articles written by a given
[ocp_author] *)
let author ocp_author =
let given_author ocp_author =
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
let articles_of_author =
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
in
Format.asprintf
{|<h1 id="page-title">Articles on %s</h1>
let author = List.hd (List.hd articles_of_author).authors in
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"/>
%a
|}
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 *)
(** [category_home] This is the home page for all available categories on the
Blog, along with number of articles of given category *)
let category_home =
Format.asprintf
{|<h1 id="page-title">Blog Categories</h1>
<hr class="featurette-divider"/>
%a
|}
Format.asprintf {|<h1 id="page-title">Blog Categories</h1>%s%a@.|}
links_to_home_pages
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "<br />")
(fun fmt (category, count) ->
Format.fprintf fmt
{|<h3><a href="/blog/category/%s">%s</a> (%d articles)</h3>|}
category
(String.capitalize_ascii category)
count ) )
{|<h3><a href="/blog/category/%s">%s</a> (%d %s)</h3>|}
(normalize_url category) category count
( if count > 1 then
"articles"
else
"article" ) ) )
categories_count
(** [authors_home] List of all available authors on the Blog, along with number
of articles for a given author *)
(** [authors_home] This is the home page of all available authors on the Blog,
along with number of articles for a given author *)
let authors_home =
Format.asprintf
{|<h1 id="page-title">Blog Authors</h1>
<hr class="featurette-divider"/>
%a
|}
Format.asprintf {|<h1 id="page-title">Blog Authors</h1>%s%a@.|}
links_to_home_pages
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "<br /> ")
(fun fmt (author, count) ->
Format.fprintf fmt
{|<h3><a href="/blog/authors/%s">%s (%d articles)</h3>|} author
author count ) )
{|<h3><a href="/blog/authors/%s">%s</a> (%d %s)</h3>|}
(normalize_url author) author count
( if count > 1 then
"articles"
else
"article" ) ) )
authors_count
(** [home_page] this is the home page for the blog, articles appear as excerpts
from most recent to oldest *)
let home_page =
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
Format.asprintf
{|<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
|}
Format.asprintf {|<h1 id="page-title">Blog</h1>%s%a@.|} links_to_home_pages
pp_blog_posts articles_by_date
title:Generating static and portable executables with OCaml
authors:Louis Gesbert
date:2021-09-02
category:tooling
tags:static,portable,binaries
title=Generating static and portable executables with OCaml
authors=Louis Gesbert
date=2021-09-02
category=Tooling
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.
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>
From binaries: run
</li>
</ol>
<pre class="wp-block-preformatted">bash -c "sh <(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh) --version 2.0.9"</pre>
or download manually from <a href="https://github.com/ocaml/opam/releases/tag/2.0.9">the Github "Releases" page</a> to your PATH. In this case, don't forget to run <code>opam init --reinit -ni</code> to enable sandboxing if you had version 2.0.0~rc manually installed or to update you sandbox script.
2. From source, using opam:
<pre class="wp-block-preformatted"> opam update; opam install opam-devel </pre>
(then copy the opam binary to your PATH as explained, and don't forget to run <code>opam init --reinit -ni</code> to enable sandboxing if you had version 2.0.0~rc manually installed or to update your sandbox script)
From source, manually: see the instructions in the <a href="https://github.com/ocaml/opam/tree/2.0.9#compiling-this-repo">README</a>.
We hope you enjoy this new minor version, and remain open to <a href="https://github.com/ocaml/opam/issues">bug reports</a> and <a href="https://github.com/ocaml/opam/issues">suggestions</a>.
<hr class="featurette-divider"/>
<p>
<strong>
About OCamlPro:
</strong>
<br/>
OCamlPro is a R&amp;D lab founded in 2011, with the mission to help industrial users benefit from state-of-the art programming languages like OCaml and Rust. We design, create and implement custom ad-hoc software for our clients. We also have a long experience in developing and maintaining open-source tooling for OCaml, such as Opam, <a href="http://try.ocamlpro.com">TryOCaml</a>, ocp-indent, ocp-index and ocp-browser, and we contribute to the core-development of OCaml, notably with our work on the Flambda optimizer branch. Another area of expertise is that of Formal Methods, with tools such as our SMT Solver Alt-Ergo (check our <a href="https://alt-ergo.ocamlpro.com/#club">Alt-Ergo Users' Club</a>). We also provide vocational trainings in OCaml and Rust, and we can build courses on formal methods on-demand. Do not hesitate to reach out by email: <a href="mailto:contact@ocamlpro.com">contact@ocamlpro.com</a>.
title:opam 2.1.0 is released!
authors:rjbou
date:2021-08-05
category:tooling
tags:opam
title=opam 2.1.0 is released!
authors=rjbou
date=2021-08-05
category=Tooling
tags=opam
<div class="row">
<div class="col-lg-12" align="center">
......
title=Profiling OCaml amd64 code under Linux
authors=Çagdas Bozman
date=2012-08-08
category=OCaml
tags=ocaml,tooling
We have recently worked on modifying the OCaml system to be able to profile OCaml code on Linux amd64 systems, using the processor performance counters now supported by stable kernels. This page presents this work, funded by Jane Street.
The patch is provided for OCaml version 4.00.0. If you need it for 3.12.1, some more work is required, as we would need to backport some improvements that were already in the 4.00.0 code generator.
<h2 class="page-subtitle">
An example: profiling <code>ocamlopt.opt</code>
</h2>
Here is an example of a session of profiling done using both Linux performance tools and a modified OCaml 4.00.0 system (the patch is available at the end of this article).
Linux performance tools are available as part of the Linux kernel (in the <code>linux-tools</code> package on Debian/Ubuntu). Most of the tools are invoked through the <code>perf</code> command, à la git. For example, we are going to check where the time is spent when calling the <code>ocamlopt.opt</code> command:
```bash
perf record -g ./ocamlopt.opt -c -I utils -I parsing -I typing typing/*.ml
```
This command generates a file <code>perf.data</code> in the current directory, containing all the events that were received during the execution of the command. These events contain the values of the performance counters in the amd64 processor, and the call-chain (backtrace) at the event.
We can inspect this file using the command:
```bash
perf report -g
```
The command displays:
```bash
Events: 3K cycles
+ 9.81% ocamlopt.opt ocamlopt.opt [.] compare_val
+ 8.85% ocamlopt.opt ocamlopt.opt [.] mark_slice
+ 7.75% ocamlopt.opt ocamlopt.opt [.] caml_page_table_lookup
+ 7.40% as as [.] 0x5812
+ 5.60% ocamlopt.opt [kernel.kallsyms] [k] 0xffffffff8103d0ca
+ 3.91% ocamlopt.opt ocamlopt.opt [.] sweep_slice
+ 3.18% ocamlopt.opt ocamlopt.opt [.] caml_oldify_one
+ 3.14% ocamlopt.opt ocamlopt.opt [.] caml_fl_allocate
+ 2.84% as [kernel.kallsyms] [k] 0xffffffff81317467
+ 1.99% ocamlopt.opt ocamlopt.opt [.] caml_c_call
+ 1.99% ocamlopt.opt ocamlopt.opt [.] caml_compare
+ 1.75% ocamlopt.opt ocamlopt.opt [.] camlSet__mem_1148
+ 1.62% ocamlopt.opt ocamlopt.opt [.] caml_oldify_mopup