1032 lines
43 KiB
Text
1032 lines
43 KiB
Text
Information for developers
|
|
==========================
|
|
|
|
This document is intended to explain some of the more useful things
|
|
within the tree, and provide a standard for working on the code.
|
|
|
|
General stuff -- common subdirectory
|
|
------------------------------------
|
|
|
|
String handling
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Use `snprintf()`. It's even provided with a compatibility module if the
|
|
target system doesn't have it natively.
|
|
|
|
If you use `snprintf()` to load some value into a buffer, make sure you
|
|
provide the format string. Don't use user-provided format strings,
|
|
since that's an easy way to open yourself up to an exploit.
|
|
|
|
Don't use `strcat()`. We have a neat wrapper for `snprintf()` called
|
|
`snprintfcat()` that allows you to append to `char *` with a format
|
|
string and all the usual string length checking of `snprintf()` routine.
|
|
|
|
Error reporting
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Don't call `syslog()` directly. Use `upslog_with_errno()` and `upslogx()`.
|
|
They may write to the syslog, stderr, or both as appropriate. This
|
|
means you don't have to worry about whether you're running in the
|
|
background or not.
|
|
|
|
The `upslog_with_errno()` routine prints your message plus the string
|
|
expansion of `errno`. The `upslogx()` just prints the message.
|
|
|
|
`fatal_with_errno()` and `fatalx()` work the same way, but they
|
|
also `exit(EXIT_FAILURE)` afterwards. Don't call `exit()` directly.
|
|
|
|
Debugging information
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The `upsdebug_with_errno()`, `upsdebugx()`, `upsdebug_hex()` and
|
|
`upsdebug_ascii()` routines use the global `nut_debug_level`, so you
|
|
don't have to mess around with `printf()`'s and `if`'s yourself.
|
|
Use them.
|
|
|
|
Memory allocation
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
`xmalloc()`, `xcalloc()`, `xrealloc()` and `xstrdup()` all check the
|
|
results of the base calls before continuing, so you don't have to.
|
|
Don't use the raw calls directly.
|
|
|
|
Config file parsing
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
The configuration parser, called `parseconf`, is now up to its fourth
|
|
major version. It has multiple entry points, and can handle many
|
|
different jobs. It's usually used for parsing files, but it can also
|
|
take input a line at a time or even a character at a time.
|
|
|
|
You must initialize a context buffer with `pconf_init()` before using any
|
|
other `parseconf` function. `pconf_encode()` is the only exception, since
|
|
it operates on a buffer you supply and is an auxiliary function.
|
|
|
|
Escaping special characters and quoting multiple-word elements is all
|
|
handled by the state machine. Using the same code for all config files
|
|
avoids code duplication.
|
|
|
|
NOTE: this does not apply to drivers. Driver authors should use the
|
|
`upsdrv_makevartable()` scheme to pick up values from 'ups.conf' file.
|
|
Drivers should not have their own config files.
|
|
|
|
Drivers may have their own data files, such as lists of hardware,
|
|
mapping tables, or similar. The difference between a data file and a
|
|
config file is that users should never be expected to edit a data file
|
|
under normal circumstances. This technique might be used to add more
|
|
hardware support to a driver without recompiling.
|
|
|
|
<time.h> vs. <sys/time.h>
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This is already handled by autoconf, so just `#include "timehead.h"` and
|
|
you will get the right headers on every system.
|
|
|
|
Device drivers -- main.c
|
|
------------------------
|
|
|
|
The device drivers use `main.c` as their core.
|
|
|
|
To write a new driver, you create a file with a series of support
|
|
functions that will be called by main. These all have names that start
|
|
with `upsdrv_`, and they will be called at different times by main
|
|
depending on what needs to happen.
|
|
|
|
See the <<new-drivers,driver documentation>> for information on writing
|
|
drivers, and also refer to the skeletal driver in `skel.c`.
|
|
|
|
Portability
|
|
-----------
|
|
|
|
Avoid things that will break on other systems. All the world is not an
|
|
x86 Linux box.
|
|
|
|
There are still older systems out there that don't do C++ style comments.
|
|
|
|
--------------------------------------
|
|
/* Comments look like this. */
|
|
// Not like this.
|
|
--------------------------------------
|
|
|
|
Newer versions of gcc allow you to declare a variable inside a function
|
|
after code, somewhat like the way C++ operates, like this:
|
|
|
|
-------------------------------------------------------------------------------
|
|
function do_stuff(void)
|
|
{
|
|
check_something();
|
|
|
|
int a;
|
|
|
|
a = do_something_else();
|
|
}
|
|
-------------------------------------------------------------------------------
|
|
|
|
While this will compile and run on these newer versions, it will fail
|
|
miserably for anyone on an older system. That means you must not use
|
|
it. `gcc` only warns about this with `-pedantic` flag.
|
|
|
|
Another feature that does not work on some compilers (e.g. conforming
|
|
to "ANSI C"/C89/C90 standard) is initial variable declaration inside a
|
|
'for loop' block, like this:
|
|
--------------------------------------------------------------------------------
|
|
function do_stuff(void)
|
|
{
|
|
/* This should declare "int i;" first, then use it in "for" loop: */
|
|
for (int i = 0; i < INT_MAX; ++i) { ... }
|
|
|
|
/* Additional loops cause also an error about re-declaring a variable: */
|
|
for (int i = 10; i < 15; ++i) { ... }
|
|
}
|
|
--------------------------------------------------------------------------------
|
|
|
|
TIP: At this point NUT is expected to work correctly when built with a
|
|
C99 (or rather GNU99 on many systems) or newer standard.
|
|
|
|
The NUT codebase may build in a mode without warnings made fatal on C89
|
|
(GNU89), but the emitted warnings indicate that those binaries may crash.
|
|
By the end of 2021, NUT codebase has been revised to pass GNU and strict-C
|
|
mode builds with C89 standard with the GCC toolkit (and on systems that do
|
|
have the newer features in libraries, just hide them in standard headers);
|
|
however CLANG toolkit is more restrictive about the C99+ syntax used.
|
|
If somebody in the community requires to build and run NUT on systems
|
|
that old, pull requests to fix the offending coding issues are welcome.
|
|
|
|
Note also that the NUT codebase currently relies on certain features,
|
|
such as the printf format modifiers for `(s)size_t`, use of `long long`,
|
|
some nuances about structure/array initializers, variadic macros for
|
|
debugging, etc. that a pedantic C90 mode compilation warns is not part
|
|
of the standard but a GNU extension (and part of C99 and newer standard
|
|
revisions). Many of the "offences" against the older standard actually
|
|
come from system and third-party header files.
|
|
|
|
That said, the NUT CI farm does run non-regression builds with GNU C89
|
|
and strict C89 standard revisions and minimal passing warnings level,
|
|
to ensure that codebase is and remains at least basically compliant.
|
|
|
|
Continuous Integration and Automated Builds
|
|
-------------------------------------------
|
|
|
|
To ease and automate the build scenarios which were deemed important for
|
|
quality assurance and non-regression checks of NUT, several solutions
|
|
were introduced over time.
|
|
|
|
ci_build.sh
|
|
^^^^^^^^^^^
|
|
|
|
This script was originally introduced (following ZeroMQ/ZProject example)
|
|
to automate CI builds, by automating certain scenarios driven by exported
|
|
environment variables to set particular `configure` options and `make`
|
|
some targets (chosen by the `BUILD_TYPE` envvar). It can also be used
|
|
locally to avoid much typing to re-run those scenarios during development.
|
|
|
|
Developers can directly use the scripts involved in CI builds to fix
|
|
existing code on their workstations or to ensure support for new
|
|
compilers and C standard revisions, e.g. save a local file like this
|
|
to call the common script with pre-sets:
|
|
|
|
$ cat _fightwarn-gcc10-gnu17.sh
|
|
#!/bin/sh
|
|
|
|
BUILD_TYPE=default-all-errors \
|
|
CFLAGS="-Wall -Wextra -Werror -pedantic -std=gnu17" \
|
|
CXXFLAGS="-Wall -Wextra -Werror -std=gnu++17" \
|
|
CC=gcc-10 CXX=g++-10 \
|
|
./ci_build.sh
|
|
|
|
...and then execute it to prepare a workspace, after which you can go
|
|
fixing bugs file-by-file running a `make` after each save to confirm
|
|
your solutions and uncover the next issue to address :-)
|
|
|
|
Helpfully, the NUT CI farm build logs report the configuration used for
|
|
each executed stage, so if some build combination fails -- you can just
|
|
scroll to the end of that section and copy-paste the way to reproduce
|
|
an issue locally (on an OS similar to that build case).
|
|
|
|
Note that while spelling out sets of warnings can help in a quest to
|
|
fix certain bugs during development (if only by removing noise from
|
|
classes of warnings not relevant to the issue one is working on), there
|
|
is a reasonable set of warnings which NUT codebase actively tries to
|
|
be clean about (and checks in CI), detailed in the next section.
|
|
|
|
For the `ci_build.sh` usage like above, one can instead pass the setting
|
|
via `BUILD_WARNOPT=...`, and require that all emitted warnings are fatal
|
|
for their build, e.g.:
|
|
|
|
$ cat _fightwarn-clang9-gnu11.sh
|
|
#!/bin/sh
|
|
|
|
BUILD_TYPE=default-all-errors \
|
|
BUILD_WARNOPT=hard BUILD_WARNFATAL=yes \
|
|
CFLAGS="-std=gnu11" \
|
|
CXXFLAGS="-std=gnu++11" \
|
|
CC=clang-9 CXX=clang++-9 CPP=clang-cpp \
|
|
./ci_build.sh
|
|
|
|
Finally, for refactoring effort geared particularly for fighting the
|
|
warnings which exist in current codebase, the script contains some
|
|
presets (which would evolve along with codebase quality improvements)
|
|
as `BUILD_TYPE=fightwarn-gcc`, `BUILD_TYPE=fightwarn-clang` or plain
|
|
`BUILD_TYPE=fightwarn`:
|
|
|
|
BUILD_TYPE=fightwarn-clang ./ci_build.sh
|
|
|
|
As a rule of thumb, new contributions must not emit any warnings when
|
|
built in GNU99 mode with a `minimal` "difficulty" level of warnings.
|
|
Technically they must survive the part of test matrix across the several
|
|
platforms tested by NUT CI and marked in project settings as required
|
|
to pass, to be accepted for a pull request merge.
|
|
|
|
Developers aiming to post successful pull requests to improve NUT can
|
|
pass the `--enable-warnings` option to the `configure` script in local
|
|
builds to see how that behaves and ensure that at least in some set-up
|
|
their contribution is viable. Note that different compiler versions and
|
|
vendors (gcc/clang/...), building against different OS and third-party
|
|
dependencies, with different CPU architectures and different language
|
|
specification revisions, might all complain about different issues --
|
|
and catching this in as diverse range of set-ups as possible is why we
|
|
have CI tests.
|
|
|
|
It can be beneficial for serial developers to set up a local BuildBot,
|
|
Travis or a Jenkins instance with a matrix test job, to test their local
|
|
git repository branches with whatever systems they have available.
|
|
|
|
* https://github.com/networkupstools/nut/issues/823
|
|
|
|
While `autoconf` tries its best to provide portable shell code, sometimes
|
|
there are builds of system shell that just fail under stress. If you are
|
|
seeing random failures of `./configure` script in different spots with
|
|
the same inputs, try telling `./ci_build.sh` to loop configuring until
|
|
success (instead of quickly failing), and/or tell `./configure` to use
|
|
another shell at least for the system call-outs, with options like these:
|
|
|
|
SHELL=/bin/bash CONFIG_SHELL=/bin/bash CI_SHELL_IS_FLAKY=true \
|
|
./ci_build.sh
|
|
|
|
Jenkins CI
|
|
^^^^^^^^^^
|
|
|
|
Since mid-2021, the NUT CI farm is implemented by several virtual servers
|
|
courteously provided by http://fosshost.org
|
|
|
|
These run various operating systems as build agents, and a Jenkins instance
|
|
to orchestrate the builds of NUT branches and pull requests on those agents.
|
|
|
|
This is driven by `Jenkinsfile-dynamatrix` and a Jenkins Shared Library called
|
|
link:https://github.com/networkupstools/jenkins-dynamatrix[jenkins-dynamatrix]
|
|
which prepares a matrix of builds across as many operating systems,
|
|
bitnesses/architectures, compilers, make programs and C/C++ revisions
|
|
as it can -- based on the population of currently available build agents
|
|
and capabilities which they expose as agent labels.
|
|
|
|
This hopefully means that people interested in NUT can contribute to the
|
|
build farm (and ensure NUT is and remains compatible with their platform)
|
|
by running a Jenkins Swarm agent with certain labels, which would dial
|
|
into https://ci.networkupstools.org/ controller. Please contact the NUT
|
|
maintainer if you want to participate in this manner.
|
|
|
|
The `Jenkinsfile-dynamatrix` recipe allows NUT CI farm to run different sets
|
|
of build scenarios based on various conditions, such as the name of branch
|
|
being built (or PR'ed against), changed files (e.g. C/C++ sources vs. just
|
|
docs), and some build combinations may be not required to succeed.
|
|
|
|
For example, the main development branch and pull requests against it must
|
|
cleanly pass all specified builds and tests on various platforms with the
|
|
default level of warnings specified in the `configure` script. These are
|
|
balanced to not run too many build scenarios overall, but just a quick and
|
|
sufficiently representative set.
|
|
|
|
As another example, there is special handling for "fightwarn" pattern in
|
|
the branch names to run many more builds with varying warning levels and
|
|
more variants of intermediate language revisions, and so expose concerns
|
|
deliberately missed by default warnings levels in "master" branch builds
|
|
(the bar moves over time, as some classes of warnings become extinct from
|
|
our codebase).
|
|
|
|
Further special handling for branches named like `fightwarn.*89.*` regex
|
|
enables more intensive warning levels for a GNU89 build specifically (which
|
|
are otherwise disabled as noisy yet not useful for supported C99+ builds),
|
|
and is intended to help develop fixes for support of this older language
|
|
revision, if anyone would dare.
|
|
|
|
Many of those unsuccessful build stages are precisely the focus of the
|
|
"fightwarn" effort, and are currently marked as "may fail", so they end
|
|
up as "UNSTABLE" (seen as orange bubbles in the Jenkins BlueOcean UI, or
|
|
orange cells in the tabular list of stages in the legacy UI), rather than
|
|
as "FAILURE" (red bubbles) for build scenarios that were not expected to
|
|
fail and usually represent higher-priority problems that would block a PR.
|
|
|
|
Developers whose PR builds (or attempts to fix warnings) did not succeed in
|
|
some cell of such build matrix, can look at the individual logs of that cell.
|
|
Beside indication from the compiler about the failure, the end of log text
|
|
includes the command which was executed by CI worker and can be reproduced
|
|
locally by the developer, e.g.:
|
|
----
|
|
22:26:01 FINISHED with exit-code 2 cmd: (
|
|
22:26:01 [ -x ./ci_build.sh ] || exit
|
|
22:26:01
|
|
22:26:01 eval BUILD_TYPE="default-alldrv" BUILD_WARNOPT="hard" \
|
|
BUILD_WARNFATAL="yes" MAKE="make" CC=gcc-10 CXX=g++-10 \
|
|
CPP=cpp-10 CFLAGS='-std=gnu99 -m64' CXXFLAGS='-std=gnu++11 -m64' \
|
|
LDFLAGS='-m64' ./ci_build.sh
|
|
22:26:01 )
|
|
----
|
|
or for autotools-driven scenarios (which prep, configure, build and test
|
|
in separate stages -- so for reproducing a failed build you should also
|
|
look at its configuration step separately):
|
|
----
|
|
22:28:18 FINISHED with exit-code 0 cmd: ( [ -x configure ] || exit; \
|
|
eval CC=clang-9 CXX=clang++-9 CPP=clang-cpp-9 CFLAGS='-std=c11 -m64' \
|
|
CXXFLAGS='-std=c++11 -m64' LDFLAGS='-m64' time ./configure )
|
|
----
|
|
|
|
To re-run such scenario locally, you can copy the line from `eval` (but
|
|
without the `eval` keyword itself) up to and including the executed script
|
|
or tool, into your shell. Depending on locally available compilers, you
|
|
may have to tweak the `CC`, `CXX` and `CPP` arguments; note that a `CPP`
|
|
may be specified as `/path/to/CC -E` for GCC and CLANG based toolkits
|
|
at least, if they lack a standalone preprocessor program (e.g. IntelCC).
|
|
|
|
NOTE: While NUT recipes do not currently recognize a separate `CXXCPP`,
|
|
it would follow similar semantics.
|
|
|
|
Some further details about the NUT CI farm workers are available in
|
|
link:config-prereqs.txt[config-prereqs.txt] and
|
|
link:ci-farm-lxc-setup.txt[ci-farm-lxc-setup.txt] documents.
|
|
|
|
Travis CI
|
|
^^^^^^^^^
|
|
|
|
See the `.travis.yml` file in project sources for a detailed list of third
|
|
party dependencies and a large matrix of `CFLAGS` and compiler versions
|
|
last known to work or to not (yet) work on operating systems available
|
|
to that CI solution.
|
|
|
|
[NOTE]
|
|
======
|
|
The cloud Travis CI offering became effectively defunct for
|
|
open-source projects in mid-2021, so the `.travis.yml` file in NUT
|
|
codebase is not actively maintained.
|
|
|
|
Local private deployments of Travis CI are possible, so if anybody does
|
|
use it and has updated markup to share, they are welcome to post PRs.
|
|
======
|
|
|
|
The NUT project on GitHub has integration with Travis CI to test a large
|
|
set of compiler and option combinations, covering different versions of
|
|
gcc and clang, C standards, and requiring to pass builds at least in a
|
|
mode without warnings (and checking the other cases where any warnings
|
|
are made fatal).
|
|
|
|
Pre-set warning options
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The options chosen into pre-sets that can be selected by `configure`
|
|
script options are ones we use for different layers of CI tests.
|
|
|
|
Values to note include:
|
|
|
|
* `--enable-Werror(=yes/no)` -- make warnings fatal;
|
|
|
|
* `--enable-warnings(=.../no)` -- enable certain warning presets:
|
|
|
|
** `gcc-hard`, `clang-hard`, `gcc-medium`, `clang-medium`, `gcc-minimal`,
|
|
`clang-minimal`, `all` -- actual definitions that are compiler-dependent
|
|
(the latter just adds `-Wall` which may be relatively portable);
|
|
|
|
** `hard`, `medium` or `minimal` -- if current compiler is detected as
|
|
CLANG or GCC, apply corresponding setting from above (or `all` otherwise);
|
|
|
|
** `gcc` or `clang` -- apply the set of options (regardless of detected
|
|
compiler) with default "difficulty" hard-coded in `configure` script,
|
|
to tweak as our codebase becomes cleaner;
|
|
|
|
** `yes`/`auto` (also takes effect if `--enable-warnings` is requested
|
|
without an `=ARG` part) -- if current compiler is detected as CLANG
|
|
or GCC, apply corresponding setting with default "difficulty" from
|
|
above (or `all` otherwise).
|
|
|
|
Note that for backwards-compatibility reasons and to help filter out
|
|
introduction of blatant errors, builds with compilers that claim GCC
|
|
compatibility can enable a few easy warning presets by default. This
|
|
can be avoided with an explicit argument to `--disable-warnings` (or
|
|
`--enable-warnings=no`).
|
|
|
|
All levels of warnings pre-sets for GCC in particular do not enforce
|
|
the `-pedantic` mode for builds with C89/C90/ANSI standard revision
|
|
(as guesstimated by `CFLAGS` content), because nowadays it complains
|
|
more about the system and third-party library headers, than about NUT
|
|
codebase quality (and "our offenses" are mostly something not worth
|
|
fixing in this era, such as the use of `__func__` in debug commands).
|
|
If there still are practical use-cases that require builds of NUT on
|
|
pre-C99 compiler toolkits, pull requests are of course welcome -- but
|
|
the maintainer team does not intend to spend much time on that.
|
|
|
|
Hopefully this warnings pre-set mechanism is extensible enough if we
|
|
would need to add more compilers and/or "difficulty levels" in the
|
|
future.
|
|
|
|
Finally, note that such pre-set warnings can be mixed with options
|
|
passed through `CFLAGS` or `CXXFLAGS` values to your local `configure`
|
|
run, but it is up to your compiler how it interprets the resulting mix.
|
|
|
|
Coding style
|
|
------------
|
|
|
|
This is how we do things:
|
|
|
|
-------------------------------------------------------------------------------
|
|
int open_subspace(char *ship, int privacy)
|
|
{
|
|
if (!privacy)
|
|
return insecure_channel(ship);
|
|
|
|
if (!init_privacy(ship))
|
|
fatal_with_errno("Can't open secure channel");
|
|
|
|
return secure_channel(ship);
|
|
}
|
|
-------------------------------------------------------------------------------
|
|
|
|
The basic idea is that we try to group things into functions, and then
|
|
find ways to drop out of them when we can't go any further. There's
|
|
another way to program this involving a big else chunk and a bunch of
|
|
braces, and it can be hard to follow. You can read this from top to
|
|
bottom and have a pretty good idea of what's going on without having to
|
|
track too much `{ }` nesting and indenting.
|
|
|
|
We don't really care for `pretentiousVariableNamingSchemes`, but you can
|
|
probably get away with it in your own driver that we will never have to
|
|
touch. If your function or variable names start pushing important code
|
|
off the right margin of the screen, expect them to meet the byte
|
|
chainsaw sooner or later.
|
|
|
|
All types defined with typedef should end in `_t`, because this is
|
|
easier to read, and it enables tools (such as indent and emacs) to
|
|
display the source code correctly.
|
|
|
|
Indenting with tabs vs. spaces
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Another thing to notice is that the indenting happens with tabs instead
|
|
of spaces. This lets everyone have their personal tab-width setting
|
|
without inflicting much pain on other developers. If you use a space,
|
|
then you've fixed the spacing in stone and have really annoyed half of
|
|
the people out there.
|
|
|
|
Note that tabs apply only to *indenting*. Alignment of text after any
|
|
non-tab character has appeared on the line must be done by spaces in
|
|
order for it to remain at the same alignment when someone views tabs at
|
|
a different widths.
|
|
|
|
One common example for this is multi-line if condition:
|
|
|
|
--------------------------------------------------------------------------------
|
|
if (something &&
|
|
something_else) {
|
|
--------------------------------------------------------------------------------
|
|
|
|
which may be written without mixing tabs and spaces to indent, as:
|
|
|
|
--------------------------------------------------------------------------------
|
|
if (something
|
|
&& something_else
|
|
) {
|
|
--------------------------------------------------------------------------------
|
|
|
|
Another example is tables of definitions that are better aligned with
|
|
(non-leading) spaces at least between names and values not too many
|
|
characters wide; it still helps to align the columns with spaces at
|
|
offsets divisible by 4 or 8 (consistently for the whole table):
|
|
|
|
--------------------------------------------------------------------------------
|
|
#define SHORT_MACRO 1 /* flag comment */
|
|
#define SOMETHING_WITH_A_VERY_LONG_NAME 255 /* flag comment */
|
|
--------------------------------------------------------------------------------
|
|
|
|
If you write something that uses leading spaces, you may get away with
|
|
it in a driver that's relatively secluded. However, if we have to work
|
|
on that code, expect it to get reformatted according to the above.
|
|
|
|
Patches to existing code that don't conform to the coding style being
|
|
used in that file will probably be dropped. If it's something we really
|
|
need, it will be grudgingly reformatted before being included.
|
|
|
|
When in doubt, have a look at Linus's take on this topic in the Linux
|
|
kernel -- Documentation/CodingStyle. He's done a far better job of
|
|
explaining this.
|
|
|
|
Line breaks
|
|
~~~~~~~~~~~
|
|
|
|
It is better to have lines that are longer than 80 characters than to
|
|
wrap lines in random places. This makes it easier to work with tools
|
|
such as `grep`, and it also lets each developer choose their own
|
|
window size and tab setting without being stuck to one particular
|
|
choice.
|
|
|
|
Of course, this does not mean that lines should be made unnecessarily
|
|
long when there is a better alternative (see the note on
|
|
`pretentiousVariableNamingSchemes` above). Certainly there should not
|
|
be more than one statement per line. Please do not use
|
|
|
|
-------------------------------------------------------------------------------
|
|
if (condition) break;
|
|
-------------------------------------------------------------------------------
|
|
|
|
but use the following:
|
|
|
|
-------------------------------------------------------------------------------
|
|
if (condition) {
|
|
break;
|
|
}
|
|
-------------------------------------------------------------------------------
|
|
|
|
Un-used variables and function arguments
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Whenever a function needs to satisfy a particular API, it can end up
|
|
taking arguments that are not used in practice (think a too-trivial
|
|
signal handler). While some compilers offer the facility of decorations
|
|
like `__attribute__(unused)`, this proved not to be a portable solution.
|
|
Also the abilities of newer C++ standard revisions are of no help to
|
|
the vast range of existing systems that run NUT today and expect to be
|
|
able to do so tomorrow (hence the required C99+ support noted above).
|
|
|
|
In NUT codebase we prefer to mark un-used variables explicitly in the
|
|
body of the function (or an `#ifdef` branch of its code) using the
|
|
`NUT_UNUSED_VARIABLE(varname)` as a routine call inside a function
|
|
body, referring to the macro defined in `common.h`.
|
|
|
|
To display in a rough example:
|
|
|
|
-------------------------------------------------------------------------------
|
|
static void signal_X_handler(int signal_X) {
|
|
NUT_UNUSED_VARIABLE(signal_X);
|
|
/* We have explicitly got nothing to do if we catch signal X */
|
|
return;
|
|
}
|
|
-------------------------------------------------------------------------------
|
|
|
|
Miscellaneous coding style tools
|
|
--------------------------------
|
|
|
|
You can go a long way towards converting your source code to the NUT
|
|
coding style by piping it through the following command:
|
|
|
|
indent -kr -i8 -T FILE -l1000 -nhnl
|
|
|
|
This next command does a reasonable job of converting most C++ style
|
|
comments (but not URLs and DOCTYPE strings):
|
|
|
|
sed 's#\(^\|[ \t]\)//[ \t]*\(.*\)[ \t]*#/* \2 */#'
|
|
|
|
Emacs users can adjust how tabs are displayed. For example, it is
|
|
possible to set a tab stop to be 3 spaces, rather than the usual 8.
|
|
(Note that in the saved file, one indentation level will still
|
|
correspond to one tab stop; the difference is only how the file is
|
|
rendered on screen). It is even possible to set this on a
|
|
per-directory basis, by putting something like this into your `.emacs`
|
|
file:
|
|
|
|
-------------------------------------------------------------------------------
|
|
;; NUT style
|
|
|
|
(defun nut-c-mode ()
|
|
"C mode with adjusted defaults for use with the NUT sources."
|
|
(interactive)
|
|
(c-mode)
|
|
(c-set-style "K&R")
|
|
(setq c-basic-offset 3) ;; 3 spaces C-indentation
|
|
(setq tab-width 3)) ;; 3 spaces per tab
|
|
|
|
;; apply NUT style to all C source files in all subdirectories of nut/
|
|
|
|
(setq auto-mode-alist (cons '(".*/nut/.*\\.[ch]$". nut-c-mode)
|
|
auto-mode-alist))
|
|
-------------------------------------------------------------------------------
|
|
|
|
Finishing touches
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
We like code that uses `const` and `static` liberally. If you don't need
|
|
to expose a function or global variable to the outside world, `static` is
|
|
your friend. If nobody should edit the contents of some buffer that's
|
|
behind a pointer, `const` keeps them honest.
|
|
|
|
We always compile with `-Wall`, so things like `const` and `static` help you
|
|
find implementation flaws. Functions that attempt to modify a constant
|
|
or access something outside their scope will throw a warning or even
|
|
fail to compile in some cases. This is what we want.
|
|
|
|
|
|
Switch case fall-through
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
While C standards allow to write `switch` statements to "fall through"
|
|
from handling one case into another, modern compilers frown upon that
|
|
practice and spew warnings which complicate detecting real bugs in the
|
|
code (and also looking back at some of the cases written decades ago,
|
|
it is not trivial to state whether the fall-through was intentional or
|
|
really is a bug).
|
|
|
|
Compilers which detect such problem usually offer ways to decorate the
|
|
code with comments or attributes to keep it quiet it in cases where the
|
|
jump is intentional; also C++17 introduces special keywords for that in
|
|
the standard. NUT aiming to be portable and independent of compilers as
|
|
much as possible, prefers the arguably clearer and standards-based way
|
|
of using `goto` into the next intended operation, even though it is a
|
|
couple of lines away, e.g.:
|
|
|
|
int uppercase = 0;
|
|
switch (char_opt) {
|
|
case 'U':
|
|
uppercase = 1;
|
|
goto fallthrough_case_u_option;
|
|
case 'u':
|
|
fallthrough_case_u_option:
|
|
process_u_option(uppercase);
|
|
break;
|
|
}
|
|
|
|
In trivial cases, like falling through to `default` which just returns,
|
|
it may be clearer and more maintainable (adding other option cases in
|
|
the future) to just `return same_result` in the code block that would
|
|
fall through otherwise and avoid `goto` statements altogether.
|
|
|
|
Spaghetti
|
|
~~~~~~~~~
|
|
|
|
If you use a `goto` that jumps over long distances (see "Switch case
|
|
fall-through" section above), expect us to drop it when our head stops
|
|
spinning. It gives us flashbacks to the very old code we wrote.
|
|
We've tried to clean up our act, and you should make the effort
|
|
as well.
|
|
|
|
We're not making a blanket statement about gotos, since everything
|
|
probably has at least one good use. There are a few cases where a goto
|
|
is more efficient than any other approach, but you probably won't
|
|
encounter them very often in this software.
|
|
|
|
|
|
Legacy code
|
|
~~~~~~~~~~~
|
|
|
|
There are parts of the source tree that do not yet conform to these
|
|
specs. Part of this is due to the fact that the coding style has been
|
|
evolving slightly over the course of the project. Some of the code you
|
|
see in these directories is 5 years old, and things have gotten cleaner
|
|
since then. Don't worry -- it'll get cleaned up the next time something
|
|
in the vicinity gets a visit.
|
|
|
|
Memory leak checking
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
We can't say enough good things about valgrind. If you do anything with
|
|
dynamic memory in your code, you need to use this. Just compile with
|
|
`gcc -g` and start the program inside `valgrind`. Run it through the
|
|
suspected area and then exit cleanly. valgrind will tell you if you've
|
|
done anything dodgy like freeing regions twice, reading uninitialized
|
|
memory, or if you've leaked memory anywhere.
|
|
|
|
For more information, refer to the link:http://valgrind.kde.org[Valgrind]
|
|
project.
|
|
|
|
Conclusion
|
|
~~~~~~~~~~
|
|
|
|
The summary: please be kind to our eyes. There's a lot of stuff in here,
|
|
and many people have put a lot of time and energy to improve it.
|
|
|
|
Submitting patches
|
|
------------------
|
|
|
|
Current preference for suggesting changes is to open a pull request on
|
|
GitHub for the https://github.com/networkupstools/nut/ project.
|
|
|
|
For some cases, small patches that arrive by mailing list in unified
|
|
format (`diff -u`) as plain text attachments with no HTML and a brief
|
|
summary at the top are easy to handle, but sadly also easy to overlook.
|
|
|
|
If a patch is sent to the nut-upsdev mailing list, it stands a better
|
|
chance of being seen immediately. However, it is likely to be dropped
|
|
if any issues cannot be resolved quickly. If your code might not work
|
|
for others, or if it is a large change, your best bet is to submit a
|
|
pull request or create an
|
|
link:https://github.com/networkupstools/nut/issues[issue on GitHub].
|
|
|
|
The issue tracker allows us to track the patches over a longer period
|
|
of time, and it is less likely that a patch will fall through the cracks.
|
|
Posting a reminder to the developers (via the nut-upsdev list) about a
|
|
patch on GitHub is fair game.
|
|
|
|
Patch cohesion
|
|
--------------
|
|
|
|
Patches should have some kind of unifying element. One patch set is one
|
|
message, and it should all touch similar things. If you have to edit 6
|
|
files to add support for neutrino detection in UPS hardware, that's
|
|
fine.
|
|
|
|
However, sending one huge patch that does massive separate changes all over
|
|
the tree is not recommended. That kind of patch has to be split up and
|
|
evaluated separately, assuming the core developers care enough to do that
|
|
instead of just dropping it.
|
|
|
|
If you have to make big changes in lots of places, send multiple
|
|
patches -- one per item.
|
|
|
|
The finishing touches: manual pages and device entry in HCL
|
|
-----------------------------------------------------------
|
|
|
|
If you change something that involves an argument to a program or
|
|
configuration file parsing, the man page is probably now out of date.
|
|
If you don't update it, we have to, and we have enough to do as it is.
|
|
|
|
If you write a new driver, send in the man page when you send us the
|
|
source code for your driver. Otherwise, we will be forced to write a
|
|
skeletal man page that will probably miss many of the finer points of
|
|
the driver and hardware.
|
|
|
|
The same remark goes for device entries: if you add support for new models,
|
|
please remember to also complete the hardware compatibility list, present
|
|
in link:data/driver.list.in[]. This will be used to generate both textual,
|
|
static HTML and dynamic searchable HTML for the website.
|
|
|
|
Finally, don't forget about fame and glory: if you added or substantially
|
|
updated a driver, your copyright belongs in the heading comment (along
|
|
with existing ones). For vendor backed (or sponsored) contributions we
|
|
welcome an entry in the link:docs/acknowledgements.txt[] file as well,
|
|
to track and know the industry players who help make NUT better and more
|
|
useful.
|
|
|
|
It is nice to update the link:NEWS[] file for significant development
|
|
to be seen as part of next release, as well as to update the
|
|
link:UPGRADING[] file for potentially breaking changes and similar
|
|
heads-up notes for third-party teams (distribution packagers, clients
|
|
and bindings, etc.)
|
|
|
|
Source code management
|
|
----------------------
|
|
|
|
We currently use a Git repository hosted at GitHub to track changes to
|
|
the NUT source code. This allows you to clone the repository (or fork,
|
|
in GitHub parlance), make changes, and post them online for peer review
|
|
prior to integration.
|
|
|
|
To obtain permission to commit directly to the common upstream NUT repository,
|
|
you must be prepared to spend a fair amount of time contributing to the
|
|
NUT codebase. Most developers will be well served by committing to their
|
|
own forked Git repository (preferably in a uniquely named branch for each
|
|
new contribution), and having the NUT team merge their changes using pull
|
|
requests.
|
|
|
|
Git offers a little more flexibility than the +svn update+ command.
|
|
You may fetch other developers' changes into your repository, but hold
|
|
off on actually combining them with your branch until you have compared
|
|
the two branches (for instance, with `gitk --all`). Git also allows you
|
|
to accumulate more than one commit worth of changes before pushing to
|
|
another repository. This allows development to continue without a constant
|
|
network connection.
|
|
|
|
For a quick change to a file in the Git working copy, you can use
|
|
`git diff` to generate a patch to send to the nut-upsdev mailing list.
|
|
If you have more extensive changes, you can use `git format-patch` on
|
|
a complete commit or branch, and send the resulting series of patches
|
|
to the list.
|
|
|
|
If you use GitHub's web-based editor to make changes, it tends to create
|
|
lots of small commits, one per change per file. Unless there is reason to
|
|
keep the intermediate history, we will probably collapse (or "squash" in
|
|
Git parlance) the entire branch into one commit with a `git rebase -i`
|
|
before merging.
|
|
|
|
The link:https://git.wiki.kernel.org/index.php/GitSvnCrashCourse[GitSvnCrashCourse]
|
|
wiki page has some useful information for long-time users of Subversion.
|
|
|
|
Git access
|
|
~~~~~~~~~~
|
|
|
|
Anonymous Git checkouts are possible:
|
|
|
|
git clone git://github.com/networkupstools/nut.git
|
|
|
|
or
|
|
|
|
git clone https://github.com/networkupstools/nut.git
|
|
|
|
if it is necessary to get around a pesky firewall that blocks the native
|
|
Git protocol.
|
|
|
|
For a quicker checkout (when you don't need the entire repository history),
|
|
you can limit the depth of the clone:
|
|
|
|
git clone --depth 1 git://github.com/networkupstools/nut.git
|
|
|
|
Mercurial (hg) access
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There are those who prefer the simplicity and self-consistency of the
|
|
Mercurial SCM client over the hodgepodge of unique commands which make
|
|
up Git. Rather than debate the merits of each system, we will gently
|
|
guide you towards the link:http://hg-git.github.com/[hg-git project]
|
|
which would theoretically be a transparent bridge between the central
|
|
Git repository, and your local Mercurial working copy.
|
|
|
|
Other tools for hg/git interoperability are sure to exist. We would
|
|
welcome any feedback about this process on the nut-upsdev mailing list.
|
|
|
|
Subversion (SVN) access
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you prefer to check out the NUT source code using an SVN client, GitHub
|
|
has a link:https://github.com/blog/966-improved-subversion-client-support[SVN
|
|
interface to Git repositories] hosted on their servers. You can fork a copy
|
|
of the NUT repository and commit to your fork with SVN.
|
|
|
|
Be aware that the examples in the GitHub blog post might result in a
|
|
checkout that includes all of the current branches, as well as the trunk.
|
|
You are most likely interested in a command line similar to the following:
|
|
|
|
svn co https://github.com/networkupstools/nut/trunk nut-trunk-svn
|
|
|
|
Ignoring generated files
|
|
------------------------
|
|
|
|
The NUT repository generally only holds files which are not generated from
|
|
other files. This prevents spurious differences from being recorded in the
|
|
repository history.
|
|
|
|
If you add a driver, it is recommended that you add the driver executable
|
|
name to the `.gitignore` file in that directory. Similarly, files generated
|
|
from `*.in` and `*.am` source templates should be ignored as well.
|
|
We try to include a number of generated files in the tarball releases with
|
|
`make dist` hooks in order to minimize the number of dependencies for end
|
|
users, but the assumption is that a developer can install the packages
|
|
needed to regenerate those files.
|
|
|
|
Commit message formatting
|
|
-------------------------
|
|
|
|
From the `git commit` man page:
|
|
|
|
[quote]
|
|
Though not required, it's a good idea to begin the commit message with a
|
|
single short (less than 50 character) line summarizing the change, followed
|
|
by a blank line and then a more thorough description. The text up to the
|
|
first blank line in a commit message is treated as the commit title, and
|
|
that title is used throughout git.
|
|
|
|
If your commit is just a change to one component, such as the HCL, upsd or a
|
|
specific driver, prefix your commit message in a way that matches similar
|
|
commits. This helps when searching the repository or tracking down a
|
|
regression.
|
|
|
|
Referring to previous commits can be tricky. If you are referring to the
|
|
immediate parent of a given commit, it suffices to say "the previous commit".
|
|
(Are you correcting a typo in the previous commit? If you haven't pushed yet,
|
|
consider using the `git commit --amend` command instead of creating a new
|
|
commit.) For other commits, even though tools like gitk and GitHub's
|
|
repository viewers recognize Git hashes and create links automatically, it is
|
|
best to add some context such as the commit title or a date.
|
|
|
|
You may notice that some older commits have `[[SVN:####]]` tags and Fossil-ID
|
|
footers. These were lifted from the old SVN commit messages using reposurgeon,
|
|
and should *not* be used as a guide for future commits.
|
|
|
|
Repository etiquette and quality assurance
|
|
------------------------------------------
|
|
|
|
For developers who have commit access to the common upstream NUT repository:
|
|
Please keep the Git "master" branch in working condition at all times.
|
|
The "master" branch may be used to generate daily tarballs, it provides the
|
|
baseline for new contributions, and occasionally is tagged for a new release.
|
|
It should not contain broken code. If you need to commit incremental changes
|
|
that leave the system in a broken state, please do so in a separate branch
|
|
and merge the changes back into "master" once they are complete.
|
|
|
|
To help keep the codebase ever-green, we run a number of CI tests and builds
|
|
in various conditions, including older compilers, different C/C++ standard
|
|
revisions, and an assortment of operating systems; a section below elaborates
|
|
on this in more detail.
|
|
|
|
You are encouraged to use `git rebase -i` on your private Git branches to
|
|
separate your changes into <<_patch_cohesion,logical changes>>.
|
|
|
|
From there, you can generate patches for the issue tracker, or the nut-upsdev
|
|
mailing list.
|
|
|
|
Note that once you rebase a branch, anyone else who has a copy of this branch
|
|
will need to rebase on top of your rebased branch. Obviously, this hinders
|
|
collaboration. In this case, we recommend that you rebase only in your private
|
|
repository, and push when things are ready for discussion. Merging instead of
|
|
rebasing will help with collaboration, but please do not turn the repository
|
|
history into a pile of spaghetti by merging unnecessarily. (Test merges can be
|
|
done on integration branches, which can be discarded if the merge is trivial.)
|
|
Be sure that your commit messages are descriptive when merging.
|
|
|
|
If you haven't created a commit out of your local changes yet, and you want to
|
|
fetch the latest code, you can also use +git stash+ before pulling, then +git
|
|
stash pop+ to apply your saved changes.
|
|
|
|
Here is an example workflow:
|
|
|
|
------------------------------------------------------------------------------
|
|
git clone -o central git://github.com/networkupstools/nut.git
|
|
|
|
cd nut
|
|
git remote add -f username git://github.com/username/nut.git
|
|
|
|
git checkout master
|
|
git branch my-new-feature
|
|
git checkout my-new-feature
|
|
|
|
# Hack away
|
|
|
|
git add changed-file.c
|
|
git commit
|
|
|
|
# Fix a typo in a file or commit message:
|
|
|
|
git commit -a --amend
|
|
|
|
# Someone committed something to the central repository. Fetch it.
|
|
|
|
git fetch central
|
|
git rebase central/master
|
|
|
|
# Publish your branch to your GitHub repository:
|
|
|
|
git push username my-new-feature
|
|
------------------------------------------------------------------------------
|
|
|
|
If you are new to Git, but are familiar with SVN, the
|
|
link:http://git-scm.com/course/svn.html[following link] may be of use.
|
|
|
|
[[building]]
|
|
Building the Code
|
|
-----------------
|
|
|
|
For a developer, the NUT build process starts with `./autogen.sh`.
|
|
|
|
This script generates the `./configure` script that end users typically
|
|
invoke to build NUT. If you are making a number of changes to the NUT
|
|
source tree, configuring with the `--enable-maintainer-mode` flag will
|
|
ensure that after you change a `Makefile.am`, nearby `Makefile.in` and
|
|
`Makefile` get regenerated. At a minimum, you will need at least:
|
|
|
|
* autoconf
|
|
* automake
|
|
* libtool
|
|
* Python
|
|
* Perl
|
|
|
|
[NOTE]
|
|
======
|
|
See the link:config-prereqs.txt[] for better detailed package lists for
|
|
different operating systems.
|
|
|
|
See `ci_build.sh` for automating many practical scenarios, for easier
|
|
iterations.
|
|
======
|
|
|
|
Even if you do not use your distribution's packages of NUT, installing the
|
|
distribution's list of build dependencies for NUT can reduce the amount of
|
|
trial-and-error when installing dependencies. For instance, in Debian, you
|
|
can run `apt-get build-dep nut` to install all of the auto* tools as well
|
|
as any development libraries and headers.
|
|
|
|
After running `./autogen.sh`, you can pass your local configuration
|
|
options to `./configure` and run `make` from the top-level directory.
|
|
To avoid the need for root privileges when testing new NUT code, you
|
|
may wish to use `--prefix=$HOME/local/nut --with-statepath=/tmp`.
|
|
You can also keep compilation times down by only building the driver
|
|
which you are currently working on: `--with-drivers=driver1,dummy-ups`.
|
|
|
|
Before pushing your commits upstream, please run `make distcheck-light`.
|
|
This checks that the Makefiles are not broken, that all the relevant files
|
|
are distributed, and that there are no compilation or installation errors.
|
|
Note that unless you specifically pass `--with-doc=skip` to `configure`,
|
|
this requires all of the dependencies necessary to build the documentation
|
|
to be locally installed on your system, including `asciidoc`, `a2x`,
|
|
`xsltproc`, `dblatex` and any additional XSL stylesheets.
|
|
|
|
Running `make distcheck-light` is especially important if you have added or
|
|
removed files, or updated `configure.ac` or some `Makefile.am` file.
|
|
Remember: simply adding a file to Git does not mean it will be distributed.
|
|
To distribute a file, you must update the corresponding `Makefile.am` with
|
|
`EXTRA_DIST` entry and possibly other recipe handling.
|
|
|
|
There is also `make distcheck`, which runs an even stricter set of
|
|
tests than `make distcheck-light`, but will not work unless you have all
|
|
of the optional third-party libraries and features installed.
|
|
|
|
Finally note, that since 2017 the GitHub upstream project is monitored
|
|
by Travis CI (in addition to earlier multi-platform buildbots which
|
|
occasionally do not work), replaced since 2021 by a dedicated NUT CI farm.
|
|
This means that if your posted improvements are based on current NUT
|
|
"master" branch, the resulting pull request should get tested for a number of
|
|
scenarios automatically. If your code adds a substantial feature, consider
|
|
extending the `Jenkinsfile-dynamatrix` and/or `ci_build.sh` scripts in the
|
|
workspace root to add another `BUILD_TYPE` to the matrix of tests run in
|
|
parallel.
|