Good practices
Use sh
If you do not need bash
features, then use sh
, it is installed on every UNIX-like system.
Exit on failure
Shell scripts continue even if a commend returns error. To fail right away use:
Trap C-c
Catch Control-c and exit.
Use script directory
Assume we are executing a script from directory /Admin
, where /
is the root of a given project directory.
|
script_path="${0}"
script_root="$(dirname "${script_path}")"
|
We can use ${script_root}
to call other scripts form the Admin
directory, but we can also use it to relate to the /
.
|
project_root="$(realpath "${script_root}/../")"
echo "[INFO] Entering directory: ${project_root}"
cd "${project_root}"
|
So with above we can run commands form /
(repository root). Like for example make
and other:
|
make
python3 ./Admin/serve_1.py
|
Even better
Use Python for repository maintenance scripts.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
from os import chdir
from os import path
from subprocess import run
from sys import argv
script_path = path.realpath(__file__)
script_root = path.dirname(script_path)
project_root = path.realpath(path.join(script_root, ".."))
print(f" * Entering directory: {project_root}")
chdir(project_root)
leftover_args = argv[1::]
command_arguments = ["make"] + leftover_args
cmd_string = " ".join(command_arguments)
print(f" * Executing command: {cmd_string}")
run(command_arguments, check=True)
|
Check load path
Some Elisp package compilation failures are caused by not setting the loadpath correctly. It mostly happens when you compile source from a directory that is not the current working directory. For example:
In most cases you can cd
or override the S
variable to set it to location where ELisp source resides.
But in other cases you can append to load path the directory with source, see:
|
BYTECOMPFLAGS="${BYTECOMPFLAGS} -L elisp" elisp-compile elisp/*.el
|
Do not rename auto-generated autoload file
elisp-make-autoload-file
allows to name the generated autoload file. For sake of easier debugging and writing Gentoo SITEFILE
s, please do not rename the generated file.
The name of that file should always be ${PN}-autoloads.el
.
Use new elisp-enable-tests function
elisp-enable-tests
allows to set up IUSE
, RESTRICT
, BDEPEND
and the test runner function for running tests with the specified test runner.
The 1st (test-runner
) argument must be one of:
buttercup
— for buttercup
provided via app-emacs/buttercup
,
ert-runner
— for ert-runner
provided via app-emacs/ert-runner
,
ert
— for ERT, the built-in GNU Emacs test utility.
The 2nd argument is the directory where test are located, the leftover arguments are passed to the selected test runner.
Example:
|
EAPI=8
inherit elisp
# Other package settings ...
SITEFILE="50${PN}-gentoo.el"
DOCS=( README.md )
elisp-enable-tests buttercup test
|
Remove empty SITEFILEs
Recently a feature was added to elisp.eclass
that will cause build process to generate the required SITEFILE
with boilerplate code if it does not exist.
So if your SITEFILE
looked like this:
|
(add-to-list 'load-path "@SITELISP@")
|
… then, you can just remove that file.
But remember to keep the SITEFILE
variable inside your ebuild:
|
SITEFILE="50${PN}-gentoo.el"
|
Remove pkg.el files
The *-pkg.el
files are useless to Gentoo distribution model of Emacs Lisp packages and should be removed. It is as simple as adding this line to a ebuild:
|
ELISP_REMOVE="${PN}-pkg.el"
|
Beware that some packages will try to find their ${PN}-pkg.el
file, but in most cases this will show up in failing package tests.
Use official repository
It is tedious to repackage Elpa tarballs, so use the official upstream even if you have to snapshot a specific commit.
To snapshot GitHub repos you would generally use this code:
|
# First check if we have the correct version to prevent
# autobumping package version without changing the commit.
[[ ${PV} == *_p20220325 ]] && COMMIT=65c496d3d1d1298345beb9845840067bffb2ffd8
# Use correct URL that supports snapshots.
SRC_URI="https://github.com/domtronn/${PN}/archive/${COMMIT}.tar.gz
-> ${P}.tar.gz"
# Override the temporary build directory variable.
S="${WORKDIR}"/${PN}-${COMMIT}
|
Include live version support
We do not want to be worse than the Melpa unstable :D
So, why not allow the given package to be used live?
Even if you do not push the live package to the overlay, please include support for it.
|
if [[ ${PV} == *9999* ]] ; then
inherit git-r3
EGIT_REPO_URI="https://github.com/example/${PN}.git"
else
SRC_URI="https://github.com/example/${PN}/archive/${PV}.tar.gz
-> ${P}.tar.gz"
KEYWORDS="~amd64 ~x86"
fi
|
Git is good, git tags are good. In case if upstream does not tag their package or just forgets to, kindly ask them to create a git tag when bumping Emacs package versions.
Constructing debugging syntax
I wanted to echo parameter values when I set them in my blog’s frog.rkt
config file.
Nothing simpler in Racket!
First I create this macro for echoing a single parameter value when it is set:
1
2
3
4
5
6
7
8
9
10
11
12 |
(define-syntax-rule (verbose-set-parameter parameter-id parameter-value)
(begin
;; Set the parameter.
(parameter-id parameter-value)
;; Then call the parameter and print is's value.
;; The "'parameter-id" is special syntax
;; for turning a "parameter-id" identifier to a symbol.
;; We can also write it like:
;; > (quote parameter-id)
;; to be less confusing.
(printf "[DEBUG] (~a ~v)\n" 'parameter-id (parameter-id))))
|
then, I create a wrapper for above macro that can take multiple parameter pairs:
|
(define-syntax-rule (verbose-set-parameters (parameter-id parameter-value) ...)
(begin
;; Unpack a chain of "(parameter-id parameter-value)" pairs
;; using the "..." syntax.
(verbose-set-parameter parameter-id parameter-value) ...))
|
Using the macro
Afterwards we can call it like so:
|
(verbose-set-parameters
(current-title "XGQT's blog")
(current-author "Maciej Barć"))
|
Notice that even the form of setting a parameter, that is (parameter-procedure "value")
, remains the same, but in reality it is just similar to how the syntax macro pattern-matches on it.
Inspecting macro expansion
In racket-mode
inside GNU Emacs we can inspect the macro expansion with racket-expand-region
. Stepping through the expansion provided this result:
|
(begin
(begin
(current-title "XGQT's blog")
(printf "[DEBUG] (~a ~v)\n" 'current-title (current-title)))
(begin
(current-author "Maciej Barć")
(printf "[DEBUG] (~a ~v)\n" 'current-author (current-author))))
|
Unwanted (VDI) present
Recently I was given a task to run a outdated piece of commercial software on a very outdated (in)famous operating system. Unlucky for me on the given VDI (VirtualBox disk image) the so-called “Guests Additions” that enabled “better” time synchronization were already installed.
Fighting VBox time
So, this is what I had to do to keep the old clock:
- turn off networking card in the virtual machine configuration, because we do not want the system to use NTP or other newt service to get the “new” time,
- disable getting the host’s clock time:
VBoxManage setextradata "WinXP"
"VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 1
,
- and… this magic:
VBoxManage modifyvm "WinXP"
--biossystemtimeoffset "-341597644449"
, this thing does the very weird thing of telling the virtual machine BIOS how much to set it back in time, the offset
here is total milliseconds between some old date and current date.
Getting the old date
With the Python script snippet below you will get the milliseconds to set back the VirtualBox clock to 01.01.2012 01:00
.
|
from datetime import datetime
round((datetime.strptime("01.01.2012 01:00", "%d.%m.%Y %H:%M")
- datetime.now()).total_seconds() * 1000)
|
Equality methods
By implementing a method for equality equal-to?
and two extraction methods equal-hash-code-of
and equal-secondary-hash-code-of
we can define our own object comparison rules.
For more info see Object Equality and Hashing.
Consider the following example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
|
If we create a new integer%
object we can notice that it is not transparent (we can not inspect values of any of it’s fields).
|
(new integer%)
;; => (object:integer% ...)
|
But if we compare two fresh integer%
objects they will be equal.
Transparent class
A transparent cvlass is a class with the inspect
expression valuye se to #false
.
From Racket documentation Creating Classes:
Just as for structure types, an inspector controls access to the class’s fields, including private fields, and also affects comparisons using equal?.
Consider the following example:
If we create a new integer%
object we can see it’s field values.
|
(new integer%)
;; => (object:integer% 0)
|
And if we compare two fresh integer%
objects they will be equal.
I have moved my main Fediverse account from @xgqt@fosstodon.org to @xgqt@emacs.ch.
The new account’s RSS feed can be found at emacs.ch/users/xgqt.rss.
Config
First let’s prepare a suitable nginx configuration file.
This one is pretty bare but it works well for our case:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 |
worker_processes 1;
daemon off;
pid ./nginx/temp/nginx.pid;
error_log /dev/stdout info;
events {
worker_connections 1024;
}
http {
client_body_temp_path ./nginx/temp/client 1 2;
proxy_temp_path ./nginx/temp/proxy;
fastcgi_temp_path ./nginx/temp/fastcgi;
uwsgi_temp_path ./nginx/temp/uwsgi;
scgi_temp_path ./nginx/temp/scgi;
server {
listen 127.0.0.1:8080;
server_name localhost;
access_log /dev/stdout;
error_log /dev/stdout info;
root ./;
location / {
autoindex on;
}
}
}
|
Server config is set up for serving all static files from the current directory.
Startup
Preparation
Based on how you want to store _temp_path
files it might be necessary to create (or clean up) additional directories, for example:
|
rm -r ./nginx/temp
mkdir -p ./nginx/temp
|
Run in current directory
|
nginx -c ./nginx.conf -p ./
|
BTW, you may want to replace ./
with "$(pwd)"
and occurrences in the config with static paths.
Bonus: other simple servers
Some of no-dependency-except-itself http servers it’s good to know about:
Python http.server
|
python3 -m http.server -b 127.0.0.1 8080
|
Busybox
|
busybox httpd -f -p 127.0.0.1:8080 -v
|
You can read more about configuring busybox’s httpd on OpenWRT docs.