This document is a little all over the place--I've been working on it for a while, but I keep refactoring it as I move pieces of it into man pages and whatnot. It's mostly a brain dump, sorry. There are three parts to this; procedures, "content" guidelines and "style" guidelines. The style guidelines are less strict; as long as things are consistent at the file-level, I'm pretty happy. Contributing ============ I'd love to have your patches! Code should be hackable; if you want to modify something, but can't figure out how: 1) ping me for help, 2) it probably means the code was too complicated in the first place. Patches should be sent to ; please put "[PATCH]" and "libretools" in the subject line. If you have commit access, but want me to look over it first, feel free to create a new branch in git, and I will notice it. Try to avoid pushing to the "master" branch unless it's a trivial change; it makes it easier to review things; though I *will* look over every commit before I do a release, so don't think you can sneak something in :) I'd love to discuss possible changes on IRC (I'm lukeshu), either on irc.freenode.net#parabola or in personal messages. My account may be online even if I'm not; I will eventually see your it, I do a search for mentions of "luke" on #parabola every time I get on. Testing ======= Please write unit tests for new things. Tests can be run with `make check`, which just runs `./testenv roundup` in the `test/` directory. Relatedly, you need the `roundup` tool to run the tests. `./testenv` can be given `--no-network` and/or `--no-sudo` to dissable tests that require those things. Make can be made to pass those things in by setting `TESTENVFLAGS`. If you don't dissable either, I *strongly* recommend setting TMPDIR to somewhere on a btrfs partition before running the tests; otherwise the chroot tests will take forever. I mean, they already take long on btrfs, but without it... _dang_. Code content ============ Be aware of the `librelib(7)` library suite, which lives `src/lib`. It is a suite of Bash libraries that will help you out. Most of the people looking at the libretools code are familiar with the `messages` part of it, which actually contains a much of utility routines, not just message printing. There is also a library for dealing with `blacklist.txt`, and one for loading configuration files and PKGBUILDs. These are common tasks, but are tricky to handle consistently--the libraries are there to make things easier. Take a look at the man pages. Message printing: All of the message printing routines, except for `term_title` and `flag`, take printf-type arguments. Take advantage of that; don't use string interpolation (don't do `"foo ${var} bar"`). The reason for this is that if you don't do string interpolation, messages can be automatically internationalized. (Internationalization is incomplete at the momement) Message printing: The in `--help`/`-h` text, use `print` to print lines that should not wrap, `echo` to print blank lines, `prose` to print paragraphs, `bullet` to print bullet points, and `flag` to print option flags. The text should follow this general format: print "Usage: %s [OPTIONS] VARS_ARE_UNDERSCORE_AND_CAPITAL" "${program_name}" print "One line description of program, no period" echo prose "More details. This is a paragraph." echo print "Options:" flag "-h" "Show this message" In the "Usage:" line, use printf `%s` and the value `"${0##*/}"` to determine the program name at runtime. There used to be guidelines for how to align the option flags and descriptions, but now the `flag` command exists takes care of it for you. Yay for things being easier! When using `set -u`, `set -e`, or `trap`, you should also use `set -E` to have the error handling be passed down to subshells. Feel free to use `set -e` (fail on error), but be careful of the caveats (there are a bunch of them); don't assume all errors are checked because of it. Use `set -u` if you can; it makes using an unset variable an error. - If a variable not being set is valid (perhaps a configuration option), use `${var:-}` when accessing it to suppress the error. - An empty array counts as unset, so if you have an array that may be empty, use `set +u` before accessing it. - The reason for this is that a normal string variable is basically an array with length=1; an unset variable looks like an array with length=0. Weird stuff. In the shebang, use `#!/usr/bin/env bash`. This allows us to not hardcode the location of bash (I'm not sure why this is useful for something distro-dependent like libretools, but fauno seems to have a use-case for it). In the shebang, don't pass flags to bash, besides breaking `env` (above), it means people will make mistakes when debugging, and running things with `bash FILENAME`. Instead, use `set` to adjust the flags inside of the program. Obey `$TMPDIR`. It's usually as easy as passing `--tmpdir` to `mktemp`. Use `trap` to clean up your temporary files. This way, even if your program terminates early, things will be cleaned up. Bash best practices =================== Basically, know what you are doing, and be safe with it. The problem is that most people don't know about safe bash scripting. A lot of people look at the "Advanced Bash Scripting" ebook--DO NOT do that, it is trash... though it contains a "reference card" page that may be useful and isn't trash. Take a look at Gentoo's Bash guidelines . They're pretty good, and cover most of the "gotcha's" about Bash syntax. It mentions but discourages the use of Bash 3 features... why? Who still uses Bash 2? Feel free to use Bash 4 features! I wrote an article on Bash arrays . A lot of people think they're tricky, but they're simple once you know how they work. It's short enough that you should read the whole thing. Know the difference between `"${array[@]}"` and `"${array[*]}"`. And I'll say it again here, ALWAYS wrap those in double quotes; there is no reason I can think of that the unquoted behavior would ever be the correct thing. My brief rules of thumb: - Quote every variable. - That includes arrays: `"${array[@]}"` and `"${array[*]}"`. - In most (but not all!) cases inside of `[[ ... ]]` conditions, variables don't need to be quoted. When in doubt, quote them. - When assigning one variable to another, you don't need quotes; you don't need quotes for `foo=$bar` - Try to avoid global variables; declare all variables in functions with `local`. - Or `declare`; inside of a function, unless you pass the `-g` flag, `declare` makes the variable local. - Use `local VAR` before a `for VAR in LIST` loop--the variable is created in the current scope, not the scope of the loop. - Feeding input to `while` loops is weird because of how subshells work: # Input from a file # BAD cat file | while read line; do ... done # GOOD while read line; do ... done