Para começar, existem alguns nomes convencionais para diretórios que você não pode ignorar, baseados na longa tradição do sistema de arquivos Unix. Esses são:
trunk
├── bin : for all executables (applications)
├── lib : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src : for source files
└── doc : for documentation
Provavelmente, é uma boa ideia manter esse layout básico, pelo menos no nível superior.
Sobre a divisão dos arquivos de cabeçalho e arquivos de origem (cpp), ambos os esquemas são bastante comuns. No entanto, costumo preferir mantê-los juntos, é apenas mais prático nas tarefas diárias reunir os arquivos. Além disso, quando todo o código está em uma pasta de nível superior, ou seja, a trunk/src/
pasta, você pode observar que todas as outras pastas (bin, lib, include, doc e talvez alguma pasta de teste) no nível superior, além de o diretório "build" para uma build fora da fonte, são todas as pastas que contêm nada além de arquivos gerados no processo de build. E, portanto, apenas é necessário fazer backup da pasta src, ou muito melhor, mantida em um sistema / servidor de controle de versão (como Git ou SVN).
E quando se trata de instalar seus arquivos de cabeçalho no sistema de destino (se você finalmente distribuir sua biblioteca), o CMake possui um comando para instalar arquivos (cria implicitamente um destino "install", para "make install") que você pode usar para colocar todos os cabeçalhos no /usr/include/
diretório Eu apenas uso a seguinte macro cmake para esse fim:
# custom macro to register some headers as target for installation:
# setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)
Onde SRCROOT
está uma variável cmake que eu defini para a pasta src e INCLUDEROOT
é a variável cmake que eu configuro para onde quer que os cabeçalhos precisem ir. Claro, existem muitas outras maneiras de fazer isso, e tenho certeza de que meu caminho não é o melhor. O ponto é que não há razão para dividir os cabeçalhos e as fontes apenas porque apenas os cabeçalhos precisam ser instalados no sistema de destino, porque é muito fácil, especialmente com o CMake (ou CPack), escolher e configurar os cabeçalhos para ser instalado sem a necessidade de tê-los em um diretório separado. E é isso que tenho visto na maioria das bibliotecas.
Citação: Em segundo lugar, eu gostaria de usar o Google C ++ Testing Framework para testar o meu código por unidade, pois parece bastante fácil de usar. Você sugere empacotá-lo com meu código, por exemplo, em uma pasta "inc / gtest" ou "contrib / gtest"? Se incluído, você sugere o uso do script fuse_gtest_files.py para reduzir o número ou os arquivos ou deixá-lo como está? Se não estiver agrupado, como essa dependência é tratada?
Não agrupe dependências com sua biblioteca. Geralmente, essa é uma ideia horrível, e eu sempre odeio quando estou parada tentando construir uma biblioteca que fez isso. Deve ser o seu último recurso e cuidado com as armadilhas. Freqüentemente, as pessoas agrupam dependências em sua biblioteca porque elas visam um ambiente de desenvolvimento terrível (por exemplo, Windows) ou porque suportam apenas uma versão antiga (obsoleta) da biblioteca (dependência) em questão. A principal armadilha é que sua dependência empacotada pode colidir com versões já instaladas da mesma biblioteca / aplicativo (por exemplo, você empacotou o gtest, mas a pessoa que tenta compilar sua biblioteca já possui uma versão mais nova (ou mais antiga) do gtest já instalada e, em seguida, os dois podem entrar em conflito e dar a essa pessoa uma dor de cabeça muito desagradável). Então, como eu disse, faça por sua conta e risco, e eu diria apenas como último recurso. Pedir às pessoas que instalem algumas dependências antes de poder compilar sua biblioteca é um mal muito menor do que tentar resolver conflitos entre suas dependências agrupadas e as instalações existentes.
Citação: Quando se trata de escrever testes, como eles geralmente são organizados? Eu estava pensando em ter um arquivo cpp para cada classe (test_vector3.cpp por exemplo), mas todos compilados em um binário para que todos possam ser executados juntos facilmente?
Um arquivo cpp por classe (ou pequeno grupo coeso de classes e funções) é mais usual e prático na minha opinião. No entanto, definitivamente, não os compile todos em um binário apenas para que "todos possam ser executados juntos". Essa é uma péssima ideia. Geralmente, quando se trata de codificação, você deseja dividir as coisas o quanto for razoável. No caso de testes de unidade, você não deseja que um binário execute todos os testes, porque isso significa que qualquer pequena alteração feita em qualquer coisa da sua biblioteca provavelmente causará uma recompilação quase total desse programa de teste de unidade , e são apenas alguns minutos / horas perdidos à espera da recompilação. Basta seguir um esquema simples: 1 unidade = 1 programa de teste de unidade. Então,
Citação: Como a biblioteca gtest geralmente é construída usando cmake e make, eu estava pensando que faria sentido para o meu projeto também ser construído assim? Se eu decidisse usar o seguinte layout do projeto:
Prefiro sugerir este layout:
trunk
├── bin
├── lib
│ └── project
│ └── libvector3.so
│ └── libvector3.a products of installation / building
├── docs
│ └── Doxyfile
├── include
│ └── project
│ └── vector3.hpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── src
│ └── CMakeLists.txt
│ └── Doxyfile.in
│ └── project part of version-control / source-distribution
│ └── CMakeLists.txt
│ └── vector3.hpp
│ └── vector3.cpp
│ └── test
│ └── test_vector3.cpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── build
└── test working directories for building / testing
└── test_vector3
Algumas coisas a serem observadas aqui. Primeiro, os subdiretórios do diretório src devem espelhar os subdiretórios do diretório de inclusão, isso é apenas para manter as coisas intuitivas (tente também manter a estrutura do subdiretório razoavelmente plana (superficial), porque o aninhamento profundo de pastas geralmente é mais complicado do que qualquer outra coisa). Segundo, o diretório "include" é apenas um diretório de instalação, seu conteúdo é exatamente o que os cabeçalhos selecionam no diretório src.
Terceiro, o sistema CMake deve ser distribuído pelos subdiretórios de origem, não como um arquivo CMakeLists.txt no nível superior. Isso mantém as coisas locais, e isso é bom (no espírito de dividir as coisas em partes independentes). Se você adicionar uma nova fonte, um novo cabeçalho ou um novo programa de teste, tudo o que você precisa é editar um arquivo CMakeLists.txt pequeno e simples no subdiretório em questão, sem afetar mais nada. Isso também permite reestruturar os diretórios com facilidade (CMakeLists são locais e estão contidos nos subdiretórios que estão sendo movidos). Os CMakeLists de nível superior devem conter a maioria das configurações de nível superior, como definir diretórios de destino, comandos personalizados (ou macros) e localizar pacotes instalados no sistema. Os CMakeLists de nível inferior devem conter apenas listas simples de cabeçalhos, fontes,
Citação: como o CMakeLists.txt deveria parecer para poder criar apenas a biblioteca ou a biblioteca e os testes?
A resposta básica é que o CMake permite excluir especificamente determinados destinos de "todos" (que é criado quando você digita "make") e também pode criar pacotes específicos de destinos. Não posso fazer um tutorial do CMake aqui, mas é bastante simples descobrir você mesmo. Nesse caso específico, no entanto, a solução recomendada é, obviamente, usar o CTest, que é apenas um conjunto adicional de comandos que você pode usar nos arquivos CMakeLists para registrar um número de destinos (programas) marcados como unidades. testes. Portanto, o CMake colocará todos os testes em uma categoria especial de compilações, e é exatamente isso que você solicitou, portanto, o problema foi resolvido.
Citação: Também vi alguns projetos que têm um anúncio de construção e um diretório bin. A construção acontece no diretório de construção e os binários foram transferidos para o diretório bin? Os binários dos testes e a biblioteca morariam no mesmo local? Ou faria mais sentido estruturá-lo da seguinte maneira:
Ter um diretório de compilação fora da fonte (compilação "fora da fonte") é realmente a única coisa sensata a ser feita, é o padrão de fato atualmente. Portanto, definitivamente, tenha um diretório "build" separado, fora do diretório de origem, exatamente como o pessoal do CMake recomenda, e como todo programador que eu já conheci. Quanto ao diretório bin, bem, isso é uma convenção, e provavelmente é uma boa ideia cumpri-la, como eu disse no começo deste post.
Citação: Eu também gostaria de usar o doxygen para documentar meu código. É possível fazer isso rodar automaticamente com o cmake e make?
Sim. É mais do que possível, é incrível. Dependendo de como você deseja obter, existem várias possibilidades. O CMake possui um módulo para Doxygen (isto é, find_package(Doxygen)
) que permite registrar destinos que executam o Doxygen em alguns arquivos. Se você quiser fazer coisas mais sofisticadas, como atualizar o número da versão no arquivo Doxyfile ou inserir automaticamente uma data / autor para os arquivos de origem e assim por diante, tudo isso é possível com um pouco de kung-fu do CMake. Geralmente, isso implica que você mantenha um Doxyfile de origem (por exemplo, o "Doxyfile.in" que eu coloquei no layout da pasta acima) que possui tokens a serem encontrados e substituídos pelos comandos de análise do CMake. No meu arquivo CMakeLists de nível superior , você encontrará um desses pedaços de kung-fu do CMake que faz algumas coisas sofisticadas com o cmake-doxygen juntos.