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
Contents
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):
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.
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.
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)
# 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)"
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()
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 should be prefixed by _ before their camel case part:
ab_mylib::_myPrivateLibFunc() # Library my_fine_prog:_myFinePrivateFunc() # Script
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 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
}
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
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().
Use camelCase; you must always use local:
local myString="uff-tata" # a local variable local -i i=0 # a local int (index) variable
Note
TODO
Prefer [[ expr ]] over [ expr ] or test expr:
[[ -f "${myFile}" && ${count} -ge 5 ]]
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}"
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}."
As a principal, all documentation (except the usage help for tools) is done using reStructuredText.
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.
con-dev style libraries should be fully inline-documented.
Note
TODO
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: