<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luca Barbato</title>
    <description>The latest articles on DEV Community by Luca Barbato (@luzero).</description>
    <link>https://dev.to/luzero</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F153809%2F870c14cf-a8ed-486a-9e26-0cd57543115b.png</url>
      <title>DEV Community: Luca Barbato</title>
      <link>https://dev.to/luzero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luzero"/>
    <language>en</language>
    <item>
      <title>winnow-args 0.5.0</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Sun, 25 Oct 2026 14:20:17 +0000</pubDate>
      <link>https://dev.to/luzero/winnow-args-050-24oe</link>
      <guid>https://dev.to/luzero/winnow-args-050-24oe</guid>
      <description>&lt;p&gt;I spent a bit more time working on &lt;a href="https://crates.io/crates/winnow-args" rel="noopener noreferrer"&gt;winnow-args&lt;/a&gt;. I improved it after trying to integrate it with &lt;a href="https://github.com/reubeno/brush" rel="noopener noreferrer"&gt;brush&lt;/a&gt;, &lt;a href="https://github.com/lu-zero/maki/commit/6f1c3d23066ebbe87cac7447fafbc5ef01911f01" rel="noopener noreferrer"&gt;maki&lt;/a&gt;, &lt;a href="https://github.com/lu-zero/portage-cli" rel="noopener noreferrer"&gt;em&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;Mainly I expanded a bit relations between fields and groups to address what &lt;code&gt;maki&lt;/code&gt; needs, improved the subcommand and multicommand support in preparation to &lt;code&gt;em&lt;/code&gt; release and fixed more corner cases in &lt;code&gt;brush&lt;/code&gt; improving winnow-args in the process.&lt;/p&gt;

&lt;p&gt;Now we are closer to have feature parity with &lt;code&gt;usage&lt;/code&gt; and &lt;code&gt;clap&lt;/code&gt; but the road is still long.&lt;/p&gt;

&lt;p&gt;The early adopters that used the &lt;code&gt;derive&lt;/code&gt; API do not have to change anything in their code. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;0.2.0&lt;/th&gt;
&lt;th&gt;0.3.0&lt;/th&gt;
&lt;th&gt;0.4.0&lt;/th&gt;
&lt;th&gt;0.5.0&lt;/th&gt;
&lt;th&gt;usage 7&lt;/th&gt;
&lt;th&gt;clap 4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Errors and rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Did you mean" tips&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every missing required argument reported at once&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;required_if&lt;/code&gt;, &lt;code&gt;required_unless_all&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required_if_all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;requires_if&lt;/code&gt;, &lt;code&gt;exclusive&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;requires&lt;/code&gt; naming a group&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Own message for a conflict or requirement&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;validate&lt;/code&gt;, a check on the parsed struct&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Values and defaults&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;from_str&lt;/code&gt;, a value parsed by &lt;code&gt;FromStr&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;default_from&lt;/code&gt;, a default made from other fields&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A bare &lt;code&gt;env&lt;/code&gt;, named after the field&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A repeated &lt;code&gt;default&lt;/code&gt; on a &lt;code&gt;Vec&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;bool_value&lt;/code&gt; (&lt;code&gt;--wait=false&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;value_names&lt;/code&gt; (&lt;code&gt;--point &amp;lt;X&amp;gt; &amp;lt;Y&amp;gt;&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;env_fallback&lt;/code&gt;, more variable names&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subcommands&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generated dispatch (&lt;code&gt;run&lt;/code&gt;, &lt;code&gt;run_async&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;0.2.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A flag that selects the default subcommand&lt;/td&gt;
&lt;td&gt;0.2.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global flags in a flattened struct&lt;/td&gt;
&lt;td&gt;0.2.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A subcommand variant with named fields&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default_subcommand_on_empty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External subcommands&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multicall&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Help&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;*emphasis*&lt;/code&gt; and &lt;code&gt;**strong**&lt;/code&gt; painted&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global flags listed in a subcommand's help&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default subcommand marked, its page after the root's&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Examples&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A paragraph under a heading&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A flag's &lt;code&gt;warning&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flatten_help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;hide_default_value&lt;/code&gt;, &lt;code&gt;hide_env&lt;/code&gt;, &lt;code&gt;display_order&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verbatim_doc_comment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Width in terminal columns (&lt;code&gt;unicode&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;visible_alias&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;long_version&lt;/code&gt;, &lt;code&gt;before_help&lt;/code&gt;, &lt;code&gt;usage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What each choice means&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hide_choices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;help = CONSTANT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;brief_help&lt;/code&gt;: &lt;code&gt;--help&lt;/code&gt; prints the short page&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deprecation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deprecated flags, positionals and subcommands&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;deprecated_warn_at&lt;/code&gt;, &lt;code&gt;deprecated_remove_at&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deprecated_env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Completion and pages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completion of a value by hint or function&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Man and markdown pages&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effects, outputs, exit codes in the pages&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A page for each multicall command&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here some more details&lt;/p&gt;

&lt;h3&gt;
  
  
  Errors and rules
&lt;/h3&gt;

&lt;p&gt;An unknown flag, subcommand or value now comes with a tip for what was probably meant (0.2). The search runs only once there is an error, so a correct command line pays nothing for it.&lt;/p&gt;

&lt;p&gt;When several required arguments are missing, they are all reported at once, each as &lt;code&gt;--help&lt;/code&gt; lists it (0.4).&lt;/p&gt;

&lt;p&gt;The rules gained most of what clap has: &lt;code&gt;required_if&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;required_unless_all&lt;/code&gt;, &lt;code&gt;requires_if&lt;/code&gt; and &lt;code&gt;exclusive&lt;/code&gt; (0.3.1), and&lt;br&gt;
&lt;code&gt;required_if_all&lt;/code&gt;, plus &lt;code&gt;requires&lt;/code&gt; naming a group (0.4). &lt;code&gt;conflicts&lt;/code&gt;, &lt;code&gt;requires&lt;/code&gt; and groups take a &lt;code&gt;message&lt;/code&gt; with the program's own wording. &lt;/p&gt;

&lt;p&gt;For anything the attributes can't express, &lt;code&gt;validate = function&lt;/code&gt; runs the program's own check on the parsed struct.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Values and defaults
&lt;/h3&gt;

&lt;p&gt;We have new attributes to parse the cli values to types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;from_str&lt;/code&gt; parses a field with its type's &lt;code&gt;FromStr&lt;/code&gt;, for a type from another crate (0.3). &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default_from&lt;/code&gt; makes a default from other fields, after their own defaults (0.3.1).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bool_value&lt;/code&gt; takes &lt;code&gt;--wait=false&lt;/code&gt;,
and &lt;code&gt;value_names&lt;/code&gt; gives a flag several placeholders, &lt;code&gt;--size &amp;lt;W&amp;gt; &amp;lt;H&amp;gt;&lt;/code&gt; (0.4).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bare &lt;code&gt;env&lt;/code&gt; reads the variable named after the field, and a &lt;code&gt;Vec&lt;/code&gt; can repeat &lt;code&gt;default&lt;/code&gt; (0.4). &lt;code&gt;env_fallback&lt;/code&gt; reads more variables after &lt;code&gt;env&lt;/code&gt; (0.5).&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Subcommands
&lt;/h2&gt;

&lt;p&gt;Most of it came with the effort to support completely all I need for &lt;code&gt;em&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;With &lt;code&gt;default_subcommand_flags&lt;/code&gt;, a flag the root doesn't take selects the default subcommand, so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;em &lt;span class="nt"&gt;-pu&lt;/span&gt; @world 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Maps automatically to:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;em emerge &lt;span class="nt"&gt;-pu&lt;/span&gt; @world &lt;span class="c"&gt;# (0.2.1)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;default_subcommand_on_empty&lt;/code&gt; runs it for a line with no command at all (0.3.1). &lt;/p&gt;

&lt;p&gt;A subcommand can be a variant with named fields (0.3), and a flattened struct can hold global flags (0.2.1). &lt;/p&gt;

&lt;p&gt;&lt;code&gt;#[arg(run)]&lt;/code&gt; on an enum of subcommands generates the dispatch to the command it holds (0.2.1).&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;code&gt;multicall&lt;/code&gt; makes one binary act as each of its subcommands when it is run under that subcommand's name, the way busybox's applets work (0.4). &lt;/p&gt;

&lt;p&gt;Help and errors use the name it was run as. &lt;code&gt;#[arg(external)]&lt;/code&gt; on a variant takes any word that names no other subcommand, plus the rest of the line, the way &lt;code&gt;cargo foo&lt;/code&gt; runs &lt;code&gt;cargo-foo&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Help
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;winnow-args&lt;/code&gt; starting point was to get the best possible performance, but having nice help does not impair this need so I tried to make it as expressive as possible.&lt;/p&gt;

&lt;p&gt;Help has examples, a paragraph under a heading, and a flag's warning (0.3).&lt;/p&gt;

&lt;p&gt;A subcommand's help lists the global flags it inherits, and the default subcommand is marked, with its page after the root's. &lt;code&gt;*emphasis*&lt;/code&gt; and &lt;code&gt;**strong**&lt;/code&gt; spans are painted (0.2), and an example's line is painted word by word (0.3.2). &lt;/p&gt;

&lt;p&gt;0.4 added &lt;code&gt;verbatim_doc_comment&lt;/code&gt;, widths in terminal columns&lt;br&gt;
under the &lt;code&gt;unicode&lt;/code&gt; feature, &lt;code&gt;visible_alias&lt;/code&gt;, &lt;code&gt;long_version&lt;/code&gt;, &lt;code&gt;before_help&lt;/code&gt; and &lt;code&gt;usage&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;Help can say what each choice means (0.5). A &lt;code&gt;ValueEnum&lt;/code&gt; variant's doc comment, or &lt;code&gt;choices("fast" = "…")&lt;/code&gt; on a field, is listed one value a line by &lt;code&gt;--help&lt;/code&gt;, while &lt;code&gt;-h&lt;/code&gt; keeps the short &lt;code&gt;[possible values: …]&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A hand-written &lt;code&gt;FromArg&lt;/code&gt; can answer from data only the running program has; &lt;code&gt;brush&lt;/code&gt; uses this for the &lt;code&gt;complete -A&lt;/code&gt; and &lt;code&gt;-o&lt;/code&gt; choices, whose meanings live in brush-core.&lt;/p&gt;

&lt;p&gt;0.5 also added &lt;code&gt;hide_choices&lt;/code&gt;, &lt;code&gt;help = CONSTANT&lt;/code&gt;, and &lt;code&gt;brief_help&lt;/code&gt;, which makes &lt;code&gt;--help&lt;/code&gt; print the short page.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Deprecation
&lt;/h2&gt;

&lt;p&gt;A flag, a positional, a subcommand or an environment variable can be marked &lt;code&gt;deprecated&lt;/code&gt; (0.5). Using one prints a warning.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deprecated_warn_at&lt;/code&gt; keeps the warning quiet until a later version, and &lt;code&gt;deprecated_remove_at&lt;/code&gt; says when it goes away.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deprecated_env&lt;/code&gt; covers a variable that has been renamed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Args::parse&lt;/code&gt; prints the warnings; &lt;code&gt;Args::warnings&lt;/code&gt; returns them to a program that wants to report them itself.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Completion and pages
&lt;/h2&gt;

&lt;p&gt;A value is completed by a hint, &lt;code&gt;hint = "dir"&lt;/code&gt;, or by the program itself, &lt;code&gt;complete = function&lt;/code&gt; (0.2). &lt;/p&gt;

&lt;p&gt;The completion script asks the program, so it always matches what the program accepts.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;With &lt;code&gt;WINNOW_ARGS_SPEC&lt;/code&gt; set, the derives write a TOML description of each type at compile time (0.2). &lt;code&gt;winnow-args-man&lt;/code&gt; and &lt;code&gt;winnow-args-markdown&lt;/code&gt; turn those descriptions into pages. &lt;/p&gt;

&lt;p&gt;The program is only checked, never run, so the pages can be made for a target that can't run on the build host, this addresses one of the papercut that comes from generating man and completion from the cli.&lt;/p&gt;

&lt;p&gt;Since then the pages have picked up examples, headings, effects, outputs and exit codes (0.3), a page for each multicall command, &lt;code&gt;ls.1&lt;/code&gt; rather than &lt;code&gt;box-ls.1&lt;/code&gt; (0.4), and deprecations (0.5).&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Coming next
&lt;/h2&gt;

&lt;p&gt;I'll keep improving the crate aiming to reach feature-parity (see the &lt;a href="https://github.com/lu-zero/winnow-args/blob/master/docs/CHECKLIST.md" rel="noopener noreferrer"&gt;checklist&lt;/a&gt; tracking the features), you are welcome to use it, find bugs and send patches as usual. &lt;/p&gt;

</description>
      <category>rust</category>
      <category>cli</category>
    </item>
    <item>
      <title>winnow-args: yet another args parser</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Tue, 13 Oct 2026 05:11:14 +0000</pubDate>
      <link>https://dev.to/luzero/winnow-args-yet-another-args-parser-5cl4</link>
      <guid>https://dev.to/luzero/winnow-args-yet-another-args-parser-5cl4</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/lu-zero/winnow-args" rel="noopener noreferrer"&gt;winnow-args&lt;/a&gt; is a command line parser for Rust, built on &lt;a href="https://github.com/winnow-rs/winnow" rel="noopener noreferrer"&gt;winnow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why another one
&lt;/h2&gt;

&lt;p&gt;There are plenty of great crates already, why adding one more given that you have &lt;a href="https://crates.io/crates/clap" rel="noopener noreferrer"&gt;clap&lt;/a&gt;, &lt;a href="https://crates.io/crates/bpaf" rel="noopener noreferrer"&gt;bpaf&lt;/a&gt; and &lt;a href="https://crates.io/crates/usage-rs" rel="noopener noreferrer"&gt;usage&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;All of them are quite good for different purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;clap&lt;/strong&gt; is pretty much the standard, quite user-friendly, but bit slow and not as flexible as you want once you need to parse something more than the usual &lt;code&gt;--long&lt;/code&gt;, &lt;code&gt;-l&lt;/code&gt; options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bpaf&lt;/strong&gt; is way more flexible, but not built for speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;usage-rs&lt;/strong&gt; has a very interesting approach to get the best performance while having as many features as clap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sadly the 3 do not have at the same time the flexibility and speed I need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed on the cli?
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://github.com/reubeno/brush" rel="noopener noreferrer"&gt;brush&lt;/a&gt;, every builtin is a small command line: &lt;code&gt;set -eu&lt;/code&gt;, &lt;code&gt;declare -x NAME=value&lt;/code&gt;, &lt;code&gt;printf -v var '%s' x&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A script runs thousands of them, so the parser runs thousands of times in one process, and a parser that builds its&lt;br&gt;
description of the command at each call is paid for at each call.&lt;/p&gt;

&lt;p&gt;Builtins also have to support patterns that are not so common, e.g: &lt;code&gt;set +x&lt;/code&gt; turns off what &lt;code&gt;set -x&lt;/code&gt; turns on.&lt;/p&gt;

&lt;p&gt;I'm using &lt;strong&gt;brush&lt;/strong&gt; extensively in my portage rewrite to parse &lt;code&gt;ebuilds&lt;/code&gt;, anything that gets me a bit of speed is welcome.&lt;/p&gt;
&lt;h3&gt;
  
  
  How can a cli be annoyingly irregular?
&lt;/h3&gt;

&lt;p&gt;One of the most annoying and yet widely used cli syntax is the one used by the GNU linker and thus supported by &lt;code&gt;lld&lt;/code&gt;, &lt;code&gt;mold&lt;/code&gt; and &lt;code&gt;wild&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since I started looking at supporting more special pattern I decided to tackle also that. Sadly the &lt;code&gt;mold&lt;/code&gt; author prefers the current bespoke solution at least so far.&lt;/p&gt;

&lt;p&gt;Here few examples on how irregular it can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ld &lt;span class="nt"&gt;-shared&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; out.so &lt;span class="nt"&gt;--as-needed&lt;/span&gt; &lt;span class="nt"&gt;-lc&lt;/span&gt; a.o &lt;span class="nt"&gt;-z&lt;/span&gt; now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;long options with one dash: &lt;code&gt;-shared&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;short options with arguments: &lt;code&gt;-o&lt;/code&gt; and &lt;code&gt;-z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;flags whose order matters: &lt;code&gt;--as-needed&lt;/code&gt; applies to the libraries after it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it can get &lt;a href="https://github.com/lu-zero/winnow-args/blob/master/examples/ld.rs" rel="noopener noreferrer"&gt;even more complex&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  winnow-args
&lt;/h2&gt;

&lt;p&gt;Under the hood it leverages &lt;a href="https://github.com/winnow-rs/winnow" rel="noopener noreferrer"&gt;winnow&lt;/a&gt; zero-copy combinators over a slice of &lt;code&gt;&amp;amp;OsStr&lt;/code&gt;, that maps neatly with the native &lt;a href="https://docs.rs/winnow/1.0.4/winnow/stream/struct.BStr.html" rel="noopener noreferrer"&gt;BStr&lt;/a&gt; provided.&lt;/p&gt;

&lt;p&gt;To make it easier to use a &lt;code&gt;derive&lt;/code&gt; is provided in a fashion similar to the other crates.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A struct is a command line. &lt;/li&gt;
&lt;li&gt;A field in the struct is a flag or a positional, its doc comment is its help. &lt;/li&gt;
&lt;li&gt;Enums can be used to express possible values or subcommands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here some examples:&lt;/p&gt;
&lt;h3&gt;
  
  
  mini-mise
&lt;/h3&gt;

&lt;p&gt;This example uses subcommands, aliases, dynamic custom values, examples, runtime completion support and colored help.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;winnow_args&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;color&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Paint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Palette&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;winnow_args&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Shell&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;winnow_args&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Subcommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValueEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report_with&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cd"&gt;/// Install and run development tools&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Args,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="nd"&gt;#[arg(&lt;/span&gt;
    &lt;span class="nd"&gt;name&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tool"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;version,&lt;/span&gt;
    &lt;span class="nd"&gt;default_subcommand&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"use"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;example(&lt;/span&gt;&lt;span class="s"&gt;"tool use -g node@22"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;header&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Install a tool for every project"&lt;/span&gt;&lt;span class="nd"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;example&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tool ls --json"&lt;/span&gt;
&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Cli&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/// Show more (repeat for more)&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(short,&lt;/span&gt; &lt;span class="nd"&gt;long,&lt;/span&gt; &lt;span class="nd"&gt;count,&lt;/span&gt; &lt;span class="nd"&gt;global)]&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="cd"&gt;/// When to use color&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(long,&lt;/span&gt; &lt;span class="nd"&gt;global,&lt;/span&gt; &lt;span class="nd"&gt;default&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"auto"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;When&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(subcommand)]&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(ValueEnum,&lt;/span&gt; &lt;span class="nd"&gt;Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone,&lt;/span&gt; &lt;span class="nd"&gt;Copy)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Always&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Never&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Subcommand,&lt;/span&gt; &lt;span class="nd"&gt;Debug)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/// Install a tool and add it to the project&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(alias&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"u"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;example&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tool use node@22 python@3.13"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/// Use the global config instead of the project's&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(short,&lt;/span&gt; &lt;span class="nd"&gt;long)]&lt;/span&gt;
        &lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cd"&gt;/// How many downloads run at once&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(short,&lt;/span&gt; &lt;span class="nd"&gt;long,&lt;/span&gt; &lt;span class="nd"&gt;default&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;env&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"TOOL_JOBS"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cd"&gt;/// What to install, as `name@version`&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(positional,&lt;/span&gt; &lt;span class="nd"&gt;required,&lt;/span&gt; &lt;span class="nd"&gt;complete&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;tools)]&lt;/span&gt;
        &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="cd"&gt;/// List installed tools&lt;/span&gt;
    &lt;span class="n"&gt;Ls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/// Print JSON&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(long)]&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="cd"&gt;/// Remove a tool&lt;/span&gt;
    &lt;span class="nd"&gt;#[arg(alias&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rm"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Remove&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/// The tool to remove, as `name@version`&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(positional,&lt;/span&gt; &lt;span class="nd"&gt;complete&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;tools)]&lt;/span&gt;
        &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cd"&gt;/// Remove it even if a project uses it&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(short,&lt;/span&gt; &lt;span class="nd"&gt;long,&lt;/span&gt; &lt;span class="nd"&gt;warning&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Projects that pin it will stop working."&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="cd"&gt;/// Print a completion script&lt;/span&gt;
    &lt;span class="n"&gt;Completion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;#[arg(positional)]&lt;/span&gt;
        &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Shell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// What a shell offers for a tool, given what is typed of it.&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Candidate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"node@20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"node@22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"python@3.13"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ruby@3.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"zig@0.15"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nf"&gt;.into_iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.filter&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typed&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Candidate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args_os&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;completion_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{answer}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Help and version end up here too: `report_with` prints and says how to exit. The theme() implementation is further below.&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;report_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Completion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="py"&gt;.command&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;completion_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{cli:#?}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;h3&gt;
  
  
  Help, from the doc comments
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;-h&lt;/code&gt; is the summary and &lt;code&gt;--help&lt;/code&gt; the whole text. A subcommand's page lists the global flags it&lt;br&gt;
inherits, a flag can carry a warning, and the examples are declared, not pasted into a string.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Palettes and themes
&lt;/h3&gt;

&lt;p&gt;Help and errors are painted from a palette, and the terminal says how deep it goes: no color in a pipe or under &lt;code&gt;NO_COLOR&lt;/code&gt;, the 16 basic colors, 256, or 24 bits. &lt;/p&gt;

&lt;p&gt;A theme is a palette for each depth.&lt;br&gt;
The default one, on a 16-color and on a 256-color terminal:&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;With &lt;code&gt;NO_COLOR=1&lt;/code&gt;, or in a pipe:&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;A program brings its own with a few lines. Here one palette for 24-bit terminals, and clap's look for people who are used to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// The paints of help and errors: `TOOL_THEME` picks one.&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Paint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Paint&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// On a 24-bit terminal only; the others keep the default palettes.&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SUNSET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Palette&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;158&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.bold&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;175&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.bold&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;175&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.bold&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;187&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;247&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;247&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;142&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;247&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;118&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;142&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nn"&gt;Palette&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEFAULT_256&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TOOL_THEME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.as_deref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// clap's own look: bold and underline, color only in errors.&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"clap"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ansi16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Palette&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CLAP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ansi256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;truecolor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sunset"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;truecolor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUNSET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nn"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEFAULT&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;




&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Extended error reporting
&lt;/h3&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Completion that asks the program
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tool completion fish&lt;/code&gt; prints a short script that calls the program back, so the shell always offers what this build of the program takes. &lt;/p&gt;

&lt;p&gt;Flags come with their descriptions, a value with its choices, and &lt;code&gt;complete = tools&lt;/code&gt; above hands the question to a function of yours.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;bash, zsh, fish, elvish and PowerShell are supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exotic syntaxes
&lt;/h3&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-eu +x&lt;/code&gt; is three fields of type &lt;code&gt;Option&amp;lt;bool&amp;gt;&lt;/code&gt;, each declared &lt;code&gt;#[arg(short = 'e', plus = 'e')]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The linker's flags are an enum, and the struct holds them in the order they were given.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the hood
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxvnn5tfhs32v5y5n3y57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxvnn5tfhs32v5y5n3y57.png" alt=" " width="800" height="857"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The derive produces the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the parser&lt;/li&gt;
&lt;li&gt;the help as static data&lt;/li&gt;
&lt;li&gt;optionally a &lt;code&gt;toml&lt;/code&gt; description. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At run time the words go through a small lexer, the generated parser binds each to its field, &lt;code&gt;FromArg&lt;/code&gt; turns a word into a&lt;br&gt;
value, and a last step applies the environment, the defaults and the rules between fields.&lt;/p&gt;

&lt;p&gt;Help, the tips in errors and completion all read the same static data, and only when needed.&lt;/p&gt;

&lt;p&gt;The toml descriptions are needed by the documentation builders. They read the files the derive writes on request and they do not depend on running the code generated for the target architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The parser is generated at compile time.&lt;/strong&gt; The derive writes a &lt;code&gt;match&lt;/code&gt; over the flags of your struct. Nothing is assebled when the program starts, so a program with two hundred subcommands starts parsing as fast as one with two.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Words are borrowed.&lt;/strong&gt; Nothing is copied or re-split, and the bytes need not be UTF-8 until a field's type asks for text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistakes in the declaration are compile errors.&lt;/strong&gt; A &lt;code&gt;conflicts("nme")&lt;/code&gt; that names no field, two flags with the same letter, a positional after a list that takes every word: the build fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Help is data.&lt;/strong&gt; The prose sits in read-only memory until &lt;code&gt;--help&lt;/code&gt; asks for it, and a feature leaves it out of the binary altogether if it is not needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is a winnow parser, so you can build your own by combining the basic parsers together manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;winnow-args&lt;/strong&gt; is built essentially for speed first, comfort as close second.&lt;/p&gt;

&lt;p&gt;The repository has two benchmarks used to make sure adding features won't cause slow downs.&lt;/p&gt;

&lt;p&gt;It compares with the other argument parsers I normally use:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;a small CLI&lt;/th&gt;
&lt;th&gt;mise's 211 commands&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;winnow-args&lt;/td&gt;
&lt;td&gt;187 ns&lt;/td&gt;
&lt;td&gt;327 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;usage 7&lt;/td&gt;
&lt;td&gt;516 ns&lt;/td&gt;
&lt;td&gt;767 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clap 4&lt;/td&gt;
&lt;td&gt;15.0 µs&lt;/td&gt;
&lt;td&gt;757 µs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bpaf 0.9&lt;/td&gt;
&lt;td&gt;14.8 µs&lt;/td&gt;
&lt;td&gt;2.65 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm3ez0bx0zhpp1wl5abhx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm3ez0bx0zhpp1wl5abhx.png" alt=" " width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same command lines are written once per framework in the repository's &lt;a href="https://github.com/lu-zero/winnow-args/tree/master/benchmarks" rel="noopener noreferrer"&gt;&lt;code&gt;benchmarks/&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A program that parses once will not feel the difference with clap on a small command line. A shell does, and so does a tool with as many subcommands as mise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implemented features
&lt;/h2&gt;

&lt;p&gt;A good deal of features are already implemented, but some found on more mature parsers are still missing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;winnow-args&lt;/th&gt;
&lt;th&gt;usage&lt;/th&gt;
&lt;th&gt;clap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flags, values, positionals, subcommands, global flags, &lt;code&gt;flatten&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflicts, requirements, groups&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Help from doc comments&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completion scripts&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parser generated at compile time, words borrowed&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combinators, without a macro&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;+x&lt;/code&gt; options, as a shell builtin takes&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long options with one dash, &lt;code&gt;-z&lt;/code&gt; keywords, as GNU &lt;code&gt;ld&lt;/code&gt; takes&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flags kept in command-line order&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flag constraints checked at compile time&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Help prose left out of the binary by a feature&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A CLI built at runtime, external subcommands&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional requirements (&lt;code&gt;required_if&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Man pages, markdown&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Did you mean" suggestions&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;winnow-args-man&lt;/code&gt; and &lt;code&gt;winnow-args-markdown&lt;/code&gt; render one page per command. &lt;/p&gt;

&lt;p&gt;They do not run the program to build it: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when &lt;code&gt;WINNOW_ARGS_SPEC&lt;/code&gt; set, the derives write a description of each type while &lt;code&gt;cargo check&lt;/code&gt; or &lt;code&gt;cargo build&lt;/code&gt; expands them.&lt;/li&gt;
&lt;li&gt;the pages are built from those files. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So man pages can be built correctly even when cross compiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  In closing
&lt;/h2&gt;

&lt;p&gt;With version 0.3.0 &lt;code&gt;winnow-args&lt;/code&gt; graduated from being a quick experiment to see how far I could push the parsing performance and now it can be used to deal with the quirks of complex CLIs.&lt;/p&gt;

&lt;p&gt;Some features that I didn't need to support &lt;code&gt;brush&lt;/code&gt; and &lt;code&gt;portage-cli&lt;/code&gt; &lt;strong&gt;em&lt;/strong&gt; are still missing and I have plans to add a more flexible generator based on &lt;code&gt;minijinja&lt;/code&gt;, but I welcome you to try it and possibly provide feedback and pull requests to improve it further.&lt;/p&gt;

</description>
      <category>rust</category>
    </item>
    <item>
      <title>pseudoroot, fakeroost, hakoniwa... How to deal with unpriviledge packaging?</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Sat, 04 Jul 2026 17:52:23 +0000</pubDate>
      <link>https://dev.to/luzero/pseudoroot-fakeroost-hakoniwa-how-to-deal-with-unpriviledge-packaging-h</link>
      <guid>https://dev.to/luzero/pseudoroot-fakeroost-hakoniwa-how-to-deal-with-unpriviledge-packaging-h</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/lu-zero/portage-cli" rel="noopener noreferrer"&gt;em&lt;/a&gt; is progressing nicely, to the point that it is now very close to produce a correct &lt;a href="https://wiki.gentoo.org/wiki/Stage_file#Stage_1" rel="noopener noreferrer"&gt;stage1&lt;/a&gt; from scratch.&lt;/p&gt;

&lt;p&gt;Since one of the additional design constraint for &lt;code&gt;portage-cli&lt;/code&gt; is that I want to be able to keep everything as unprivileged as possible, now it is time to tackle that annoying bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build systems and root privileges
&lt;/h2&gt;

&lt;p&gt;Most packages usually do not care at all about having privileges, they build as whichever user you are, as long the build and install directories are writable.&lt;/p&gt;

&lt;p&gt;Some need something more because at the install phase they need to create device files using &lt;a href="https://man7.org/linux/man-pages/man1/mknod.1.html" rel="noopener noreferrer"&gt;mknod&lt;/a&gt; or just they want to have dedicated users.&lt;/p&gt;

&lt;p&gt;Nice build systems may let you override the root-only bits or just disable them, that's what &lt;a href="https://docs.brew.sh/Homebrew-on-Linux" rel="noopener noreferrer"&gt;linuxbrew&lt;/a&gt; tends to leverage. But since the plan is to produce complete Gentoo stage3 and not just user-install packages this isn't an option.&lt;/p&gt;

&lt;h2&gt;
  
  
  To root or not to root
&lt;/h2&gt;

&lt;p&gt;The traditional way get away with that is either become &lt;code&gt;root&lt;/code&gt; using &lt;code&gt;sudo&lt;/code&gt; for the install phase and be done with that, relying on some kind of &lt;a href="https://wiki.gentoo.org/wiki/Sandbox_(Portage)" rel="noopener noreferrer"&gt;sandboxing&lt;/a&gt; to avoid surprises.&lt;/p&gt;

&lt;p&gt;Another path is to simulate being root without being it, &lt;a href="https://salsa.debian.org/clint/fakeroot" rel="noopener noreferrer"&gt;fakeroot&lt;/a&gt; is a good example of this approach.&lt;/p&gt;

&lt;p&gt;A third way sort of in between, that I'm using in &lt;a href="https://github.com/lu-zero/crossdev-stages" rel="noopener noreferrer"&gt;crossdev-stages&lt;/a&gt; already, is to leverage more modern linux features to have both sandboxing AND the illusion of being root. &lt;a href="https://crates.io/crates/hakoniwa" rel="noopener noreferrer"&gt;hakoniwa&lt;/a&gt; and &lt;a href="https://github.com/containers/bubblewrap" rel="noopener noreferrer"&gt;bubblewrap&lt;/a&gt; are the best tools to achieve that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faking root
&lt;/h2&gt;

&lt;p&gt;When dealing with simulating being root one of the details to consider is how much overhead this adds.&lt;/p&gt;

&lt;p&gt;We are using this while building software and this is already time consuming in itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  LD_PRELOAD
&lt;/h3&gt;

&lt;p&gt;The venerable approach is to just rely on the dynamic linker to interpose over the libc functions we care about (&lt;a href="https://www.man7.org/linux/man-pages/man2/stat.2.html" rel="noopener noreferrer"&gt;stat&lt;/a&gt;, &lt;a href="https://www.man7.org/linux/man-pages/man2/mknod.2.html" rel="noopener noreferrer"&gt;mknod&lt;/a&gt; and so on) patch its argument and keep track of the fake creations so when its time to package the files the correct users and permissions are recorded.&lt;/p&gt;

&lt;p&gt;This works as long as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The binary is dynamically linked&lt;/li&gt;
&lt;li&gt;The dynamic linker listens to you&lt;/li&gt;
&lt;li&gt;You interpose all the functions used in the binary (e.g. stat64 vs stat)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That on Linux distributions is generally the case, but since statically linking is back fashionable we might not want to bet on it.&lt;/p&gt;

&lt;p&gt;It has potentially among the least amount of overhead as long as the permission/ownership accounting does not get in the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  ptrace
&lt;/h3&gt;

&lt;p&gt;If you have to deal with the corner case an approach is to rely on &lt;a href="https://man7.org/linux/man-pages/man2/ptrace.2.html" rel="noopener noreferrer"&gt;ptrace&lt;/a&gt; and do the patching at the &lt;strong&gt;syscall&lt;/strong&gt; level.&lt;/p&gt;

&lt;p&gt;This has an higher overhead compared to alternatives, but if we are on Linux, &lt;a href="https://www.man7.org/linux/man-pages/man2/seccomp.2.html" rel="noopener noreferrer"&gt;seccomp&lt;/a&gt; lets us be more selective, but still the per-call overhead is much larger. Since on macOS it became impractical to use, it also has portability concerns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux namespaces
&lt;/h3&gt;

&lt;p&gt;Linux &lt;a href="https://man7.org/linux/man-pages/man7/user_namespaces.7.html" rel="noopener noreferrer"&gt;namespaces&lt;/a&gt; make easy to simulate the user id (by having subid offsets) and it is pretty much 0 overhead.&lt;/p&gt;

&lt;p&gt;The only problem is that to avoid leaving a large security hole mknod cannot create new devices in the sandbox produced, so it isn't a full solution if we aim to cover also that specific bit of the image/package creation story.&lt;/p&gt;

&lt;h2&gt;
  
  
  So far
&lt;/h2&gt;

&lt;p&gt;I released &lt;a href="https://crates.io/crates/pseudoroot" rel="noopener noreferrer"&gt;pseudoroot&lt;/a&gt; since it is already overall nicer and faster than the alternatives and provides enough coverage.&lt;/p&gt;

&lt;p&gt;I'm still contributing to &lt;a href="https://github.com/koca-build/fakeroost/pull/8" rel="noopener noreferrer"&gt;fakeroost&lt;/a&gt; trying to make it faster even if it works only in Linux, since you never know when static build systems might appear, even if a sandbox based on Landlock and Seatbelt might complete the picture keeping the overhead hopefully low.&lt;/p&gt;

</description>
      <category>gentoo</category>
      <category>fakeroot</category>
      <category>packaging</category>
    </item>
    <item>
      <title>portage-cli</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Sat, 20 Jun 2026 08:58:10 +0000</pubDate>
      <link>https://dev.to/luzero/portage-cli-4b3f</link>
      <guid>https://dev.to/luzero/portage-cli-4b3f</guid>
      <description>&lt;p&gt;I'm experimenting with an alternative implementation of &lt;a href="https://codeberg.org/gentoo/portage" rel="noopener noreferrer"&gt;portage&lt;/a&gt; in Rust.&lt;/p&gt;

&lt;p&gt;It is quite different from &lt;a href="https://github.com/pkgcraft/pkgcraft" rel="noopener noreferrer"&gt;pkgcraft&lt;/a&gt; even if both projects have similar targets.&lt;/p&gt;

&lt;p&gt;Main differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separation of concerns: &lt;a href="https://github.com/lu-zero/portage-cli" rel="noopener noreferrer"&gt;portage-cli&lt;/a&gt; is based on a set of layered crates, that let people reuse specific parts of it. &lt;strong&gt;pkgcraft&lt;/strong&gt; on the other hand is a bit monolithic right now.&lt;/li&gt;
&lt;li&gt;pure-rust: &lt;strong&gt;pkgcraft&lt;/strong&gt; for correctness &lt;a href="https://github.com/pkgcraft/pkgcraft/tree/main/crates/scallop" rel="noopener noreferrer"&gt;wraps bash&lt;/a&gt;, &lt;a href="https://github.com/lu-zero/portage-cli/tree/master/portage-repo" rel="noopener noreferrer"&gt;portage-repo&lt;/a&gt; leverages &lt;a href="https://crates.io/crates/brush" rel="noopener noreferrer"&gt;brush&lt;/a&gt; fixing issues as &lt;a href="https://github.com/lu-zero/brush/tree/for-portage-repo" rel="noopener noreferrer"&gt;they are found&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;focus on different tooling: &lt;strong&gt;pkgcraft&lt;/strong&gt; offers already extensive linting support via &lt;code&gt;pkgcruft&lt;/code&gt;, &lt;strong&gt;portage-cli&lt;/strong&gt; offers an emerge workalike (&lt;code&gt;em&lt;/code&gt;) with a &lt;a href="https://github.com/pubgrub-rs/pubgrub" rel="noopener noreferrer"&gt;pubgrub&lt;/a&gt;-based dependency resolver.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some numbers
&lt;/h2&gt;

&lt;p&gt;Some comparisons with the current implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  metadata regen
&lt;/h3&gt;

&lt;p&gt;All 3 tools are able to do that and we have a &lt;a href="https://github.com/lu-zero/portage-cli/blob/master/benchmarks/scripts/compare-regen.sh" rel="noopener noreferrer"&gt;script&lt;/a&gt; to do that in a simple way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GENTOO_REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/db/repos/gentoo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;EM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;target/release/em &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;PK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../pkgcraft/target/release/pk &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;INCLUDE_EGENCACHE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;ITERATIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
./benchmarks/scripts/compare-regen.sh 8 16 20 24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tool&lt;/th&gt;
&lt;th&gt;j&lt;/th&gt;
&lt;th&gt;run&lt;/th&gt;
&lt;th&gt;real&lt;/th&gt;
&lt;th&gt;user&lt;/th&gt;
&lt;th&gt;sys&lt;/th&gt;
&lt;th&gt;files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;em&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m18.008s&lt;/td&gt;
&lt;td&gt;2m12.733s&lt;/td&gt;
&lt;td&gt;0m9.387s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;egencache&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5m24.107s&lt;/td&gt;
&lt;td&gt;0m0.003s&lt;/td&gt;
&lt;td&gt;0m0.004s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pk&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m48.226s&lt;/td&gt;
&lt;td&gt;5m27.982s&lt;/td&gt;
&lt;td&gt;1m8.484s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;em&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m10.115s&lt;/td&gt;
&lt;td&gt;2m17.674s&lt;/td&gt;
&lt;td&gt;0m11.647s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;egencache&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4m36.731s&lt;/td&gt;
&lt;td&gt;0m0.000s&lt;/td&gt;
&lt;td&gt;0m0.007s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pk&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m28.172s&lt;/td&gt;
&lt;td&gt;6m0.818s&lt;/td&gt;
&lt;td&gt;1m22.995s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;em&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m9.593s&lt;/td&gt;
&lt;td&gt;2m27.314s&lt;/td&gt;
&lt;td&gt;0m13.700s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;egencache&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4m35.749s&lt;/td&gt;
&lt;td&gt;0m0.000s&lt;/td&gt;
&lt;td&gt;0m0.006s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pk&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m23.707s&lt;/td&gt;
&lt;td&gt;5m55.650s&lt;/td&gt;
&lt;td&gt;1m33.241s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;em&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m9.229s&lt;/td&gt;
&lt;td&gt;2m39.258s&lt;/td&gt;
&lt;td&gt;0m16.244s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;egencache&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4m56.389s&lt;/td&gt;
&lt;td&gt;0m0.007s&lt;/td&gt;
&lt;td&gt;0m0.000s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pk&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0m23.367s&lt;/td&gt;
&lt;td&gt;5m54.775s&lt;/td&gt;
&lt;td&gt;1m44.188s&lt;/td&gt;
&lt;td&gt;31880&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here we see that the rust implementation are way faster and &lt;code&gt;portage-repo&lt;/code&gt; parallel visit via &lt;a href="https://crates.io/crates/jwalk" rel="noopener noreferrer"&gt;jwalk&lt;/a&gt; has an edge.&lt;/p&gt;
&lt;h3&gt;
  
  
  dependency resolution
&lt;/h3&gt;

&lt;p&gt;Only &lt;code&gt;emerge&lt;/code&gt; and &lt;code&gt;em&lt;/code&gt; have a resolver wired in and the search support.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUNS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;EM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;target/release/em ./benchmarks/bench-em-vs-emerge.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;em (mean ± σ)&lt;/th&gt;
&lt;th&gt;emerge (mean ± σ)&lt;/th&gt;
&lt;th&gt;speedup (em vs emerge)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;firefox -p&lt;/td&gt;
&lt;td&gt;0.917 s ± 0.082 s&lt;/td&gt;
&lt;td&gt;3.675 s ± 0.000 s&lt;/td&gt;
&lt;td&gt;4.01×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;libreoffice -p&lt;/td&gt;
&lt;td&gt;0.918 s ± 0.026 s&lt;/td&gt;
&lt;td&gt;3.953 s ± 0.013 s&lt;/td&gt;
&lt;td&gt;4.31×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;multi (5 pkgs) -p&lt;/td&gt;
&lt;td&gt;1.041 s ± 0.025 s&lt;/td&gt;
&lt;td&gt;4.724 s ± 0.196 s&lt;/td&gt;
&lt;td&gt;4.54×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gcc -s&lt;/td&gt;
&lt;td&gt;0.154 s ± 0.011 s&lt;/td&gt;
&lt;td&gt;5.175 s ± 0.006 s&lt;/td&gt;
&lt;td&gt;33.63×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since it is fun to watch, here an example of &lt;code&gt;em&lt;/code&gt; dealing with some &lt;strong&gt;use&lt;/strong&gt; changes compared with &lt;code&gt;emerge&lt;/code&gt;. &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;The pubgrub solver wiring covers already plenty of the niceties of emerge, but I need to test it more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress so far
&lt;/h2&gt;

&lt;p&gt;To make updating simpler all the crates are now in the same &lt;a href="https://github.com/lu-zero/portage-cli" rel="noopener noreferrer"&gt;portage-cli&lt;/a&gt; monorepo and most are already released, with few on hold waiting on having all the fixes I prepared for &lt;a href="https://github.com/reubeno/brush" rel="noopener noreferrer"&gt;brush&lt;/a&gt; landing (help reviewing and polishing very welcome).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;crates.io&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gentoo-interner&lt;/td&gt;
&lt;td&gt;0.3.1&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/gentoo-interner" rel="noopener noreferrer"&gt;crates.io/crates/gentoo-interner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gentoo-core&lt;/td&gt;
&lt;td&gt;0.5.1&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/gentoo-core" rel="noopener noreferrer"&gt;crates.io/crates/gentoo-core&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gentoo-stages&lt;/td&gt;
&lt;td&gt;0.5.1&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/gentoo-stages" rel="noopener noreferrer"&gt;crates.io/crates/gentoo-stages&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-atom&lt;/td&gt;
&lt;td&gt;0.10.0&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/portage-atom" rel="noopener noreferrer"&gt;crates.io/crates/portage-atom&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-metadata&lt;/td&gt;
&lt;td&gt;0.8.0&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/portage-metadata" rel="noopener noreferrer"&gt;crates.io/crates/portage-metadata&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-atom-pubgrub&lt;/td&gt;
&lt;td&gt;0.5.0&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/portage-atom-pubgrub" rel="noopener noreferrer"&gt;crates.io/crates/portage-atom-pubgrub&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-atom-resolvo&lt;/td&gt;
&lt;td&gt;0.7.0&lt;/td&gt;
&lt;td&gt;Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/portage-atom-resolvo" rel="noopener noreferrer"&gt;crates.io/crates/portage-atom-resolvo&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-vdb&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;Pending brush release&lt;/td&gt;
&lt;td&gt;&lt;a href="https://crates.io/crates/portage-vdb" rel="noopener noreferrer"&gt;crates.io/crates/portage-vdb&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-repo&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;Pending brush release&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-distfiles&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;Pending brush release&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;portage-cli&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;~20% complete&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Right now it is possible to enjoy a quite &lt;strong&gt;faster&lt;/strong&gt; dependency resolution, cache regeneration and search. There is some essential support for building and merging packages, but it is far from being battle tested.&lt;/p&gt;

&lt;p&gt;In the coming weeks I'll try to fill more gaps and make it more functional and hopefully blog more often.&lt;/p&gt;

</description>
      <category>gentoo</category>
      <category>llm</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Bringing up an AmpereOne system</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Sat, 28 Feb 2026 17:16:52 +0000</pubDate>
      <link>https://dev.to/luzero/bringing-up-an-ampereone-system-1ln6</link>
      <guid>https://dev.to/luzero/bringing-up-an-ampereone-system-1ln6</guid>
      <description>&lt;p&gt;I decided to buy something quite large since my workstations are aging and I wanted to build stuff quickly.&lt;/p&gt;

&lt;p&gt;I decided to buy a &lt;a href="https://www.asrockrack.com/general/productdetail.asp?Model=AMPONED8-2T/BCM" rel="noopener noreferrer"&gt;AMPONED8-2T/BCM&lt;/a&gt; with a nice 128-cores CPU and a bit of RAM.&lt;/p&gt;

&lt;p&gt;It took a bit for the people at &lt;a href="https://www.mifcom.de/" rel="noopener noreferrer"&gt;https://www.mifcom.de/&lt;/a&gt; to source all the components, but eventually I got it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;The board comes with a very pretty BMC so all you need is to connect the management one (or any since they are bonded by default) and let the dhcp get an ip and then feed it to your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboskg7izb53ibe9suqz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboskg7izb53ibe9suqz8.png" alt="BMC" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I'm using Gentoo the &lt;a href="https://www.gentoo.org/downloads/arm64/" rel="noopener noreferrer"&gt;boot media&lt;/a&gt; just worked, you may feed it directly from the Remote KVM&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3do5pwnxg010tth40v6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3do5pwnxg010tth40v6b.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just boot it from the browser&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2hr12gt71ctr2yqasnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2hr12gt71ctr2yqasnw.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the process is the usual one&lt;/p&gt;

&lt;h3&gt;
  
  
  Partition and untar
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Partition&lt;/th&gt;
&lt;th&gt;Mount point&lt;/th&gt;
&lt;th&gt;Filesystem&lt;/th&gt;
&lt;th&gt;Recommended size&lt;/th&gt;
&lt;th&gt;label&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;p1&lt;/td&gt;
&lt;td&gt;/boot/efi&lt;/td&gt;
&lt;td&gt;vfat&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;td&gt;EF00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p2&lt;/td&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;btrfs&lt;/td&gt;
&lt;td&gt;500+ GiB&lt;/td&gt;
&lt;td&gt;8300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p3&lt;/td&gt;
&lt;td&gt;/home&lt;/td&gt;
&lt;td&gt;btrfs&lt;/td&gt;
&lt;td&gt;500+ GiB&lt;/td&gt;
&lt;td&gt;8302&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkfs.vfat &lt;span class="nt"&gt;-n&lt;/span&gt; EFI /dev/nvme0n1p1
&lt;span class="nb"&gt;mkdir&lt;/span&gt; /mnt/btrfs
mkfs.btrfs &lt;span class="nt"&gt;-L&lt;/span&gt; root &lt;span class="nt"&gt;--checksum&lt;/span&gt; xxhash /dev/nvme0n1p2
mount /dev/nvme0n1p2 /mnt/btrfs
btrfs subvolume create /mnt/btrfs/@
btrfs subvolume create /mnt/btrfs/@repos
btrfs subvolume create /mnt/btrfs/@snapshots
btrfs subvolume create /mnt/btrfs/@var_log
btrfs subvolume create /mnt/btrfs/@containers
umount /mnt/btrfs

mount &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@ /dev/nvme0n1p2 /mnt/gentoo
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/gentoo/&lt;span class="o"&gt;{&lt;/span&gt;.snapshots,var/&lt;span class="o"&gt;{&lt;/span&gt;db/repos,log&lt;span class="o"&gt;}&lt;/span&gt;,var/lib/containers&lt;span class="o"&gt;}&lt;/span&gt;
mount &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@snapshots /dev/nvme0n1p2 /mnt/gentoo/.snapshots
mount &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@repos     /dev/nvme0n1p2 /mnt/gentoo/var/db/repos
mount &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@var_logs  /dev/nvme0n1p2 /mnt/gentoo/var/log
mount &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@containers /dev/nvme0n1p2 /mnt/gentoo/var/lib/containers

&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/gentoo
wget &lt;span class="o"&gt;{&lt;/span&gt;stage3&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xpf&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;stage3&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enter the stage
&lt;/h3&gt;

&lt;p&gt;Usual chroot&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Mount special filesystems&lt;/span&gt;
mount &lt;span class="nt"&gt;--types&lt;/span&gt; proc /proc /mnt/gentoo/proc
mount &lt;span class="nt"&gt;--rbind&lt;/span&gt; /sys /mnt/gentoo/sys
mount &lt;span class="nt"&gt;--make-rslave&lt;/span&gt; /mnt/gentoo/sys
mount &lt;span class="nt"&gt;--rbind&lt;/span&gt; /dev /mnt/gentoo/dev
mount &lt;span class="nt"&gt;--make-rslave&lt;/span&gt; /mnt/gentoo/dev
mount &lt;span class="nt"&gt;--bind&lt;/span&gt; /run /mnt/gentoo/run
mount &lt;span class="nt"&gt;--make-slave&lt;/span&gt; /mnt/gentoo/run

&lt;span class="c"&gt;# copy the nameservers already present&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;--dereference&lt;/span&gt; /etc/resolv.conf /mnt/gentoo/etc/

&lt;span class="c"&gt;# Enter the chroot&lt;/span&gt;
&lt;span class="nb"&gt;env&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;TERM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TERM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(chroot) &lt;/span&gt;&lt;span class="nv"&gt;$PS1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;chroot&lt;/span&gt; /mnt/gentoo /bin/bash &lt;span class="nt"&gt;--login&lt;/span&gt;

&lt;span class="c"&gt;# Inside chroot now:&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; /etc/profile
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"(chroot) &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initial bits to set up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;emerge &lt;span class="nt"&gt;--sync&lt;/span&gt;
getuto
&lt;span class="c"&gt;# Install your favourite editor, even from the binhost&lt;/span&gt;
emerge &lt;span class="nt"&gt;-G&lt;/span&gt; vim
&lt;span class="c"&gt;# do your changes&lt;/span&gt;
vim /etc/portage/make.conf
&lt;span class="c"&gt;# minimal and simple setup to to boot&lt;/span&gt;
emerge gentoo-sources grub dracut
eselect kernel &lt;span class="nb"&gt;set &lt;/span&gt;1
&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/src/linux
&lt;span class="c"&gt;# the default config works well enough&lt;/span&gt;
zcat /proc/config.gz &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .config
make olddefconfig
&lt;span class="c"&gt;# add what you need for e.g. docker or lxd&lt;/span&gt;
make menuconfig
make &lt;span class="nt"&gt;-j&lt;/span&gt; 128 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nt"&gt;-j128&lt;/span&gt; modules &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make modules_install &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# set up your user&lt;/span&gt;
emerge supeadduser
superadduser
&lt;span class="c"&gt;# set up ssh&lt;/span&gt;
emerge openssh metalog
rc-update add sshd
rc-update add metalog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All considered the whole process was quite uneventful and took literally minutes to then build additional toolchains and start doing some tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qlop &lt;span class="nt"&gt;-c&lt;/span&gt; | egrep &lt;span class="s1"&gt;'llvm|rust'&lt;/span&gt;
app-eselect/eselect-rust: 2′38″ average &lt;span class="k"&gt;for &lt;/span&gt;1 merge
dev-lang/rust: 15′19″ average &lt;span class="k"&gt;for &lt;/span&gt;1 merge
dev-lang/rust-bin: 13′21″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
dev-lang/rust-common: 42s average &lt;span class="k"&gt;for &lt;/span&gt;1 merge
dev-util/rustup: 1′42″ average &lt;span class="k"&gt;for &lt;/span&gt;1 merge
llvm-core/clang: 4′21″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-core/clang-common: 12s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-core/clang-linker-config: 5′53″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-core/clang-toolchain-symlinks: 5s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-core/llvm: 9′05″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-core/llvm-common: 2′41″ average &lt;span class="k"&gt;for &lt;/span&gt;1 merge
llvm-core/llvm-toolchain-symlinks: 11s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-core/llvmgold: 7s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/clang-rtlib-config: 5′54″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/clang-runtime: 6s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/clang-stdlib-config: 5′56″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/clang-unwindlib-config: 5′58″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/compiler-rt: 26s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/compiler-rt-sanitizers: 37s average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
llvm-runtimes/openmp: 6′10″ average &lt;span class="k"&gt;for &lt;/span&gt;2 merges
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>gentoo</category>
      <category>ampere</category>
    </item>
    <item>
      <title>cargo-c common questions</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Mon, 02 Sep 2024 09:44:38 +0000</pubDate>
      <link>https://dev.to/luzero/cargo-c-common-questions-2cdc</link>
      <guid>https://dev.to/luzero/cargo-c-common-questions-2cdc</guid>
      <description>&lt;p&gt;I wrote and maintain &lt;a href="https://crates.io/crates/cargo-c" rel="noopener noreferrer"&gt;cargo-c&lt;/a&gt; a &lt;a href="https://doc.rust-lang.org/cargo/index.html" rel="noopener noreferrer"&gt;cargo&lt;/a&gt; &lt;del&gt;applet&lt;/del&gt; custom subcommand that let you build a &lt;a href="https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html" rel="noopener noreferrer"&gt;rust crate&lt;/a&gt; sporting a C-API as a proper C library and install it along with C headers and &lt;a href="https://www.freedesktop.org/wiki/Software/pkg-config/" rel="noopener noreferrer"&gt;pkg-config&lt;/a&gt; files.&lt;/p&gt;

&lt;p&gt;When I notice some rust software sporting a C-API that I'd consider packaging in &lt;a href="https://gentoo.org" rel="noopener noreferrer"&gt;Gentoo&lt;/a&gt; I tend to provide a patch to add the few metadata entries needed to have &lt;strong&gt;cargo-c&lt;/strong&gt; do all the work, some other people politely ask if &lt;strong&gt;cargo-c&lt;/strong&gt; is supported and sometimes it is an uphill battle usually because the project maintainer doesn't know the required effort is minimum or is not aware setting &lt;code&gt;crate-type=cdylib&lt;/code&gt; is not enough.&lt;/p&gt;

&lt;p&gt;I'm writing this to explain some of the problems &lt;strong&gt;cargo-c&lt;/strong&gt; solves and hopefully give few pointers since the &lt;a href="https://github.com/lu-zero/cargo-c/blob/master/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; containing all the documentation maybe grew to be fairly big.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top 6 questions, and their answers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why this exists, why cargo isn't doing all of this on its own?
&lt;/h3&gt;

&lt;p&gt;It exists because &lt;strong&gt;cargo&lt;/strong&gt; itself does not have all the logic needed to deal with the platform specifics that come when dealing with &lt;a href="https://en.wikipedia.org/wiki/Dynamic_linker" rel="noopener noreferrer"&gt;dynamic linking&lt;/a&gt; and the details that come with &lt;strong&gt;installing&lt;/strong&gt;/&lt;strong&gt;packaging&lt;/strong&gt; a library.&lt;/p&gt;

&lt;p&gt;Depending on the platform, even if they all use ELF as binary format, you may have different rules on how to encode the version information, e.g: Linux distributions have &lt;a href="https://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html" rel="noopener noreferrer"&gt;rules on setting the version&lt;/a&gt; while Android and FreeBSD do follow others. &lt;/p&gt;

&lt;p&gt;macOS uses a different binary format and it has &lt;a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/DynamicLibraryUsageGuidelines.html" rel="noopener noreferrer"&gt;different rules&lt;/a&gt; and so does Windows.&lt;/p&gt;

&lt;p&gt;Linux distribution may have different preferred paths for the libraries, with Debian using a &lt;a href="https://wiki.debian.org/Multiarch/LibraryPathOverview" rel="noopener noreferrer"&gt;multiarch&lt;/a&gt; setup.&lt;/p&gt;

&lt;p&gt;Both dynamic library details and installing/packaging are currently out of &lt;strong&gt;cargo&lt;/strong&gt; scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  I prefer doing it on my own, why should I use cargo-c
&lt;/h3&gt;

&lt;p&gt;With &lt;strong&gt;cargo-c&lt;/strong&gt; I try to use the best practices to support as many platform as possible, trying to stay in sync with what &lt;a href="https://mesonbuild.com" rel="noopener noreferrer"&gt;meson&lt;/a&gt; does. Sadly what is conceptually trivial, installing a package, has lots of details that are platform-specific.&lt;/p&gt;

&lt;p&gt;If you want to use &lt;a href="https://crates.io/crates/cdylib-link-lines" rel="noopener noreferrer"&gt;cdylib-link-lines&lt;/a&gt; to solve the library creation problem, you still have to deal with how every platform expects the library to be installed and sadly it gets annoying.&lt;/p&gt;

&lt;h3&gt;
  
  
  I'm fine with just giving a static library, the user can copy it as they want
&lt;/h3&gt;

&lt;p&gt;You might, but the people distributing your software may then have to redo quite a bit of work, at least maintain a patch &lt;code&gt;Cargo.toml&lt;/code&gt; to add the cargo-c metadata. Some might consider that is not worth the effort and drop your package from the distribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does cargo-c require lots to set up?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;cargo-c&lt;/strong&gt; is &lt;a href="https://repology.org/project/cargo-c/versions" rel="noopener noreferrer"&gt;widely distributed&lt;/a&gt; so all you need is to add to &lt;code&gt;Cargo.toml&lt;/code&gt; the following lines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[features]&lt;/span&gt;
&lt;span class="py"&gt;capi&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;capi&lt;/code&gt; feature is used by cargo-c to know which crate in a workspace has to be made into a C library and can be used to keep the C-API within the main crate and enable it only when building with &lt;code&gt;cargo-c&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are lots of optional features to accommodate different needs and tune all the details regarding how the library, the headers and the pkg-config needs to be generated and installed.&lt;/p&gt;

&lt;p&gt;A fairly common one is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package.metadata.capi.header]&lt;/span&gt;
&lt;span class="py"&gt;generation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To not generate the header using &lt;a href="https://crates.io/crates/cbindgen" rel="noopener noreferrer"&gt;cbindgen&lt;/a&gt;, it will copy the header &lt;code&gt;{crate name}.h&lt;/code&gt; from the &lt;code&gt;assets/&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  How secure is cargo-c?
&lt;/h3&gt;

&lt;p&gt;In the wake of the &lt;a href="https://cwe.mitre.org/data/definitions/506.html" rel="noopener noreferrer"&gt;xz utils supply chain attack&lt;/a&gt; some people started caring more, so I happened to be asked about it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I rely on &lt;a href="https://deps.rs/crate/cargo-c/0.10.3+cargo-0.81.0" rel="noopener noreferrer"&gt;deps.rs&lt;/a&gt; to ensure &lt;strong&gt;cargo-c&lt;/strong&gt; is not relying on compromised and/or outdated crates.&lt;/li&gt;
&lt;li&gt;I track cargo as my upstream and I try to cut a release every time a new one is released.&lt;/li&gt;
&lt;li&gt;The code is fully auditable on github and the binaries provided are built from the CI so even that process can be checked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general any method that can be used to ensure &lt;code&gt;cargo&lt;/code&gt; itself is not compromised applies to &lt;code&gt;cargo-c&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Since cargo-c is a cargo extension, why not move it in cargo?
&lt;/h3&gt;

&lt;p&gt;In general cargo extensions exist to keep cargo as lean as possible, so the decision to keep the &lt;code&gt;install&lt;/code&gt; subcommand as bare as possible and not deal with all the details I dealt with in &lt;code&gt;cargo-c&lt;/code&gt; is intentional.&lt;/p&gt;

&lt;p&gt;Making C-libraries out of crates is not a strictly core functionality and until we do not have a stable Rust-ABI the need to produce shared/dynamic libraries in a platform-correct fashion is not a priority.&lt;/p&gt;

&lt;p&gt;Probably everything will change once there is enough progress in that regard, if somebody feel that it should be prioritized I'm open to sponsorship ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  In closing
&lt;/h2&gt;

&lt;p&gt;I hope this article helps people convince maintainers of crates trying to replace a C library to adopt it since it will spare quite a bit of headaches to distributors and make much simpler to foster adoption of memory-safer alternatives.&lt;/p&gt;

</description>
      <category>cargo</category>
      <category>rust</category>
    </item>
    <item>
      <title>Is cargo/rustc that slow?</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Fri, 30 Aug 2024 15:12:40 +0000</pubDate>
      <link>https://dev.to/luzero/is-cargorustc-that-slow-4mm9</link>
      <guid>https://dev.to/luzero/is-cargorustc-that-slow-4mm9</guid>
      <description>&lt;p&gt;As a side-discussion from the ongoing &lt;a href="https://lore.kernel.org/lkml/20240828211117.9422-1-wedsonaf@gmail.com/" rel="noopener noreferrer"&gt;mess&lt;/a&gt; I read &lt;a href="https://mstdn.party/@tragicomedy/113050650045175106" rel="noopener noreferrer"&gt;this&lt;/a&gt; about &lt;code&gt;Waiting 15 minutes for a small program, e.g. like Yazi to compile is wild.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I tried and on my laptop took about 2 minutes to build in release mode, not too bad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ cargo build
...
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 27.51s
❯ cargo clean
❯ cargo build --release
...
    Finished `release` profile [optimized] target(s) in 2m 01s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;yazi isn't exactly a small program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ cargo tree | grep -v yazi | wc -l
     685
❯ tokei
===============================================================================
 Language            Files        Lines         Code     Comments       Blanks
===============================================================================
 JSON                    1            1            1            0            0
 Lua                    34         1973         1639           31          303
 Markdown                2          207            0          149           58
 Nix                     4          270          235            6           29
 Shell                   3           56           41            6            9
 TOML                   18         1819         1484          128          207
 YAML                    1           33           29            1            3
-------------------------------------------------------------------------------
 Rust                  463        27288        22926          280         4082
 |- Markdown             6           56            0           51            5
 (Total)                          27344        22926          331         4087
===============================================================================
 Total                 526        31647        26355          601         4691
===============================================================================

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I looked a bit further and I saw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile.release]&lt;/span&gt;
&lt;span class="py"&gt;codegen-units&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="py"&gt;lto&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;panic&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"abort"&lt;/span&gt;
&lt;span class="py"&gt;strip&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Definitely not friendly if you want a fast release build&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;codegen-units = 1&lt;/code&gt; may generate a bit more optimal code but then you can use only 1 core and if you have many...&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lto = true&lt;/code&gt; tends to take lots of time for some optimality, but &lt;code&gt;lto = "thin"&lt;/code&gt; exists and the tradeoff tends to be very good, for &lt;code&gt;rav1e&lt;/code&gt; on x86_64 for a long while managed to produce even a better binary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what happens if I do those changes?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ cargo build --release
...
    Finished `release` profile [optimized] target(s) in 47.05s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I guess sometimes it is better to not stop at the first program to evaluate if a toolchain is fast or slow :)&lt;/p&gt;

&lt;h2&gt;
  
  
  P.S.
&lt;/h2&gt;

&lt;p&gt;If you are on Linux, at least right now the quickest combination seems to be &lt;code&gt;clang&lt;/code&gt; as linker with &lt;code&gt;mold&lt;/code&gt; doing the actual work.&lt;br&gt;
&lt;code&gt;gcc + mold&lt;/code&gt; seems sensibly slower and &lt;code&gt;clang + ld.bfd&lt;/code&gt; is curiously a tad slower than &lt;code&gt;gcc + ld.bfd&lt;/code&gt;.&lt;br&gt;
Setting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[target.{yourarch}]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in &lt;code&gt;~/.cargo/config.toml&lt;/code&gt; may be optimal at least right now.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cargo</category>
    </item>
    <item>
      <title>cross-stages experiments</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Fri, 02 Aug 2024 09:15:40 +0000</pubDate>
      <link>https://dev.to/luzero/cross-stages-experiments-lfm</link>
      <guid>https://dev.to/luzero/cross-stages-experiments-lfm</guid>
      <description>&lt;p&gt;I'm still playing with RISC-V and the &lt;a href="https://docs.banana-pi.org/en/BPI-F3/BananaPi_BPI-F3" rel="noopener noreferrer"&gt;bpi-f3&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support status for BPI-F3 upstream
&lt;/h2&gt;

&lt;p&gt;The upstreaming effort at least for the kernel is &lt;a href="https://patchwork.kernel.org/project/linux-riscv/list/?series=874775" rel="noopener noreferrer"&gt;ongoing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://gitee.com/bianbu-linux/" rel="noopener noreferrer"&gt;bsp&lt;/a&gt; seems actively maintained with  changes landing quite often in their fork of opensbi, u-boot and linux. Sadly being bsp and since it covers multiple boards they tend of lump lots of changes together so it can get annoying to follow what they are fixing or adding.&lt;/p&gt;

&lt;p&gt;Given enough opensource developers are getting the board or similar hardware, I hope we'll have enough eyes and hands to clean up the code in a shape it can also be upstreamed to &lt;code&gt;u-boot&lt;/code&gt; and &lt;code&gt;opensbi&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  My experiments
&lt;/h2&gt;

&lt;p&gt;I wanted to see how different would feel a whole system built to leverage at least a bit RVV 1.0 through the compiler, sadly until &lt;a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115789" rel="noopener noreferrer"&gt;this bug&lt;/a&gt; won't be addressed it would be hard to have a fully working system compiled with the autovectorizer on and I found it out the hard way.&lt;/p&gt;

&lt;p&gt;The Gentoo default way to build stages and images via &lt;a href="https://wiki.gentoo.org/wiki/Catalyst" rel="noopener noreferrer"&gt;catalyst&lt;/a&gt; relies on &lt;code&gt;qemu-user&lt;/code&gt; when no fast native system is available to avoid pitfalls of packages failing at cross-compilation. It is a bit slow but fairly reliable.&lt;/p&gt;

&lt;p&gt;Since things should improve over time, I tried to leverage &lt;a href="https://wiki.gentoo.org/wiki/Crossdev" rel="noopener noreferrer"&gt;crossdev&lt;/a&gt; and see if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] At least the stage1 packages could be cross-compiled and installed via crossdev (&lt;code&gt;perl&lt;/code&gt; seems the only package misbehaving)&lt;/li&gt;
&lt;li&gt;[x] Freshening a stage3 works. As seen with &lt;a href="https://github.com/chewi/cross-boss" rel="noopener noreferrer"&gt;cross-boss&lt;/a&gt; it feasible, I wanted to see it can be made even more straightforward.&lt;/li&gt;
&lt;li&gt;[x] Add packages and setup demons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without using &lt;code&gt;qemu-user&lt;/code&gt; so it can be made quickly, so far it mostly works.&lt;/p&gt;

&lt;p&gt;On top of it I just cross-compiled the bsp packages and copied the approach they used to assemble the final image using &lt;a href="https://github.com/pengutronix/genimage" rel="noopener noreferrer"&gt;genimage&lt;/a&gt;, trying to make it as simple as possible by using a single configuration file that does everything in a single invocation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/lu-zero/crossdev-stages" rel="noopener noreferrer"&gt;crossdev-stages&lt;/a&gt; set of scripts should let anybody with a recent Gentoo with the dependencies listed to build their images in reasonable time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;I prepared 3 scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chroot-stage.sh&lt;/code&gt; that make quite easy to fetch a stage3, unpack it and and optionally enter it using &lt;code&gt;bubblewrap&lt;/code&gt; (thanks chewi for introducing me to it).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If you already have Gentoo you can just fetch the stage&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bash chroot-stage.sh setup stage3/ riscv64
&lt;span class="c"&gt;# If you do not have Gentoo you can use the script to have a host chroot&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bash chroot-stage.sh setup my-chroot
&lt;span class="nv"&gt;$ &lt;/span&gt;bash chroot-stage.sh enter my-chroot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cross-stage.sh&lt;/code&gt; that takes care of all the building, you can go from-scratch or start from a pre-existing stage3
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This will rebuild all the stage3 using the newer compiler and the CFLAGS you set in the script&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bash cross-stage.sh update stage3/
&lt;span class="c"&gt;# Install the bare minimum needed to have the bootloader working&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bash cross-stage.sh install_boot stage3/
&lt;span class="c"&gt;# Install additional packages you may find useful&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bash cross-stage.sh install_more stage3/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;make-image.sh&lt;/code&gt; that puts everything together and build the bsp packages.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# It will produce a gentoo-linux-k1_dev-sdcard.img.xz in the build-directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bash make-image.sh /tmp/build-directory stage3/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far it works for me you are welcome to try it, help improving it or just use the pre-made &lt;a href="https://dev.gentoo.org/~lu_zero/gentoo-linux-k1_dev-sdcard.img.xz" rel="noopener noreferrer"&gt;image&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>gentoo</category>
      <category>bpif3</category>
    </item>
    <item>
      <title>Bringing up BPI-F3 - Part 3</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Tue, 04 Jun 2024 17:45:32 +0000</pubDate>
      <link>https://dev.to/luzero/bringing-up-bpi-f3-part-3-101h</link>
      <guid>https://dev.to/luzero/bringing-up-bpi-f3-part-3-101h</guid>
      <description>&lt;h2&gt;
  
  
  Initramfs
&lt;/h2&gt;

&lt;p&gt;Initially I was hoping that it would not be needed, but since the SoC has a &lt;a href="https://www.kernel.org/doc/html/latest/staging/remoteproc.html" rel="noopener noreferrer"&gt;remote processor&lt;/a&gt; and the defconfig for it enables it, I guess it is simpler to use an initramfs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remoteproc firmware
&lt;/h3&gt;

&lt;p&gt;As seen &lt;a href="https://github.com/BPI-SINOVOIP/armbian-build/commit/f4d657eda0400386bb2bf6d4db8798741afae963" rel="noopener noreferrer"&gt;here&lt;/a&gt; the remoteproc needs a firmware bit and if you happen to forget about it you'd be welcomed by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[    4.205609] remoteproc remoteproc0: rcpu_rproc is available
[    4.211421] remoteproc remoteproc0: Direct firmware load for esos.elf failed with error -2
[    4.214379] riscv-pmu-sbi: SBI PMU extension is available
[    4.219790] remoteproc remoteproc0: powering up rcpu_rproc
[    4.225306] riscv-pmu-sbi: 16 firmware and 18 hardware counters
[    4.230776] remoteproc remoteproc0: Direct firmware load for esos.elf failed with error -2
[    4.245106] remoteproc remoteproc0: request_firmware failed: -2
[    4.246235] es8326 2-0019: assuming static mclk
[    4.256170] enter spacemit_snd_sspa_pdev_probe
[    4.301833] usb 2-1: new high-speed USB device number 2 using xhci-hcd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you like to use &lt;a href="https://wiki.gentoo.org/wiki/Dracut" rel="noopener noreferrer"&gt;dracut&lt;/a&gt; all you need is to add to your &lt;code&gt;/etc/dracut.conf.d/firmware.conf&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;install_items+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" /lib/firmware/esos.elf "&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;a href="https://wiki.gentoo.org/wiki/Genkernel" rel="noopener noreferrer"&gt;Genkernel&lt;/a&gt;, set in &lt;code&gt;/etc/genkernel.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add firmware(s) to initramfs&lt;/span&gt;
&lt;span class="nv"&gt;FIRMWARE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;

&lt;span class="c"&gt;# Specify directory to pull from&lt;/span&gt;
&lt;span class="nv"&gt;FIRMWARE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/lib/firmware"&lt;/span&gt;

&lt;span class="c"&gt;# Specify a comma-separated list of firmware files or directories to include,&lt;/span&gt;
&lt;span class="c"&gt;# relative to FIRMWARE_DIR.  If empty or unset, the full contents of &lt;/span&gt;
&lt;span class="c"&gt;# FIRMWARE_DIR will be included (if FIRMWARE option above is set to YES).&lt;/span&gt;
&lt;span class="nv"&gt;FIRMWARE_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"esos.elf"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as explained &lt;a href="https://wiki.gentoo.org/wiki/Genkernel#Firmware_loading" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming next
&lt;/h2&gt;

&lt;p&gt;Now the remaining bits I'd like to have done are having a nicer u-boot configuration and hopefully wrap everything up so we can have a Gentoo image that can be simply flashed to the SD/eMMC/NVMe.&lt;/p&gt;

</description>
      <category>riscv</category>
      <category>bpif3</category>
      <category>gentoo</category>
    </item>
    <item>
      <title>Bringing up BPI-F3 - Part 2.5</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Fri, 24 May 2024 07:31:29 +0000</pubDate>
      <link>https://dev.to/luzero/bringing-up-bpi-f3-part-25-27o4</link>
      <guid>https://dev.to/luzero/bringing-up-bpi-f3-part-25-27o4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;this is a sort of intermission&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting perf to work up to a point
&lt;/h2&gt;

&lt;p&gt;Apparently the &lt;a href="https://github.com/riscv-software-src/opensbi/blob/master/docs/pmu_support.md" rel="noopener noreferrer"&gt;opensbi-mediated&lt;/a&gt; access to the performance counter does not map so using the usual &lt;code&gt;cycles&lt;/code&gt; and &lt;code&gt;instructions&lt;/code&gt; event works in &lt;code&gt;perf record&lt;/code&gt;. I got this board mainly to help with &lt;a href="https://code.videolan.org/videolan/dav1d" rel="noopener noreferrer"&gt;dav1d&lt;/a&gt; development efforts, so not having perf support would make harder to reason about performance.&lt;/p&gt;

&lt;p&gt;The best workaround after a &lt;a href="https://forum.banana-pi.org/t/perf-record-does-not-seem-to-work-is-the-device-tree-with-the-wrong-information/18051/11" rel="noopener noreferrer"&gt;discussion in the forums&lt;/a&gt;, is to build the &lt;code&gt;pmu-events&lt;/code&gt; to include custom ones and then rely on the overly precise cpu-specific events instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ perf list | grep cycle
  bus-cycles                                         [Hardware event]
  cpu-cycles OR cycles                               [Hardware event]
  ref-cycles                                         [Hardware event]
  stalled-cycles-backend OR idle-cycles-backend      [Hardware event]
  stalled-cycles-frontend OR idle-cycles-frontend    [Hardware event]
  m_mode_cycle
       [M-mode cycles]
  rtu_flush_cycle
  s_mode_cycle
       [S-mode cycles]
  stalled_cycle_backend
       [Stalled cycles backend]
  stalled_cycle_frontend
       [Stalled cycles frontend]
  u_mode_cycle
       [U-mode cycles]
  vidu_total_cycle
  vidu_vec0_cycle
  vidu_vec1_cycle
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ perf list | grep inst
  branch-instructions OR branches                    [Hardware event]
  instructions                                       [Hardware event]
  br_inst
       [Branch instructions]
  cond_br_inst
       [Conditional branch instructions]
  indirect_br_inst
       [Indirect branch instructions]
  taken_cond_br_inst
       [Taken conditional branch instructions]
  uncond_br_inst
       [Unconditional branch instructions]
instruction:
  alu_inst
       [ALU (integer) instructions]
  amo_inst
       [AMO instructions]
  atomic_inst
       [Atomic instructions]
  bus_fence_inst
       [Bus FENCE instructions]
  csr_inst
       [CSR instructions]
  div_inst
       [Division instructions]
  ecall_inst
       [ECALL instructions]
  failed_sc_inst
       [Failed SC instructions]
  fence_inst
       [FENCE instructions]
  fp_div_inst
       [Floating-point division instructions]
  fp_inst
       [Floating-point instructions]
  fp_load_inst
       [Floating-point load instructions]
  fp_store_inst
       [Floating-point store instructions]
  load_inst
       [Load instructions]
  lr_inst
       [LR instructions]
  mult_inst
       [Multiplication instructions]
  sc_inst
       [SC instructions]
  store_inst
       [Store instructions]
  unaligned_load_inst
       [Unaligned load instructions]
  unaligned_store_inst
       [Unaligned store instructions]
  vector_div_inst
       [Vector division instructions]
  vector_inst
       [Vector instructions]
  vector_load_inst
       [Vector load instructions]
  vector_store_inst
       [Vector store instructions]
  id_inst_pipedown
       [ID instruction pipedowns]
  id_one_inst_pipedown
       [ID one instruction pipedowns]
  issued_inst
       [Issued instructions]
  rf_inst_pipedown
       [RF instruction pipedowns]
  rf_one_inst_pipedown
       [RF one instruction pipedowns]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building perf
&lt;/h3&gt;

&lt;p&gt;Perf way to deal with cpu-specific events is through some machinery called jevents.&lt;/p&gt;

&lt;p&gt;It lives in &lt;code&gt;tools/perf/pmu-events&lt;/code&gt; and you can manually trigger it with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./jevents.py riscv &lt;span class="nb"&gt;arch &lt;/span&gt;pmu-events.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And produce C code from a bunch of JSON and a CSV map file.&lt;/p&gt;

&lt;p&gt;When I tried build the sources the first time I tried to cut it by setting most &lt;code&gt;NO_{}&lt;/code&gt; make variables and left &lt;code&gt;NO_JEVENTS=1&lt;/code&gt;, luckily I fixed it after noticing the different output in the forum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## I assume you have here the custom linux sources&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/src/pi-linux/tools/perf
&lt;span class="c"&gt;## being lazy I disabled about everything instead of installing dependencies, one time I disabled too much.&lt;/span&gt;
make &lt;span class="nt"&gt;-j&lt;/span&gt; 8 &lt;span class="nv"&gt;V&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;VF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;HOSTCC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-gcc &lt;span class="nv"&gt;HOSTLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-ld &lt;span class="nv"&gt;CC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-gcc &lt;span class="nv"&gt;CXX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-g++ &lt;span class="nv"&gt;AR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-ar &lt;span class="nv"&gt;LD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-ld &lt;span class="nv"&gt;NM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-nm &lt;span class="nv"&gt;PKG_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-pkg-config &lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr &lt;span class="nv"&gt;bindir_relative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bin &lt;span class="nv"&gt;tipdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;share/doc/perf-6.8 &lt;span class="s1"&gt;'EXTRA_CFLAGS=-O2 -pipe'&lt;/span&gt; &lt;span class="s1"&gt;'EXTRA_LDFLAGS=-Wl,-O1 -Wl,--as-needed'&lt;/span&gt; &lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv &lt;span class="nv"&gt;BUILD_BPF_SKEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;BUILD_NONDISTRO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;JDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;CORESIGHT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;GTK2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; feature-gtk2-infobar&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_AUXTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_BACKTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_DEMANGLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_JEVENTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;NO_JVMTI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBAUDIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBBABELTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBBIONIC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBBPF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBCAP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBCRYPTO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_LIBDW_DWARF_UNWIND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_LIBELF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_LIBNUMA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBPERL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBPFM4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBPYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBTRACEEVENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;NO_LIBUNWIND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LIBZSTD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_SDT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_SLANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_LZMA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;NO_ZLIB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;TCMALLOC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;WERROR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;LIBDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/libexec/perf-core &lt;span class="nv"&gt;libdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib64 &lt;span class="nv"&gt;plugindir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib64/perf/plugins &lt;span class="nt"&gt;-f&lt;/span&gt; Makefile.perf &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I have a &lt;code&gt;perf&lt;/code&gt; with still &lt;code&gt;cycles&lt;/code&gt; and &lt;code&gt;instructions&lt;/code&gt; not working with &lt;code&gt;perf record&lt;/code&gt;, I wonder if there is a way at opensbi or kernel level to aggregate events to make it work properly, but I never had to look into perf internals so probably I poke it way later if nobody address it otherwise, anyway&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;perf record --group -e u_mode_cycle,m_mode_cycle,s_mode_cycle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;produces something close enough for cycles, well &lt;code&gt;u_mode_cycle&lt;/code&gt; is enough.&lt;/p&gt;

&lt;p&gt;While for instructions the situation is a bit more annoying&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;perf record --group -e alu_inst,amo_inst,atomic_inst,fp_div_inst,fp_inst,fp_load_inst,fp_store_inst,load_inst,lr_inst,mult_inst,sc_inst,store_inst,unaligned_load_inst,unaligned_store_inst
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is close to count all the scalar instructions, but trying to add &lt;code&gt;vector_div_inst,vector_inst,vector_load_inst,vector_store_inst&lt;/code&gt; somehow makes perf record stop collecting samples silently, adding just 3 more events works though, so I guess I can be happy with &lt;code&gt;u_mode_cycle,alu_inst,atomic_inst,fp_inst,vector_inst&lt;/code&gt; at least.&lt;/p&gt;

</description>
      <category>bpif3</category>
    </item>
    <item>
      <title>Bringing up BPI-F3 - Part 2</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Mon, 20 May 2024 19:38:42 +0000</pubDate>
      <link>https://dev.to/luzero/bringing-up-bpi-f3-part-2-2ikj</link>
      <guid>https://dev.to/luzero/bringing-up-bpi-f3-part-2-2ikj</guid>
      <description>&lt;p&gt;This is part2, in &lt;a href="https://dev.to/luzero/bringing-up-bpi-f3-part-1-3bm4"&gt;part1&lt;/a&gt; I shown how to get a Gentoo system on the nvme taking some shortcuts. Here are the notes from the second day with some improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the eMMC
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://stijn.tintel.eu/blog/2024/05/19/compiling-uboot-bpi-f3/" rel="noopener noreferrer"&gt;stintel&lt;/a&gt; already documented how to build uboot and install it, I'm summarizing it as well here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;We need &lt;code&gt;dtc&lt;/code&gt; to compile the device-tree and &lt;code&gt;mkimage&lt;/code&gt; and git if we want to clone the trees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;emerge dtc u-boot-tools dev-vcs/git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bootloader
&lt;/h3&gt;

&lt;p&gt;This board boot process relies on &lt;a href="https://github.com/riscv/opensbi" rel="noopener noreferrer"&gt;opensbi&lt;/a&gt; + &lt;a href="https://u-boot.org" rel="noopener noreferrer"&gt;u-boot&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First build opensbi
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/BPI-SINOVOIP/pi-opensbi &lt;span class="nt"&gt;-b&lt;/span&gt; v1.3-k1
&lt;span class="nb"&gt;cd &lt;/span&gt;pi-opensbi
make &lt;span class="nv"&gt;PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;generic menuconfig
make &lt;span class="nt"&gt;-j&lt;/span&gt; 8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As &lt;a href="https://stijn.tintel.eu/blog/2024/05/19/compiling-uboot-bpi-f3/" rel="noopener noreferrer"&gt;stintel&lt;/a&gt; already reported, the default configuration is wrong, so we have to make sure that the &lt;code&gt;build/platform/generic/kconfig/.config&lt;/code&gt; has the support for K1x:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# CONFIG_PLATFORM_SPACEMIT_K1PRO is not set
CONFIG_PLATFORM_SPACEMIT_K1X=y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well &lt;code&gt;build/platform/generic/firmware/fw_dynamic.bin&lt;/code&gt; will exist.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then build u-boot
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSBI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../pi-opensbi/build/platform/generic/firmware/fw_dynamic.bin
git clone https://github.com/BPI-SINOVOIP/pi-u-boot &lt;span class="nt"&gt;-b&lt;/span&gt; v2022.10-k1
&lt;span class="nb"&gt;cd &lt;/span&gt;pi-u-boot
make k1_defconfig
make &lt;span class="nt"&gt;-j8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well it will produce the following files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSBL.bin  
bootinfo_emmc.bin  
bootinfo_sd.bin  
bootinfo_spinand.bin  
bootinfo_spinor.bin 
u-boot.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Partitions
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.banana-pi.org/en/BPI-F3/GettingStarted_BPI-F3" rel="noopener noreferrer"&gt;upstream instructions&lt;/a&gt; suggest to copy over the layout, so the &lt;code&gt;uboot&lt;/code&gt;, &lt;code&gt;bootfs&lt;/code&gt;, &lt;code&gt;rootfs&lt;/code&gt; partitions stay the same, but now the eMMC &lt;code&gt;fsbl&lt;/code&gt; and &lt;code&gt;opensbi&lt;/code&gt; are to live in their dedicated &lt;code&gt;/dev/mmcblk2boot0&lt;/code&gt;. So it is up to you to do what you prefer with them.&lt;br&gt;
Surely you have to setup &lt;code&gt;/dev/mmcblk2boot0&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo 0 &amp;gt; /sys/block/mmcblk2boot0/force_ro
dd if=bootinfo_emmc.bin of=/dev/mmcblk2boot0
dd if=FSBL.bin of=/dev/mmcblk2boot0 bs=512 seek=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As &lt;strong&gt;stintel&lt;/strong&gt; tested, all that's need is that a partition labeled &lt;code&gt;uboot&lt;/code&gt; exists and contains &lt;code&gt;u-boot.bin&lt;/code&gt; and a &lt;code&gt;bootfs&lt;/code&gt; partition that contains &lt;code&gt;env_k1-x.txt&lt;/code&gt;. It is a good idea to have &lt;code&gt;env&lt;/code&gt; and &lt;code&gt;rootfs&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kernel
&lt;/h2&gt;

&lt;p&gt;Building the kernel is straightforward enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/BPI-SINOVOIP/pi-linux &lt;span class="nt"&gt;-b&lt;/span&gt; linux-6.1.15-k1
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; pi-linux/ linux
&lt;span class="nb"&gt;cd &lt;/span&gt;linux
zcat /proc/config.gz &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .config
make oldconfig &lt;span class="c"&gt;# (or menuconfig)&lt;/span&gt;
make &lt;span class="nt"&gt;-j&lt;/span&gt; 10 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make modules_install
mount /boot &lt;span class="c"&gt;# make sure it picks the right partition&lt;/span&gt;
make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Make sure to enable &lt;code&gt;HWMON&lt;/code&gt; and &lt;code&gt;HWMON_NVME&lt;/code&gt; so you'll have the thermal sensors available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Edit &lt;code&gt;boot/env_k1-x.txt&lt;/code&gt; accordingly and your new kernel is ready.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The initial kernel has at least a known annoying bug and it is missing &lt;code&gt;HWMON&lt;/code&gt; support for its thermal sensors.&lt;br&gt;
If you use Rust, you'll notice immediately some strange issue with &lt;a href="https://bugzilla.kernel.org/show_bug.cgi?id=217923" rel="noopener noreferrer"&gt;futex()&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/BPI-SINOVOIP/pi-linux/pull/4" rel="noopener noreferrer"&gt;here&lt;/a&gt; a patch to address the bug backported to the 6.1.15 vendored kernel.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/BPI-SINOVOIP/pi-linux/pull/3" rel="noopener noreferrer"&gt;here&lt;/a&gt; my tiny patch to get something going regarding HWMON.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Coming next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Do not use at all an initrd (right now if you are following along you'd be using the initrd from the starting distro)&lt;/li&gt;
&lt;li&gt;Other u-boot tweaks&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>riscv</category>
      <category>gentoo</category>
      <category>bpif3</category>
    </item>
    <item>
      <title>Bringing up BPI-F3 - Part 1</title>
      <dc:creator>Luca Barbato</dc:creator>
      <pubDate>Sun, 19 May 2024 15:05:09 +0000</pubDate>
      <link>https://dev.to/luzero/bringing-up-bpi-f3-part-1-3bm4</link>
      <guid>https://dev.to/luzero/bringing-up-bpi-f3-part-1-3bm4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I like a lot non-x86 architectures, I still enjoy writing software for PowerPC even nowadays and I'm looking forward to see how SVE will be once there is hardware in the right price range. &lt;br&gt;
Today it is the turn of RISC-V since there is eventually a, hopefully, nice board sporting a cpu with the RISC-V Vector Extension 1.0 that is now part of the &lt;a href="https://riscv.org/technical/specifications/" rel="noopener noreferrer"&gt;RISC-V&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;The official documentation is available &lt;a href="https://docs.banana-pi.org/en/BPI-F3/BananaPi_BPI-F3" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The board comes with an empty eMMC, so either you start with a SD, or you need to upload a valid image using &lt;a href="https://docs.u-boot.org/en/latest/usage/dfu.html" rel="noopener noreferrer"&gt;dfu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sadly the usb-c does not multiplex also the serial, so you'll need to connect the usual 3 pins with a 3.3V usbserial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR:   CMD8
ERROR:   sd f! l:76
bm:0
ERROR:   emmc: invalid bootinfo magic code:0x0
ERROR:   invalid bootinfo image.
ERROR:   entering download mode.
ROM: usb download handler
usb2d_initialize : enter
Force usb FS mode!
Controller Run
usb rst int
SETUP: 0x0 0x5 0x1
SETUP: 0x80 0x6 0x100
SETUP: 0x80 0x6 0x100
SETUP: 0x80 0x6 0x302
SETUP: 0x80 0x6 0x302
SETUP: 0x80 0x6 0x301
SETUP: 0x80 0x6 0x301
SETUP: 0x80 0x6 0x30a
SETUP: 0x80 0x6 0x30a
SETUP: 0x80 0x6 0x200
SETUP: 0x80 0x6 0x200
SETUP: 0x80 0x6 0x300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Their images are Debian derivatives, I prefer &lt;a href="https://gentoo.org" rel="noopener noreferrer"&gt;Gentoo&lt;/a&gt; and since we have &lt;a href="http://distfiles.gentoo.org/releases/riscv/autobuilds/" rel="noopener noreferrer"&gt;stage3&lt;/a&gt; for &lt;code&gt;rv64_lp64d-openrc&lt;/code&gt;, I guess I can give it a try.&lt;/p&gt;

&lt;p&gt;Vendor-sources for kernel and bootloaders are available and I can piggy back over that image if I stumble over &lt;a href="https://github.com/BPI-SINOVOIP/pi-opensbi/tree/v1.3-k1" rel="noopener noreferrer"&gt;opensbi&lt;/a&gt; or &lt;a href="https://github.com/BPI-SINOVOIP/pi-u-boot/tree/v2022.10-k1" rel="noopener noreferrer"&gt;u-boot&lt;/a&gt; or I run out of time sorting out the &lt;a href="https://github.com/BPI-SINOVOIP/pi-linux/tree/linux-6.1.15-k1" rel="noopener noreferrer"&gt;patches for linux-6.1.15&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/wez/wezterm" rel="noopener noreferrer"&gt;wezterm&lt;/a&gt; or &lt;a href="https://www.gnu.org/software/screen/" rel="noopener noreferrer"&gt;screen&lt;/a&gt; to see what's going on&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://packages.gentoo.org/packages/dev-embedded/u-boot-tools" rel="noopener noreferrer"&gt;u-boot-tools&lt;/a&gt; are needed since the kernel image will  would come in a &lt;a href="https://docs.u-boot.org/en/latest/usage/fit/index.html" rel="noopener noreferrer"&gt;FIT&lt;/a&gt; image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://github.com/BPI-SINOVOIP/armbian-build/commits/v24.04.30/" rel="noopener noreferrer"&gt;armbian&lt;/a&gt; tree or any of the pre-made builds provided.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A recent &lt;a href="http://distfiles.gentoo.org/releases/riscv/autobuilds/" rel="noopener noreferrer"&gt;stage3&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lazy Setup
&lt;/h2&gt;

&lt;p&gt;The base images share this layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Number  Start (sector)    End (sector)  Size       Code  Name
   1             256             767   256.0 KiB   8300  fsbl
   2             768             895   64.0 KiB    8300  env
   3            2048            4095   1024.0 KiB  8300  opensbi
   4            4096            8191   2.0 MiB     8300  uboot
   5            8192          532479   256.0 MiB   8300  bootfs
   6          532480              &amp;lt;&amp;gt;   &amp;lt;&amp;gt; GiB     8300  rootfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the &lt;code&gt;rootfs&lt;/code&gt; taking the space it needs.&lt;/p&gt;

&lt;p&gt;The easiest way is to copy the first 5 partitions to the eMMC&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# dd if=/dev/mmcblk0 of=/dev/mmcblk2 bs=1M count=259
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then recreate the &lt;code&gt;rootfs&lt;/code&gt; partition.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!note] the initramfs does not support btrfs, nor the u-boot configuration. Until kernel and initramfs are replaced it is safer to stick to ext4 (I made this mistake).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then the usual manual unpack of a &lt;code&gt;stage3&lt;/code&gt; and chroot described in &lt;a href="https://wiki.gentoo.org/wiki/Handbook:Main_Page" rel="noopener noreferrer"&gt;Gentoo Handbook&lt;/a&gt; works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls known so far
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make sure to enable the serial ports in &lt;code&gt;/etc/inittab&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s0:12345:respawn:/sbin/agetty -L 115200 ttyS0 vt100
s1:12345:respawn:/sbin/agetty -L 115200 ttyS1 vt100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The default &lt;code&gt;u-boot&lt;/code&gt; command script is incomplete, if you want to use the nvme, you have to enter the u-boot console (press &lt;code&gt;s&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=&amp;gt; nvme scan
=&amp;gt; run nor_boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Coming next
&lt;/h2&gt;

&lt;p&gt;I hadn't tried yet to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;update u-boot and change a bit its configuration&lt;/li&gt;
&lt;li&gt;build a new kernel&lt;/li&gt;
&lt;li&gt;put everything together to have btrfs on nvme&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>riscv</category>
      <category>gentoo</category>
      <category>bpif3</category>
    </item>
  </channel>
</rss>
