hhl/public/news/porting-netbsd-userland-infrastructure-improvements/index.html
2021-02-19 22:43:36 -05:00

447 lines
22 KiB
HTML

<!DOCTYPE html>
<html lang="en" class="sidebar-visible no-js light">
<head>
<meta charset="utf-8">
<title>Hitch Hiker Linux</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="&#x2F;favicon.svg">
<link rel="shortcut icon" href="&#x2F;favicon.png">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;css&#x2F;variables.css">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;css&#x2F;general.css">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;css&#x2F;chrome.css">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;css&#x2F;print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="&#x2F;handbook&#x2F;FontAwesome&#x2F;css&#x2F;font-awesome.css">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;fonts&#x2F;fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="&#x2F;handbook&#x2F;highlight.css">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;tomorrow-night.css">
<link rel="stylesheet" href="&#x2F;handbook&#x2F;ayu-highlight.css">
<!-- Custom Stylesheets -->
<link rel="stylesheet" href="&#x2F;hhl.css">
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "&#x2F;handbook";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter">
<li class="chapter-item affix ">
<a href="/">Home</a>
</li>
<li class="chapter-item affix ">
<a href="/news/">News</a>
</li>
<li class="chapter-item affix ">
<a href="/about/">About</a>
</li>
<li class="chapter-item affix ">
<a href="/pub/">Download</a>
</li>
<li class="chapter-item affix ">
<a href="https://git.hitchhiker-linux.org">Source</a>
</li>
<li class="spacer"></li>
<li class="chapter-item affix ">
<a href="/handbook/">Handbook</a>
</li>
</ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
</div>
<h1 class="menu-title">
Hitch Hiker Linux
</h1>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1><a class="header" href="#recent" id="recent">Porting NetBSD userland, infrastructure improvements</a></h1>
<p class="subtitle"><strong>2020-08-17</strong></p>
<p>As noted previously, I have some issues with the complexity of GNU autotools and the bloat that it introduces into building what should be very simple programs. I pointed out coreutils as one of the worst offenders at that time, and have been exploring ways to either port coreutils to a simpler build system or outright replace the package.<span id="continue-reading"></span></p>
<p>In context, there is nothing particularly wrong with the utilities themselves and functionally they work as expected. One benefit that coreutils provide over almost every other implementation is improved localization. However, there is complexity that is at times not really justified. Let's take the utility program &quot;true&quot; as an example, whose sole purpose is to do nothing and exit with success. A simple C program can be constructed to perform this function perfectly well with the following code:</p>
<pre style="background-color:#2b303b;">
<code class="language-C" data-lang="C"><span style="color:#b48ead;">#include </span><span style="color:#c0c5ce;">&lt;</span><span style="color:#a3be8c;">stdio.h</span><span style="color:#c0c5ce;">&gt;
</span><span style="color:#b48ead;">int </span><span style="color:#8fa1b3;">main</span><span style="color:#c0c5ce;">() { </span><span style="color:#b48ead;">return </span><span style="color:#d08770;">0</span><span style="color:#c0c5ce;">; }
</span></code></pre>
<p>This simple code compiles to a 16k executable (stripped) and does everything that we need it to do. But we can do even better actually, with a single line shell script:</p>
<pre style="background-color:#2b303b;">
<code class="language-Bash" data-lang="Bash"><span style="color:#c0c5ce;">#!/bin/sh
exit 0
</span></code></pre>
<p>This little gem reads as a whopping 4k on disk on my machine and also fulfills exactly what we expect the program to do. So why exactly is the file true.c in coreutils 80 lines that compiles to a 40k executable after stripping?</p>
<p>I had at one point tried replacing coreutils and util-linux completely with the sbase and ubase packages from <a href="https://suckless.org/">suckless.org</a>. The main issue with doing this is that not all of the utilities are feature complete yet. They are fine for day to day use navigating around in a shell environment, but begin to fail when running scripts that are meant to be portable or building software, when an unsupported flag causes the utility call to fail with an error. I do have a branch containing the entirety of sbase and ubase with the source reconfigured to match our build tree layout of one program per directory, and building using hhl.cprog.mk.</p>
<p>I have always had a lot of admiration for BSD systems and ran FreeBSD as my main OS on several machines for a number of years. Too much is actually made of the differences between a BSD userland nad a GNU userland, as the vast majority of the time they are functionally equivalent. As mentioned previously, the GNU utilities have better localization. They also accept GNU long options. I have never found the lack of long options to be an issue; on the contrary short options are faster to type and are generally the go to choice for those familiar with the shell interface. Therefore I decided to start working on porting BSD userland to Linux with Glibc, using our HitchHiker build system. As there are a great many small utilities this is a process that isn't going to happen overnight, but has already yielded a surprising amount of success with modest effort.</p>
<p>There are a few projects already in place that do something similar, such as <a href="https://github.com/Duncaen/lobase">lobase</a>, that approach the issues of missing functions and macros by creating a compatibility library and then linking the utilities to it. While this is a perfectly valid approach, it has the drawback of increasing code size somewhat. I have also found so far that simply removing macros that don't exist on a Glibc system is enough to get the code to compile with a simple <code>gcc -Wall file.c</code>. At other times one can simply translate from one function to another, for instance from strlcpy to strncpy, the latter which is available with Glibc and does very near the same thing.</p>
<p>Without getting too further involved in the details, so far I have working copies of the utilities apply, banner, basename, cat, dirname, grep and tr taken from NetBSD. Grep was previously a separate package that has been removed. We previously already had the awesome pax utility imported from MirOS, and have now removed GNU tar in favor of a simlink to pax, which functions very well as a tar replacement. Additionally, the utilities true, false, and which have been replaced with one line shell scripts (which is a zsh builtin, which we can call from another shell with a one line zsh script).</p>
<p>The result so far is a mixed userland that is predominately composed of GNU utilities with a sprinkling of BSD licensed utilities thrown in. The endgame is to replace the bulk of the GNU utilities with ports of the BSD versions, including many utilities such as apply, banner and pax that do not traditionally even exist on a Linux system. I may supplement this over time with utilities taken from sbase, ubase, or lobase as difficulties are encountered in porting the NetBSD utils, but would like to do as much straight porting against the actual system libraries as possible, as opposed to linking against compatibility libraries.</p>
<p>On to the build tree infrastructure improvements mentioned in the post title. A few posts back I mentioned standardizing the way that we handle packages that need extra steps beyond &quot;make install&quot;. Similarly, I'm working on simplifying the use of build systems other than autotools. One feature of autotools that we leverage by default is that we can build outside of the source tree in a separate object directory. This is the default, but not all packages support this. There are even a few packages out there that mostly mimic autotools from an end user perspective, having a home-brewed configure script, but generally do not support building in an object directly. So now by simply setting the variable ${no_objdir} we can handle those cases in a unified way, and our build system knows that ${objdir}/.dirstamp is not a dependency of the configuration stage for instance.</p>
<p>Additionally, some packages eschew any kind of configuration step entirely and build just by running make. We now handle this in a more unified manner as well, rather than on a per-package basis, by setting <code>${use_configure}</code> to false.</p>
<p>These changes are minor, but are going to go a long way towards implementing a ports tree with a more concise and understandable codebase.</p>
<hr>
<p class="subtitle"><strong>Tags for this post:</strong></p>
<a class="tags" href="&#x2F;tags&#x2F;nongnu&#x2F;">NonGNU</a>
<a class="tags" href="&#x2F;tags&#x2F;porting&#x2F;">Porting</a>
</main>
</div>
</div>
</div>
<script>
(function themes() {
var html = document.querySelector('html');
var themeToggleButton = document.getElementById('theme-toggle');
var themePopup = document.getElementById('theme-list');
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
var stylesheets = {
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
highlight: document.querySelector("[href$='highlight.css']"),
};
function showThemes() {
themePopup.style.display = 'block';
themeToggleButton.setAttribute('aria-expanded', true);
themePopup.querySelector("button#" + get_theme()).focus();
}
function hideThemes() {
themePopup.style.display = 'none';
themeToggleButton.setAttribute('aria-expanded', false);
themeToggleButton.focus();
}
function get_theme() {
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
if (theme === null || theme === undefined) {
return default_theme;
} else {
return theme;
}
}
function set_theme(theme, store = true) {
let ace_theme;
if (theme == 'coal' || theme == 'navy') {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = false;
stylesheets.highlight.disabled = true;
ace_theme = "ace/theme/tomorrow_night";
} else if (theme == 'ayu') {
stylesheets.ayuHighlight.disabled = false;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = true;
ace_theme = "ace/theme/tomorrow_night";
} else {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = false;
ace_theme = "ace/theme/dawn";
}
setTimeout(function () {
themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
}, 1);
if (window.ace && window.editors) {
window.editors.forEach(function (editor) {
editor.setTheme(ace_theme);
});
}
var previousTheme = get_theme();
if (store) {
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
}
html.classList.remove(previousTheme);
html.classList.add(theme);
}
// Set theme
var theme = get_theme();
set_theme(theme, false);
themeToggleButton.addEventListener('click', function () {
if (themePopup.style.display === 'block') {
hideThemes();
} else {
showThemes();
}
});
themePopup.addEventListener('click', function (e) {
var theme = e.target.id || e.target.parentElement.id;
set_theme(theme);
});
themePopup.addEventListener('focusout', function(e) {
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
hideThemes();
}
});
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
document.addEventListener('click', function(e) {
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
hideThemes();
}
});
document.addEventListener('keydown', function (e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
if (!themePopup.contains(e.target)) { return; }
switch (e.key) {
case 'Escape':
e.preventDefault();
hideThemes();
break;
case 'ArrowUp':
e.preventDefault();
var li = document.activeElement.parentElement;
if (li && li.previousElementSibling) {
li.previousElementSibling.querySelector('button').focus();
}
break;
case 'ArrowDown':
e.preventDefault();
var li = document.activeElement.parentElement;
if (li && li.nextElementSibling) {
li.nextElementSibling.querySelector('button').focus();
}
break;
case 'Home':
e.preventDefault();
themePopup.querySelector('li:first-child button').focus();
break;
case 'End':
e.preventDefault();
themePopup.querySelector('li:last-child button').focus();
break;
}
});
})();
(function sidebar() {
var html = document.querySelector("html");
var sidebar = document.getElementById("sidebar");
var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle");
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
var firstContact = null;
function showSidebar() {
html.classList.remove('sidebar-hidden')
html.classList.add('sidebar-visible');
Array.from(sidebarLinks).forEach(function (link) {
link.setAttribute('tabIndex', 0);
});
sidebarToggleButton.setAttribute('aria-expanded', true);
sidebar.setAttribute('aria-hidden', false);
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
}
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
function toggleSection(ev) {
ev.currentTarget.parentElement.classList.toggle('expanded');
}
Array.from(sidebarAnchorToggles).forEach(function (el) {
el.addEventListener('click', toggleSection);
});
function hideSidebar() {
html.classList.remove('sidebar-visible')
html.classList.add('sidebar-hidden');
Array.from(sidebarLinks).forEach(function (link) {
link.setAttribute('tabIndex', -1);
});
sidebarToggleButton.setAttribute('aria-expanded', false);
sidebar.setAttribute('aria-hidden', true);
try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
}
// Toggle sidebar
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
if (html.classList.contains("sidebar-hidden")) {
var current_width = parseInt(
document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
if (current_width < 150) {
document.documentElement.style.setProperty('--sidebar-width', '150px');
}
showSidebar();
} else if (html.classList.contains("sidebar-visible")) {
hideSidebar();
} else {
if (getComputedStyle(sidebar)['transform'] === 'none') {
hideSidebar();
} else {
showSidebar();
}
}
});
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
function initResize(e) {
window.addEventListener('mousemove', resize, false);
window.addEventListener('mouseup', stopResize, false);
html.classList.add('sidebar-resizing');
}
function resize(e) {
var pos = (e.clientX - sidebar.offsetLeft);
if (pos < 20) {
hideSidebar();
} else {
if (html.classList.contains("sidebar-hidden")) {
showSidebar();
}
pos = Math.min(pos, window.innerWidth - 100);
document.documentElement.style.setProperty('--sidebar-width', pos + 'px');
}
}
//on mouseup remove windows functions mousemove & mouseup
function stopResize(e) {
html.classList.remove('sidebar-resizing');
window.removeEventListener('mousemove', resize, false);
window.removeEventListener('mouseup', stopResize, false);
}
document.addEventListener('touchstart', function (e) {
firstContact = {
x: e.touches[0].clientX,
time: Date.now()
};
}, { passive: true });
document.addEventListener('touchmove', function (e) {
if (!firstContact)
return;
var curX = e.touches[0].clientX;
var xDiff = curX - firstContact.x,
tDiff = Date.now() - firstContact.time;
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
showSidebar();
else if (xDiff < 0 && curX < 300)
hideSidebar();
firstContact = null;
}
}, { passive: true });
// Scroll sidebar to current active section
var activeSection = document.getElementById("sidebar").querySelector(".active");
if (activeSection) {
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
activeSection.scrollIntoView({ block: 'center' });
}
})();
</script>
</body>
</html>