É possível fazer com que o CMake construa uma versão estática e compartilhada da mesma biblioteca?

141

A mesma fonte, tudo isso, só quer uma versão estática e compartilhada. Fácil de fazer?

gct
fonte

Respostas:

123

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.ae 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.)

Christopher Bruns
fonte
Estava esperando que eles tivessem o mesmo nome, mas tudo bem. Outra pergunta: Você pode dizer ao CMake para vincular bibliotecas estáticas à biblioteca compartilhada quando possível?
GCT
Mais sobre "mesmo nome": se você está no Windows e deseja o mesmo nome para as duas bibliotecas e não precisa do arquivo .lib compartilhado, é possível criar um .lib estático e uma .dll compartilhada. Mas você precisa desse arquivo .lib compartilhado se estiver usando sua biblioteca para vincular o tempo de compilação comum.
Christopher Bruns
1
Não sei se entendi sua pergunta sobre como vincular bibliotecas estáticas à biblioteca compartilhada.
Christopher Bruns
5
Observe que essa não é mais a maneira sugerida de fazer isso. Para projetos de tamanho não trivial (aqueles que levam minutos, não segundos para compilar), é maravilhoso evitar duplicar o tempo de compilação. Veja user465139 resposta abaixo para objeto de uso Biblioteca ou os docs: cmake.org/cmake/help/v3.8/command/...
KymikoLoco
3
@KymikoLoco: A abordagem da Biblioteca de Objetos realmente reduz pela metade o tempo de compilação, mas exige que as bibliotecas estáticas sejam construídas como Código Independente de Posição (ou seja, com -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.
John Zwinck
95

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()ou add_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 *.oobjetos. Essa coleção de *.oarquivos é 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 *.oarquivos 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.

Laringe Decidua
fonte
3
Isso funcionou como um encanto para mim - a única ressalva foram as 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.txtarquivos pela metade.
fish2000
1
Você precisa "escapar" do obblib ao definir as propriedades de destino? ie set_Property (TARGET $ {objlib} PROPRIEDADE ...) vs set_Property (TARGET objlib PROPRIEDADE ...)
gnac
1
Quem recusou isso ... essa pessoa poderia fornecer uma explicação que considerou incorreta? Ainda mais porque essa é a maneira recomendada de fazer o que o OP deseja, consulte os documentos do CMake.
Laryx Decidua
1
@ user465139 Talvez você deva explicar por que deve funcionar para reutilizar arquivos de objetos para destinos estáticos e compartilhados. Especialmente, o conhecimento geral em SO ainda é muito confuso, arquivos antigos / arquivos também não ajudam a esclarecê-lo, por exemplo. cmake.org/pipermail/cmake/2008-March/020315.html É necessária uma explicação sólida do status quo. ps Não fui eu quem
diminuiu o voto
2
@gnac Não posso confirmar isso. No meu caso, o set_propertyúnico funcionou quando eu usei objlibe não ao usar ${objlib}. Talvez essa resposta possa ser corrigida?
Josch
22

Geralmente, não há necessidade de duplicar ADD_LIBRARYchamadas 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=ONe com OFFno outro.

Yaroslav Halchenko
fonte
43
Isso não parece criar AMBAS as versões estáticas e compartilhadas, que eu acho que é o que essa questão está chegando.
Nick Desaulniers
0

É 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 .libarquivos 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 OBJECTbiblioteca 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.

P-Gn
fonte
-2

É 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).

Alexander Amelkin
fonte
10
Isso é desnecessário ao usar as versões do CMake acima de 2.8. [0?], Pois a propriedade foi removida em 2009 e o comportamento fornecido agora é o padrão. Isso pode ser útil para pessoas abaixo de 2,8, mas se você ainda estiver usando o CMake <2,7, eu imploro que você atualize. github.com/Kitware/CMake/commit/…
KymikoLoco