CMake가 동일한 라이브러리의 정적 버전과 공유 버전을 모두 빌드 할 수 있습니까?


141

동일한 소스는 정적 및 공유 버전을 모두 원합니다. 쉬운가요?

답변:


123

예, 적당히 쉽습니다. 두 개의 "add_library"명령을 사용하십시오.

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)

소스 파일이 많은 경우에도 소스 목록을 cmake 변수에 배치하면 여전히 쉽습니다.

Windows에서는 공유 및 정적 파일에 ".lib"파일이 있으므로 각 라이브러리에 다른 이름을 지정해야합니다. 그러나 Linux 및 Mac에서는 두 라이브러리에 동일한 이름 (예 : libMyLib.alibMyLib.so) 을 지정할 수도 있습니다 .

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

그러나 정적 및 동적 버전의 라이브러리에 동일한 이름을 지정하지 않는 것이 좋습니다. 라이브러리에 링크하는 도구에 대해 컴파일 라인에서 정적 링크와 동적 링크를 더 쉽게 선택할 수 있기 때문에 다른 이름을 사용하는 것을 선호합니다. 일반적으로 libMyLib.so(공유) 및 libMyLib_static.a(정적) 과 같은 이름을 선택합니다 . (이것은 리눅스에서 이름이 될 것입니다.)


그들이 같은 이름을 갖기를 바랐지만, 오. 또 다른 질문 : 가능하면 CMake에게 정적 라이브러리를 공유 라이브러리에 연결하도록 지시 할 수 있습니까?
gct

"같은 이름"에 대한 추가 정보 : Windows에 있고 두 라이브러리 모두에 대해 동일한 이름을 원하고 공유 .lib 파일이 필요하지 않은 경우 정적 .lib 및 공유 .dll을 작성할 수 있습니다. 그러나 일반적인 컴파일 시간 연결에 라이브러리를 사용하는 경우 공유 .lib 파일이 필요합니다.
Christopher Bruns

1
정적 라이브러리를 공유 라이브러리에 연결하는 것에 대한 귀하의 질문을 잘 모르겠습니다.
Christopher Bruns

5
참고 이 더 이상 그것을 할 제안 된 방법은 아니라고. 사소한 규모의 프로젝트 (컴파일에 몇 초가 아닌 몇 분이 걸리는 프로젝트)의 경우 컴파일 시간을 두 배로 늘리는 것을 피할 수 없습니다. 객체 라이브러리 사용법 또는 문서에 대해서는 아래의 user465139 답변을 참조하십시오. cmake.org/cmake/help/v3.8/command/…
KymikoLoco

3
@KymikoLoco : 객체 라이브러리 접근 방식은 실제로 컴파일 시간을 절반으로 단축하지만 정적 라이브러리를 위치 독립적 코드 (예 :로 -fPIC)로 작성해야합니다. 정적 라이브러리를 사용할 때 약간의 런타임 오버 헤드가 추가됩니다. 따라서 최대 성능을 위해이 답변이 여전히 최고입니다.
존 즈 빙크

95

CMake 버전 2.8.8부터 "개체 라이브러리" 사용 하여 개체 파일의 중복 컴파일을 피할 수 있습니다 . 두 개의 소스 파일이있는 Christopher Bruns의 라이브러리 예제 사용 :

# 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>)

로부터 CMake 워드 프로세서 :

오브젝트 라이브러리는 소스 파일을 컴파일하지만 오브젝트 파일을 라이브러리에 아카이브하거나 링크하지 않습니다. 대신 다른 대상 은 양식의 표현식을 소스로 사용하여 오브젝트를 참조 add_library()하거나 add_executable()참조 할 수 있습니다 $<TARGET_OBJECTS:objlib>. 여기서 objlib는 오브젝트 라이브러리 이름입니다.

간단히 add_library(objlib OBJECT ${libsrc})말해이 명령은 CMake에게 소스 파일을 *.o객체 파일 로 컴파일하도록 지시 합니다. *.o그런 다음 이 파일 콜렉션을 동일한 오브젝트 파일 세트 에서 공유 및 정적 라이브러리를 빌드하는 적절한 라이브러리 작성 명령을 호출 $<TARGET_OBJECT:objlib>하는 두 add_library(...)명령 에서 참조됩니다 . 소스 파일이 많으면 파일을 컴파일하는 데 시간이 오래 걸릴 수 있습니다. 객체 라이브러리를 사용하면 한 번만 컴파일 할 수 있습니다.*.o

당신이 지불하는 가격은 공유 라이브러리가 이것을 필요로하기 때문에 객체 파일이 위치 독립적 코드로 작성되어야한다는 것입니다 (정적 라이브러리는 신경 쓰지 않습니다). 위치 독립적 인 코드는 효율성이 떨어질 수 있으므로 성능을 극대화하려면 정적 라이브러리를 사용하십시오. 또한 정적으로 링크 된 실행 파일을 배포하는 것이 더 쉽습니다.


3
이것은 나를위한 매력처럼 작동했습니다. 유일한주의 사항은 target_link_libraries()라이브러리에 의존하는 후속 호출이 "객체 라이브러리"를 사용하여 연결할 수 없다는 것입니다. 이들은 새로운 공유 또는 정적 라이브러리를 대상으로해야하며 중복 될 수 있습니다. 그러나 첫 번째 주석가의 경험과는 달리 이것은 매우 유용했으며 복제 된 모든 대상을 제거하고 모든 CMakeLists.txt파일을 거의 반으로 자를 수있었습니다 .
fish2000

1
대상 속성을 설정할 때 obblib을 "탈출"해야합니까? set_property 대 즉 set_property (TARGET $ {objlib} 속성 ...) (TARGET objlib 재산 ...)
gnac

1
누구든지 이것을 하향 투표했다면 ... 그 사람은 무엇이 잘못되었다고 설명 할 수 있습니까? OP가 원하는 것을 권장하는 방법 이기 때문에 CMake 문서를 참조하십시오.
Laryx Decidua

1
@ user465139 아마도 정적 및 공유 대상 모두에 대해 오브젝트 파일을 재사용해야하는 이유를 설명해야합니다. 특히, SO에 대한 일반적인 지식은 여전히 ​​매우 혼란 스럽습니다. 예를 들어 오래된 아카이브는 그것을 명확히하는 데 도움이되지 않습니다. cmake.org/pipermail/cmake/2008-March/020315.html 현상 유지에 대한 확실한 설명이 필요합니다. ps 내가
공감

2
@ gnac 나는 이것을 확인할 수 없습니다. 내 경우는 set_property단지 내가 사용 때 작동 objlib및 사용하지 않을 때 ${objlib}. 아마도이 대답은 수정 될 수 있습니까?
josch

22

일반적으로 ADD_LIBRARY목적을 위해 통화 를 복제 할 필요는 없습니다 . 그냥 활용하십시오

$> 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.

와 (한 아웃의 소스 디렉토리에) 먼저 구축하는 동안 -DBUILD_SHARED_LIBS:BOOL=ON, 그리고 함께 OFF다른있다.


43
이것은 정적 및 공유 버전을 모두 빌드하지 않는 것 같습니다.이 질문에 도달했다고 생각합니다.
Nick Desaulniers

0

이전 답변에서 제안한 것과 동일한 컴파일 호흡으로 eveything을 포장하는 것이 가능하지만 결국 간단한 프로젝트에서만 작동하는 해킹이기 때문에 조언하지 않을 것입니다. 예를 들어, 어느 시점에서 라이브러리의 다른 버전에 대해 다른 플래그가 필요할 수 있습니다 (플래그가 일반적으로 심볼 내보내기간에 전환하는 데 사용되는 Windows의 경우). 또는 위에서 언급 한 것처럼 .lib파일이 정적 또는 공유 라이브러리에 해당하는지에 따라 파일을 다른 디렉토리에 넣을 수 있습니다. 이러한 각 장애물에는 새로운 핵이 필요합니다.

명백 할 수도 있지만 이전에 언급되지 않은 한 가지 대안은 라이브러리 유형을 매개 변수로 만드는 것입니다.

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} )

두 개의 다른 이진 트리에 라이브러리의 공유 및 정적 버전이 있으면 다른 컴파일 옵션을보다 쉽게 ​​처리 할 수 ​​있습니다. 특히 컴파일이 자동화 된 경우 컴파일 트리를 고유하게 유지하는 데 심각한 결점이 없습니다.

중간 OBJECT라이브러리를 사용하여 컴파일을 상호 작용하려는 경우에도 (위에 언급 된 경고를 사용하여 강력한 이유가 필요함) 여전히 엔드 라이브러리를 두 개의 다른 프로젝트에 넣을 수 있습니다.


-2

실제로 가능합니다. @Christopher Bruns가 그의 답변에서 말했듯이 라이브러리의 두 가지 버전을 추가해야합니다.

set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})

그런 다음 여기 에 설명 된대로 두 대상 모두 동일한 출력 이름을 사용하고 서로의 파일을 덮어 쓰지 않도록 지정해야합니다.

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)

이 방법으로 libmylib.a 및 libmylib.so (Linux) 또는 mylib.lib 및 mylib.dll (Windows)을 모두 얻을 수 있습니다.


10
속성이 2009 년에 제거되어 2.8. [0?] 이상의 CMake 버전을 사용할 때는 불필요하며 제공된 동작이 기본값입니다. 이것은 2.8 미만의 사용자에게 유용 할 수 있지만 CMake <2.7을 계속 사용하는 경우 업그레이드를 요청합니다. github.com/Kitware/CMake/commit/…
KymikoLoco
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.