This is an old revision of the document!


…(WIP)

Diretrizes do Modelo de Desenvolvimento do Código HyperbolaBSD

Descrição

Este artigo especifica o modelo preferido para o desenvolvimento do código fonte do kernel HyperbolaBSD. É também um guia para o modelo preferido de desenvolvimento do codigo fonte no resto do sistema. Este guia deverá ser seguido para todo o novo código. Em geral, código pode ser considerado “novo código” quando constitui a volta de 50% ou mais arquivos com alterações. Isto é o suficiente para romper precedentes no código existente, e poder utilizar o modelo referido neste guia.

/*
 * Guia do modelo do HyperbolaBSD KNF (Kernel Normal Form).
 */
 
/*
 * Comentários MUITO importantes de linha única representam-se assim.
 */
 
/* A maior parte dos comentários de linha única representam-se assim. */
 
/*
 * Comentarios de varias linhas representam-se assim. De forma a criar frases.
 * Preencha-os de forma a criar paragrafos.
 */

Os ficheiros include do kernel (ex., <sys/*.h>) , normalmente, apresentam-se primeiro, ira percisar de <sys/types.h> ou <sys/param.h>, mas nunca ambos! <sys/types.h> inclui <sys/cdefs.h>, e nao ha problema de dependder disso.

#include <sys/types.h>  /* ficheiros include nao-locais em **!?brackets?!**. */

Se for um programa de rede, ponha od ficheiros include relacionados com rede em sequencia.

#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>

Deixe uma linha em branco, seguida por ficheiros/usr/include. Os ficheiros /usr/include , na generalidade, deveram estar organizados.

Pathnames globais estao definidos em /usr/include/paths.h. Pathnames locais para um program, em especifico, estao definidos em pathnames.h no drectorio local.

#include <paths.h>

Deixe uma linha em branco, seguida pelos ficheiros iclude relativos ao usuario.

#include "pathnames.h"  /* ficheiros locais includes em cotacoes duplas. */

Todas as funcoes necessitam de ser initializadas com um prototipo da mesma.

Prototipos de funcoes privadas (ex., funcoes que nao sao utilizadas externamente) apresentam-se no topo do primeiro modulo de fonte. No sistema de usuario, funcoes locais de um modulo em especifico devrao ser declaradas como 'static'. Isto nao devera ser feito no kernel, pois impossiblita o uso de um debugger para o kernel.

Funcoes usadas por outras partes do kernel deverao ser initializadas com um prototipo no ficheiro include relevante, a mesma.

Funcoes que sao utilizadas localmente em mais de um modulo fonte, apresenta-se num ficheiro header em separado, (ex., extern.h).

Prototipos nao deveram incluir nomes de variaveis com os tipos; ex.,

void    function(int);

not:

void    function(int a);

Prototipos poderam ter um espaco extra depois do tab para activar alinhamento dos nomes de funcoes:

static char     *function(int, const char *);
static void      usage(void);

Nao devera haver espaco entre o nome de funcao e a lista de argumentos.

Utilize __dead de <sys/cdefs.h> para funcoes que nao correm, ex.,

__dead void     abort(void);

Nos ficheiros header, introduza prototipos de funcoes conpreendidos por __BEGIN_DECLS / __END_DECLS como pares. Isto torna os ficheiros header utilizaveis para C++.

Macros sao capitalizados e apresentados entre parentises, e deveram evitar efeitos-secundarios. Se estes sao um acrescento em serie de uma funcao, a funcao e num todo defenida em lowercase; a macro tem o mesmo nome num todo mas em uppercase. Se a macro necessita mais de uma linha, utilize !?!braces!?!. Right-justify the backslashes, visto que a defenicao resultante e mais facil de ler. Se a macro emcapusula um !?!compound statement!?!, incorpore esta num loop “do”, para que esta possa ser utilizada em seguranca num “if!?!statements!?!. Qualquer ponto-virgula, utilizado como terminacao devera ser ?!obtida?! pela invocacao da macro e nao pela mesma, para fazer com que este possa ser parsable, para ?!concatenacao!? e editores de texto.

#define MACRO(x, y) do {                                        \
        variable = (x) + (y);                                   \
        (y) += 2;                                               \
} while (0)

Valores de enumeracao sao todos em !?!uppercase!?!.

enum enumtype { ONE, TWO } et;

Na definicao de integers nao atribuidos utilize “unsigned int” em vez de somente “unsigned”; o anterior tem sido uma fonte de confusao no passado.

Na declaracao de variaveis em estruturas, declare-as organizadas por uso, por seguida tamanho (maior para mais pequeno), e finalmente por ordem alfabetica. Normalmente a primeira categoria, nao se aplica, mas existem exepcoes. Cada uma apresentase numa linha separada. Introduza uma tab apos a primeira palavra, (ex., utilize ‘int^Ix;’ e ‘struct^Ifoo *x;’).

Estruturas mais relevantes deveram ser declaradas no topo do ficheiro que estas estejam a ser usadas, ou em ficheiros header separados, se estas forem utilizadas em varios ficheiros fonte. A utilizacao de estrutura devera ser separada por declaracoes e estas deveram ser externas se forem declaradas nos ficheiros header.

struct foo {
        struct  foo *next;      /* List of active foo */
        struct  mumble amumble; /* Comment for mumble */
        int     bar;
};
struct foo *foohead;            /* Head of global foo list */

Utilize macros ordenadas em vez de se basear na sua propria lista, quando posivel. Portantos o exemplo anterior poderia ser melhorado:

#include <sys/queue.h>
struct foo {
        LIST_ENTRY(foo) link;   /* Queue macro glue for foo lists */
        struct  mumble amumble; /* Comment for mumble */
        int     bar;
};
LIST_HEAD(, foo) foohead;       /* Head of global foo list */

Evite utilizar typedefs para estruturas. Pois isto impossiblita o uso de pointers de uma foma opaca por parte das aplicacoes, que e tanto possivel como benefico utilizar !?ordinary tags?! de uma estrutura. Quando a convencao requere typedef, utilize um nome que corresponde ao !?struct tag?!. Evite typedefs que terminao em “_t”, excepto quando especificado noStandard C ou pelo POSIX.

/*
 * Todas as **!?routines?!** principais deveram ter um breve comentario descrevendo o que estas
 * fazem. O comentarioda **!?routine?!** "main" devera descrever
 * o que o programa faz.
 */
int
main(int argc, char *argv[])
{
        int aflag, bflag, ch, num;
        const char *errstr;

Para haver consistencia, getopt deveria ser usado para parsear as opcoes. Estas deverao ser ordenadas no call getopt e no !?switch?!, a excepcao do !?switch cascade?!. Elementos do !?switch statement?! que o cascade deveria ter um comentario !?FALLTHROUGH?!. Argumentos Numericos deverao ser verificados para precisao.

while ((ch = getopt(argc, argv, "abn:")) != -1) {
        switch (ch) {           /* Indent the switch. */
        case 'a':               /* Don't indent the case. */
                aflag = 1;
                /* FALLTHROUGH */
        case 'b':
                bflag = 1;
                break;
        case 'n':
                num = strtonum(optarg, 0, INT_MAX, &errstr);
                if (errstr) {
                        warnx("number is %s: %s", errstr, optarg);
                        usage();
                }
                break;
        default:
                usage();
        }
}
argc -= optind;
argv += optind;

Utilize um espaco depois de palavras chave (if, while, for, return, switch). Os parentices nao sao utilizados para !?statements!? de controlo com nehuma ou apenas uma simples !?statement?!, a excepcao de essa !?statement?! for mais de uma linha, que nesse caso sao permitidas.

for (p = buf; *p != '\0'; ++p)
        continue;
for (;;)
        stmt;
for (;;) {
        z = a + really + long + statement + that + needs +
            two + lines + gets + indented + four + spaces +
            on + the + second + and + subsequent + lines;
}
for (;;) {
        if (cond)
                stmt;
}

Algumas partes de um for loop poderao ser deixadas em branco.

for (; cnt < 15; cnt++) {
        stmt1;
        stmt2;
}

Identacao e um !?tab?! de 8 caractres. Identacao de segundo nivel sao quatro espacos. Todo o codigo deve caber em 80 colunas.

while (cnt < 20)
        z = a + really + long + statement + that + needs +
            two + lines + gets + indented + four + spaces +
            on + the + second + and + subsequent + lines;

Nao adicione espacos em branco no final da linha, e somente utilize !?tabs?! seguidos de espacos para criar a identacao. Nao utilize mais espacos que um !?tab?! ira produzir, e nao utilize estes antes de espacos.

Abertura e fecho de parentises apresentam-se na mesma linha que os anteriores. Parentises desnecessarios poderam poderam ser omitidos, a nao ser que estes causem um erro de compilamento.

if (test)
        stmt;
else if (bar) {
        stmt;
        stmt;
} else
        stmt;

Nao utilize espacos depois de nomes de funcoes. Virgulas apresentam um espaco apos as mesmas. Nao utilize espacos depois dos seguintes caracteres: ‘(’ ou ‘[’ ou precedendo ‘]’ or ‘)’.

if ((error = function(a1, a2)))
        exit(error);

Operadores unitarios nao requerem espacos; ao contrario dos operadores binarios. Nao utilize parentises a nao ser que estes sejam requeridos para precedencia, e que o !?statement?! se torne confuso sem a existencia destes, a nao ser que estes causem um erro de compilamento. Lembre-se que esta pratica podera confudir outros.

a = b->c[0] + ~d == (e || f) || g && h ? i : j >> 1;
k = !(l & FLAGS);

Exits deverao utilizar 0 para representar sucesso, ou diferente de zero para erros.

/*
 * Tente evitar comentarios obvios, como:
 * "Exit 0 com successo."
 */
exit(0);

Esta tipo de funcao devera se apresentar numa linha separada, precedendo a propria funcao.

static char *
function(int a1, int a2, float fl, int a4)
{

Ao declarar variaveis de funcoes, declare-as organizadas por uso, por seguida tamanho (maior para mais pequeno), e finalmente por ordem alfabetica; multipas por linha sao aceites. Declaracoes de funcoes de acordo com o estilo antigo deverao ser evitadas. Declaracoes de funcoes que utilizao o estilo ANSI deverao ser emvez postas num ficheiro include, como por exemplo “extern.h”. Se acontecer um “overflow” numa das linhas, reutilize o tipo da palavra-chave.

enha cuidade para nao ofuscar codigo, atraves da intializacao de variaveis em declaracoes. Utilize esta tecnica apenas da maneira correcta. NAO UTILIZE chamadas de funcoes em !?initializers“!?.

struct foo one, *two;
double three;
int *four, five;
char *six, seven, eight, nine, ten, eleven, twelve;
 
four = myfunction();

Nao declare funcoes dentro de outras funcoes.

Casts and sizeof() calls are not followed by a space. Note that indent does not understand this rule.

A utilizacao do specificador?!register“ em novo codigom, nao e a melhor pratica. A optimizacao de compiladores como o gcc, e normalmente a melhor via para a excolha de variaveis a colocar nos “registers” para melhor a performance do codigo. A execpcao disto e em funcoes com contem codigo assembly onde o especificador ”register“ e requerido para uma generacao de codigo apropriada, nao ausencia de optimizacao de compilador.

Na utilizacao de longjmp() ou vfork() num programa, a !?bandeira?! -W ou -Wall, devera ser usada para verificar que o compilador nao cria erros como:

warning: variable `foo' might be clobbered by `longjmp' or `vfork'.

Se qualquer erro de este tipo ocurrer, devera aplicar o “type-qualifier”(=qualificador de tipo), na variavel em questao como ”volatile“. A Enexistencia do mesmo podera resultar em generacao de codigo impropria, quando optimizacai esta ativa. Verifique que para pointers, a localizacao de “volatile” especifica se o qualificador de tipo se aplica para esse pointer, ou mesmo para onde este estara a ser apontado. Um apontador volatil e declarado com “volatile” a direita do ”*“. Exemplo:

char *volatile foo;

Assumindo que “foo” e volatile, mas “*foo” nao. Para “*foo” o ser utilize a seguinte sintax:

volatile char *foo;

Se ambos o apontador e o que este esta apontando form volateis, utilize:

volatile char *volatile foo;

const” e tambem um qualificado de tipo e as mesmas regras se aplicam. A descricao de “register” !?read-only?! do hardware, podera se apresentar como :

const volatile char *reg;

Bandeiras globais defenidas dentro de !?handlers?! de sinais deverao ser do tipo “volatile sig_atomic_t” se possivel. Isto garante que a variavel podera ser acedida como identidade atomica, mesmo quando o sinal tenha sido recebido. Nao e garantido que variaveis globais de outros tipos (como estruturas) quando acedidas por !?handlers?! de sinais.

NULL e a contaste preferida para apontadores nulos. Utilize NULL em vez de (type *)0 ou (type *)NULL em todos os casos execpto para argumentos para funcoes !?variadic?!, onde o compilador nao tem conhecimento dos tipos do mesmo.

Nao utilize ‘!’ para testes a nao ser que seja uma expressao boolean, ex., utilize

if (*p == '\0')

nao

if (!*p)

!?Routines?! que retornam void * nao deveriam ter os seus avlores de !?return?! cast para qualquer tipo de apontador to any pointer type.

Utilize as familias de funcoes err e warn. Nao utilize as suas proprias !?!?!”roll/rolling“

if ((four = malloc(sizeof(struct foo))) == NULL)
        err(1, NULL);
if ((six = (int *)overflow()) == NULL)
        errx(1, "Number overflowed.");
return eight;

Funcoes de estilo antigo apresentam-se desta maneira:

static char *
function(a1, a2, fl, a4)
        int a1, a2;     /* Declare ints, too, don't default them. */
        float fl;       /* Beware double vs. float prototype differences. */
        int a4;         /* List in order declared. */
{
        ...
}

Utilize declaracoes de funcoes ANSI, a exepcao da necessidade de compatibilade K&R. Listas longas de parametros apresentam-se com a identacao normal de quatro espacos.

Numeros variaveis de argumentos devem-se apresentar desta maneira:

#include <stdarg.h>
 
void
vaf(const char *fmt, ...)
{
        va_list ap;
        va_start(ap, fmt);
 
        STUFF;
 
        va_end(ap);
 
        /* No return needed for void functions. */
}
 
static void
usage(void)
{

Expressoes de uso deverao-se apresentar da mesma forma que a synopsis das pagina do manual. Opcoes sem !?operands=operandos?!apresentam-se em primeiro lugar, por ordem alfabetca dentro de um unico conjunto de parentises, seguido de opcoes com operandos ou operadores verificar, por ordem alfabetca dentro de parentises em pares, seguido de argumentos requeridos na ordem em que estes estao especificados, seguidos de argumentos opcionais tambem na ordem em que estes estao especificados.

Utilize uma barra (‘|’) para separar quer argumentos e opcoes, ou quer argumentos e opcoes multiplas que sejam especificadas juntas e que sejam postas num unico conjunto de parentises.

Se numeros sao utilizados como opcoes, estes deveram se apresentar primeiro, como mostra o exemplo a baixo. letras em Uppercase tem precedencia de letras em lowercase.

"usage: f [-12aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n"
"usage: f [-a | -b] [-c [-de] [-n number]]\n"

A funcao getprogname podera ser usada em vez de codificar !?hardcode?! o nome do programa.

fprintf(stderr, "usage: %s [-ab]\n", getprogname());

Novo codigo base de kernel devera ser rasoavelmente repeitador destes parametros de estilo. Os parametros para modulos mantidos por terceiros, e drivers de !?devices?! poderam nao ter de seguir estes a risca, mas no minimo deverao ter alguma coerencia nesse mesmo estilo.

Sempre que possivel, codigo devera correr atraves de um verificador de codigo (ex., “gcc -Wall -W -Wpointer-arith -Wbad-function-cast …” or splint from the ports tree) e produzir o minimo de erros possivel. Visto que !?lint?! foi removido, o unico comentario no estilo lint que devera ser usado e somente FALLTHROUGH, visto que este e util para humanos. Outro tipo de comentarios no estilo lint como ARGSUSED, LINTED, e NOTREACHED deverao ser apagados.

Verifique que certa documentacao segue os seus proprios parametros de estilo, como documentado em mdoc.

História

Este artigo é em grande parte baseado no arquivo src/admin/style/style da publicaão 4.4BSD-Lite2, com as Atualizações necessárias para refletir a prática atual de desenvolvimento assim como desejado para o projecto HyperbolaBSD.

Licensiamento

Este artigo wiki esta publica sobre a Licenca FreeBSD.

Créditos

Este artigo wiki está baseado em OpenBSD Manual Page.