Meta: README STYLEGUIDE TUTORIAL NEWS

Tools: con-bs con-dev con-dev-release-multi con-lib2rst con-os con-vc

Libraries: con-lib con-libbs con-libbs.autotools con-libbs.cmake con-libbs.make con-libconf con-libdev con-libdev-meta con-libdev-release con-libdev-setup con-libopt con-libos con-libos.debian con-libstaging con-libsys con-libvc con-libvc.git con-libvc.svn


con-dev style guide

Contents

Bash coding style guide

Temporary script update checklist

  • Adapt all function names to convention.
  • Update all log output to con::log*.
  • Update plain "sudo" calls: sed -i 's/sudo/$(con_opt::get "S")/g'.
  • Use "always quote" style.
  • Update wording style.

Standard Compliance and Compatibility Notes

We want con script code to be able to just use anything that you usually always expect to be available on a unixoid system installation.

To make this assertion little more tangible, we define that we may just use unqualified (i.e., use cat not /bin/cat):

  • all builtin functionality of bash >=4.
  • all commands available via coreutils.
  • all commands available via util-linux.
  • all commands available via findutils.

It's recommended, but not mandatory, to use (non-bash) tools in a standard way (POSIX or de-facto) so they may work an many systems. This is a practical approach aimed to avoid most incompatiblities while remaining feasabilty (as opposed to restrict this to POSIX). For example, on a Debian system, you can get a list of command you may use beyond bash like so:

? dpkg -L util-linux coreutils findutils | grep bin | xargs --max-lines=1 basename

Bottom line: For anything beyond that, you should use con::cmd.

Error Handling

A fatal error in a script is any error condition not handled by the error handling of the script itself.

At any time, we want a script to stop immediately on such a fatal error.

Shell options

Any code is run with: errexit, pipefail, errtrace and, since bash 4.4, nounset.

Note

In your code, you can expect nounset to behave like in bash from version 4.4 onward. I.e., you can expand empty arrays in natural fashion like so:

declare -a ARRAY=()
declare a
for a in "${ARRAY[@]}"; do
  ...
done

In bash versions < 4.4, this used to error out with nounset. So for lower but supported bash versions, con-dev will not set nounset.

Note

These options are enforced implicitly when bootstrapping with the con base library, no matter what your shebang may say.

-o errexit: Any line of command in your script returning with a non-zero exit state is a fatal error:

false
cat /idontexists

Note

errexit is deliberately disabled in some special parts of bash code.

This is some compatibility thing, and seemingly will not be fixed, even not with a bashism, see:

So, if you have functions that you use this way (as if/else/while condition or such), use the || return 1 idiom as a workaround on any crucial call - this will at least make your code not continue, and the function fail:

myprog_f()
{
  command1 || return 1
  command2 || return 1
  doStuff  # not needed for last command
}
if myprog_f; then
  printf "Ok, f() run successfully."
fi

-o pipefail: Any command failing in a pipeline is considered a fatal error.

-o errtrace: Make ERR trap also work in functions and subshells (for con-dev built-in error handling).

Examples for fatal errors:

false         # Fatal error (errexit)
$(false)      # Fatal error: Subshell fails (errexit)
A=$(false)    # Fatal error: Subshell fails when assigning (errexit)
false | true  # Fatal error: First command in pipeline fails (errexit+pipefail)

Declaring variables

# DONT USE: "declare" or "local" will succeed even if my_func() doesn't.
declare myVar="$(my_func arg1)"
local   myVar="$(my_func arg1)"

# Rather use
declare myVar
myVar="$(some_func arg1)"

Function Naming

Functions in Libraries

Lower case technical prefix separated by underscore, then double colon, then camel case.

Let's say your library's id is mylib with prefix ab:

ab_mylib::myLibFunc()

Functions in Scripts

Lower case technical prefix separated by underscore, then camel case.

Let's say your script is named my-fine-prog:

my_fine_prog:myFineFunc()

Private Functions

Private functions should be prefixed by _ before their camel case part:

ab_mylib::_myPrivateLibFunc()      # Library
my_fine_prog:_myFinePrivateFunc()  # Script

Grouping Functions

Functions sharing the same context (inside a library or script) should separated from their context by ::

ab_mylib::list:add() {...}
ab_mylib::list:del() {...}
ab_mylib::list:pop() {...}
ab_mylib::list:_helper1() {...}

(Nested and Global) Helper Functions

Nested helper functions are not function-local, just globally defined when the parent function runs; so firstly, avoid nested functions if possible; only use them in case it makes sense to access local variables of the parent directly. Secondly, both nested and global helper functions need the full treat naming-wise to avoid any clashes.

Helper functions for a function are named like their parents plus their name in camel case, separated by ::

Global example:

ab_mylib::func:helper()
{
  printf "${1}"
}

ab_mylib::func()
{
  local var=17
  ab_mylib::func:helper "${var}"
}

Nested example:

ab_mylib::func()
{
  ab_mylib::func:helper()
  {
    printf "${var}"
  }
  local var=17
  ab_mylib::func:helper
}

Filter Functions

Functions that process stdin are by definition filters; post-prefix names must start with "filter":

ab_mylib::filterFunc()     # Library filter function
my_fine_prog_filterFunc()  # Tool filter function

Variable Naming

Globals

Use all capital with underscore separation; for private globals, prefix _ after the tech prefix; you should always use declare -g:

declare -g TECH_PRE_FIX_IDENTI_FIER="xyz"   # a global variable
declare -g -i TECH_PRE_FIX_INT_NUMBER=17    # a global int variable
declare -g TECH_PRE_FIX__PRIVATE="xyz"      # a private global variable

Note

Never use declare without -g for globals in libraries, as these are included inside the con::source function, and thusly become local. Libraries actually should use declare -g inside the resp. library init function PREFIX_libID:init().

Locals

Use camelCase; you must always use local:

local myString="uff-tata"        # a local variable
local -i i=0                     # a local int (index) variable

Test and Expressions

Prefer [[ expr ]] over [ expr ] or test expr:

[[ -f "${myFile}" && ${count} -ge 5 ]]
  • Expansion rules are more to what you usually expect (see man bash).
  • Use of bash add-ons like =~ with the same syntax.
  • Shell-like bool operators (&&, || instead of -a, -o).

Texts and Strings

Quoting

Primary quoting character is " (for both code and messages).

Use ' in code only when necessary (f.e., to avoid expansion), or as secondary quote:

con::log:info "Adding file \"${file}\" to directory..."
con::log:error "Tool xyz failed: \"${xyzErrCode}: '${xyzErrMsg}'\"."

Pretty much _always_ quote variables or arguments:

local v="${1}"
local v="$(ls -al)"
local v="$(ls -al "${myFile}")"

As always, there are some exceptions (when used in arithmetics only, as subscript, ...):

local myMin=1 myMax=5 max="max"
local -a MY_INT_VALUES
MY_INT_VALUES[${max}]}="${myMax}"

Log messages, usage and doc strings

Always start capital, and add a sentence ending char (".", "!", "?", "..."):

con::log:info "Adding xyz to directory..."
con::log:error "A fine error has occured!"
con::log:fatal "Unknown fatal error. Did you do something wrong?"
con_opt::add "W" "Wait/prompt after command."

User data should be quoted, program-internal values not:

con::log:info "Added file: \"${file}\"."
con::log:info "Program status switched to: ${PROGRAM_STATUS}."

Documentation style guide

As a principal, all documentation (except the usage help for tools) is done using reStructuredText.

Titles and Headers

For titles we use over- and underline using characters "=~", in that order. For sections we use underline using characters "=~-'`", in that order:

.. -*- mode: rst -*-

=====
Title
=====
~~~~~~~~
Subtitle
~~~~~~~~

.. contents::

Section 1
=========

Bla blums.

Subsection 1.1
~~~~~~~~~~~~~~

Bla blums.

Subsection 1.2
~~~~~~~~~~~~~~

Bla blums.

Subsubsection 1.2.1
-------------------

Bla blums.

Subsubsubsection 1.2.1.1
''''''''''''''''''''''''

Bla blums.

Subsubsubsubsection 1.2.1.1.1
`````````````````````````````

Bla blums.

Section 2
=========

Bla blums.

Don't use

  • .. code::: Only supported by newer docutils.

Inline bash documentation style guide

con-dev style libraries should be fully inline-documented.

Functions

Add documentation right above each public function like so:

# RST: con::func1 -a [-o] (-B|-V) <positional>
# RST: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# RST: Description...
# RST:
con::func1()
{
  ...
}

Syntax line:

  • <> for argument value placeholders (option arguments or positional arguments): <arg1>.
  • ... (ellipsis) for "any more of the previous token": <arg1>....
  • [] to denote optionality: [<arg1>].
  • (x|y) to denote "requires one of": (cmd1|cmd2).
  • [(x|y)] to denote "one of or none": [(cmd1|cmd2)].
  • -O to denote option "O".