Respostas:
Sim, é moderadamente fácil. Basta usar dois comandos "add_library":
add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)
Mesmo se você tiver muitos arquivos de origem, você colocaria a lista de fontes em uma variável cmake, por isso ainda é fácil.
No Windows, você provavelmente deve atribuir um nome diferente a cada biblioteca, pois existe um arquivo ".lib" para compartilhado e estático. Mas no Linux e Mac, você pode até dar o mesmo nome às duas bibliotecas (por exemplo, libMyLib.a
e libMyLib.so
):
set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)
Mas não recomendo atribuir o mesmo nome às versões estática e dinâmica da biblioteca. Prefiro usar nomes diferentes, porque isso facilita a escolha da ligação estática versus dinâmica na linha de compilação para as ferramentas vinculadas à biblioteca. Normalmente eu escolho nomes como libMyLib.so
(compartilhado) e libMyLib_static.a
(estático). (Esses seriam os nomes no linux.)
-fPIC
), o que adiciona uma pequena quantidade de sobrecarga de tempo de execução quando essas bibliotecas estáticas são usadas. Portanto, para obter o máximo desempenho, essa resposta ainda é a melhor.
Desde o CMake versão 2.8.8, você pode usar "bibliotecas de objetos" para evitar a compilação duplicada dos arquivos de objetos . Usando o exemplo de Christopher Bruns de uma biblioteca com dois arquivos de origem:
# list of source files
set(libsrc source1.c source2.c)
# this is the "object library" target: compiles the sources only once
add_library(objlib OBJECT ${libsrc})
# shared libraries need PIC
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
# shared and static libraries built from the same object files
add_library(MyLib_shared SHARED $<TARGET_OBJECTS:objlib>)
add_library(MyLib_static STATIC $<TARGET_OBJECTS:objlib>)
Dos documentos do CMake :
Uma biblioteca de objetos compila arquivos de origem, mas não arquiva ou vincula seus arquivos de objetos a uma biblioteca. Em vez disso, outros destinos criados por
add_library()
ouadd_executable()
podem fazer referência aos objetos usando uma expressão do formulário$<TARGET_OBJECTS:objlib>
como fonte, em que objlib é o nome da biblioteca de objetos.
Simplificando, o add_library(objlib OBJECT ${libsrc})
comando instrui o CMake a compilar os arquivos de origem em arquivos de *.o
objetos. Essa coleção de *.o
arquivos é referida $<TARGET_OBJECT:objlib>
nos dois add_library(...)
comandos que chamam os comandos de criação de biblioteca apropriados que constroem as bibliotecas compartilhadas e estáticas a partir do mesmo conjunto de arquivos de objetos. Se você possui muitos arquivos de origem, a compilação dos *.o
arquivos pode demorar bastante; com bibliotecas de objetos, você as compila apenas uma vez.
O preço que você paga é que os arquivos de objeto devem ser criados como código independente da posição, porque as bibliotecas compartilhadas precisam disso (bibliotecas estáticas não se importam). Observe que o código independente da posição pode ser menos eficiente; portanto, se você busca o desempenho máximo, deve procurar bibliotecas estáticas. Além disso, é mais fácil distribuir executáveis vinculados estaticamente.
target_link_libraries()
chamadas subseqüentes que dependem da sua biblioteca e não podem usar a "biblioteca de objetos" para vincular; esses devem ter como alvo as novas bibliotecas compartilhadas ou estáticas (e podem ser duplicadas). Mas, ao contrário da experiência dos primeiros comentaristas, isso foi bastante útil e me permitiu remover todos os alvos duplicados e cortar todos os meus CMakeLists.txt
arquivos pela metade.
set_property
único funcionou quando eu usei objlib
e não ao usar ${objlib}
. Talvez essa resposta possa ser corrigida?
Geralmente, não há necessidade de duplicar ADD_LIBRARY
chamadas para seu propósito. Basta fazer uso de
$> man cmake | grep -A6 '^ *BUILD_SHARED_LIBS$'
BUILD_SHARED_LIBS
Global flag to cause add_library to create shared libraries if on.
If present and true, this will cause all libraries to be built shared unless the library was
explicitly added as a static library. This variable is often added to projects as an OPTION
so that each user of a project can decide if they want to build the project using shared or
static libraries.
durante a construção, primeiro (em um diretório fora da fonte) com -DBUILD_SHARED_LIBS:BOOL=ON
e com OFF
no outro.
É possível empacotar tudo no mesmo fôlego de compilação, como sugerido nas respostas anteriores, mas eu aconselho isso, porque no final é um hack que funciona apenas para projetos simples. Por exemplo, em algum momento você pode precisar de sinalizadores diferentes para diferentes versões da biblioteca (especialmente no Windows, onde os sinalizadores são normalmente usados para alternar entre exportar símbolos ou não). Ou, como mencionado acima, você pode colocar .lib
arquivos em diretórios diferentes, dependendo de corresponderem a bibliotecas estáticas ou compartilhadas. Cada um desses obstáculos exigirá um novo hack.
Pode ser óbvio, mas uma alternativa que não foi mencionada anteriormente é tornar o tipo da biblioteca um parâmetro:
set( ${PROJECT_NAME}_LIBTYPE CACHE STRING "library type" )
set_property( CACHE ${PROJECT_NAME}_LIBTYPE PROPERTY STRINGS "SHARED;STATIC" )
add_library( ${PROJECT_NAME} ${PROJECT_NAME}_LIBTYPE ${SOURCE_FILES} )
Ter versões compartilhadas e estáticas da biblioteca em duas árvores binárias diferentes facilita o manuseio de diferentes opções de compilação. Não vejo nenhuma desvantagem séria em manter as árvores de compilação distintas, especialmente se suas compilações são automatizadas.
Observe que, mesmo que você pretenda mutualizar compilações usando uma OBJECT
biblioteca intermediária (com as advertências mencionadas acima, portanto, você precisa de um motivo convincente para fazê-lo), ainda assim as bibliotecas finais poderão ser colocadas em dois projetos diferentes.
É de fato possível. Como @Christopher Bruns disse em sua resposta, você precisa adicionar duas versões da biblioteca:
set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})
Então, conforme descrito aqui , você precisa especificar que os dois destinos devem usar o mesmo nome de saída e não substituir os arquivos um do outro:
SET_TARGET_PROPERTIES(mylib-static PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(mylib-shared PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)
Dessa forma, você obterá libmylib.a e libmylib.so (no Linux) ou mylib.lib e mylib.dll (no Windows).