...(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 arquivos include do kernel (ex., ) , normalmente, apresentam-se primeiro, irá percisar de ou , mas nunca ambos! inclui , e não há problema de depender disso. #include /* arquivos include não-locais em **!?brackets?!**. */ Se for um programa de rede, ponha o arquivo include relacionados com rede em sequencia. #include #include #include #include Deixe uma linha em branco, seguida por arquivos/usr/include. Os arquivos /usr/include , na generalidade, deveram estar organizados. Pathnames globais estão definidos em /usr/include/paths.h. Pathnames locais para um programa, em especifico, estão definidos em pathnames.h no diretório local. #include Deixe uma linha em branco, seguida para os arquivos iclude relativos ao usuário. #include "pathnames.h" /* arquivos locais includes em cotações duplas. */ Todas as funções necessitam de ser initializadas com um prototipo da mesma. Prototipos de funções privadas (ex., funções que não são utilizadas externamente) apresentam-se no topo do primeiro módulo de fonte. No sistema do usuário, funções locais de um módulo em especifico deverão ser declaradas como 'static'. Isto não deverá ser feito no kernel, pois impossiblita o uso de um debugger para o kernel. Funções usadas por outras partes do kernel deverãoo ser inicializadas com um protótipo no arquivo include relevante, a mesma. Funções que são utilizadas localmente em mais de um módulo fonte, apresenta-se num arquivo header em separado, (ex., extern.h). Prototipos não deveram incluir nomes de variáveis com os tipos; ex., void function(int); not: void function(int a); Prototipos poderam ter um espaço extra depois do tab para ativar o alinhamento dos nomes das funções: static char *function(int, const char *); static void usage(void); Não deverá haver espaço entre o nome da função e a lista dos argumentos. Utilize %%__dead%% de para funcoes que nao correm, ex., __dead void abort(void); Nos arquivos header, introduza os prototipos das funções conpreendidos por %%__BEGIN_DECLS%% / %%__END_DECLS%% como pares. Isto torna os ficheiros header utilizaveis para C++. Macros são capitalizados e apresentados entre parenteses, e deveram evitar efeitos-secundários. Se estes são acrescentados em série de uma função, a função é 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 fácil de ler. Se a macro emcapusula um **!?!compound statement!?!**, incorpore esta num loop "do", para que esta possa ser utilizada com segurança num “if” **!?!statements!?!**. Qualquer ponto-virgula, utilizado como terminação devera ser ?!obtida?! pela invocacao da macro e nao pela mesma, para fazer com que este possa ser parsable, para ?!concatenação!? e editores de texto. #define MACRO(x, y) do { \ variable = (x) + (y); \ (y) += 2; \ } while (0) Valores de enumeração são todos em **!?!uppercase!?!**. enum enumtype { ONE, TWO } et; Na definicao de integers não atribuidos utilize “unsigned int” em vez de somente “unsigned”; o anterior tem sido uma fonte de confusão no passado. Na declaração de variáveis nas estruturas, declare-as organizadas por uso, em seguida tamanho (maior para mais pequeno), e finalmente por ordem alfabética. Normalmente a primeira categoria, não se aplica, mas existem exepções. Cada uma apresentase numa linha separada. Introduza uma tab após a primeira palavra, (ex., utilize ‘int^Ix;’ e ‘struct^Ifoo *x;’). Estruturas mais relevantes deveram ser declaradas no topo do arquivo que estas estejam a ser usadas, ou nos arquivos header separados, se estas forem utilizadas em vários arquivos fonte. A utilização de estrutura devera ser separada por declaracoes e estas deveram ser externas se forem declaradas nos arquivos 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 própria lista, quando possível. Portanto, o exemplo anterior poderia ser melhorado: #include 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 aplicações, que e tanto possível como benefico utilizar **!?ordinary tags?!** de uma estrutura. Quando a convenção requer typedef, utilize um nome que corresponde ao **!?struct tag?!**. Evite typedefs que terminão em “_t”, exceto 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 opções. Estas deverão ser ordenadas no call getopt e no **!?switch?!**, a excepção do **!?switch cascade?!**. Elementos do **!?switch statement?!** que o cascade deveria ter um comentário **!?FALLTHROUGH?!**. Argumentos Numericos deverão ser verificados para precisão. 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 espaço depois das palavras chave (if, while, for, return, switch). Os parentises não são utilizados para **!?statements!?** de controle com nehuma ou apenas uma simples **!?statement?!**, a exceção dessa **!?statement?!** for mais de uma linha, que nesse caso são 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 poderão ser deixadas em branco. for (; cnt < 15; cnt++) { stmt1; stmt2; } Identação é um **!?tab?!** de 8 caractres. Identaçãoo de segundo nível são quatro espaços. Todo o código 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; Não adicione espaços em branco no final da linha, e somente utilize **!?tabs?!** seguidos de espaços para criar a identação. Não utilize mais espaços que um **!?tab?!** ira produzir, e não utilize estes antes de espaços. Abertura e fechamento de parentises apresentam-se na mesma linha que os anteriores. Parentises desnecessarios poderam ser omitidos, a não ser que estes causem um erro de compilamento. if (test) stmt; else if (bar) { stmt; stmt; } else stmt; Não utilize espaços depois dos nomes das funções. Virgulas apresentam um espaço após as mesmas. Não utilize espaços depois dos seguintes caracteres: ‘(’ ou ‘[’ ou precedendo ‘]’ or ‘)’. if ((error = function(a1, a2))) exit(error); Operadores unitarios não requerem espaços; ao contrário dos operadores binários. Não utilize parentises a não ser que estes sejam requeridos para precedencia, e que o **!?statement?!** se torne confuso sem a existencia destes, a não ser que estes causem um erro de compilamento. Lembre-se que esta prática podera confudir outros. a = b->c[0] + ~d == (e || f) || g && h ? i : j >> 1; k = !(l & FLAGS); Exits deverão utilizar 0 para representar sucesso, ou diferente de zero para erros. /* * Tente evitar comentários obvios, como: * "Exit 0 com successo." */ exit(0); Este tipo de função deverá se apresentar numa linha separada, precedendo a própria função. static char * function(int a1, int a2, float fl, int a4) { Ao declarar variáveis das funções, declare-as organizadas por uso, por seguida tamanho (maior para mais pequeno), e finalmente por ordem alfabética; multipas por linha são aceites. Declaracoes de funções de acordo com o estilo antigo deverao ser evitadas. Declaracoes de funcoes que utilizao o estilo ANSI deverão ser em vez postas num arquivo include, como por exemplo “extern.h”. Se acontecer um "overflow" numa das linhas, reutilize o tipo da palavra-chave. Tenha cuidado para não ofuscar o código, através da utilização de variaveis em declarações. Utilize esta técnica apenas da maneira correcta. NãO UTILIZE chamadas de funções em **!?initializers"!?**. struct foo one, *two; double three; int *four, five; char *six, seven, eight, nine, ten, eleven, twelve; four = myfunction(); Não declare funções dentro de outras funções. Casts and sizeof() calls are not followed by a space. Note that indent does not understand this rule. A utilização do **specificador?!** "register" em novo código, não é a melhor prática. A otimização dos compiladores como o gcc, é normalmente a melhor via para a excolha de variaveis a colocar nos "registers" para melhor a performance do código. A exeção disto é em funções que contém código assembly onde o especificador "register" e requerido para uma generacao de codigo apropriada, não ausencia de optimizacao de compilador. Na utilização de longjmp() ou vfork() num programa, a **!?bandeira?!** -W ou -Wall, deverá ser usada para verificar que o compilador não cria erros como: warning: variable `foo' might be clobbered by `longjmp' or `vfork'. Se qualquer erro deeste tipo ocorrer, deverá aplicar o "type-qualifier"(=qualificador de tipo), na variavel em questão como "volatile". A Enexistencia do mesmo podera resultar em generação de código impropria, quando otimização estar ativa. Verifique que para pointers, a localização 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 também um qualificado de tipo e as mesmas regras se aplicam. A descrição 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 execeto para argumentos para funções **!?variadic?!**, onde o compilador não tem conhecimento dos tipos do mesmo. Não utilize ‘!’ para testes a não ser que seja uma expressão boolean, ex., utilize if (*p == '\0') não if (!*p) **!?Routines?!** que retornam void * não deveriam ter os seus valores de **!?return?!** **cast** para qualquer tipo de apontador to any pointer type. Utilize as familias de funções 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; Funções desse modelo do 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 declarações de funções ANSI, a exeção da necessidade de compatibilade K&R. Listas longas de parametros apresentam-se com a identação normal de quatro espaços. Numeros variaveis de argumentos devem-se apresentar desta maneira: #include 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) { Expressões de uso deverão se apresentar da mesma forma que a synopsis da página do manual. Opções sem **!?operands=operandos?!**apresentam-se em primeiro lugar, por ordem alfabétca dentro de um único conjunto de parentises, seguido das opções com **operandos ou operadores verificar**, por ordem alfabetca dentro de parentises em pares, seguido de argumentos requeridos na ordem em que estes estão especificados, seguidos de argumentos opcionais também na ordem em que estes estão especificados. Utilize uma barra (‘|’) para separar quer argumentos e opções, ou quer argumentos e opções multiplas que sejam especificadas juntas e que sejam postas num único 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 código base de kernel deverá ser rasoavelmente respeitado destes parametros de modelo. Os parametros para modulos mantidos por terceiros, e drivers de **!?devices?!** poderam não ter de seguir estes a risca, mas no minimo deverão ter alguma coerencia nesse mesmo modelo. Sempre que possível, código deverá rodar através de um verificador de código (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 único comentário no modelo **lint** que deverá ser usado e somente FALLTHROUGH, visto que este é útil para humanos. Outro tipo de comentarios no modelo **lint** como ARGSUSED, LINTED, e NOTREACHED deverao ser apagados. Verifique que certa documentação 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. ===== Licenciamento ===== Este artigo da wiki estar publicado sobre a [[https://www.freebsd.org/copyright/freebsd-license.html|Licença FreeBSD]]. ===== Créditos ===== Este artigo wiki estar baseado no **[[https://man.openbsd.org/|OpenBSD Manual Page]]**.