CMake link do biblioteki zewnętrznej

126

Jak sprawić, by CMake połączył plik wykonywalny z zewnętrzną biblioteką udostępnioną, która nie jest skompilowana w ramach tego samego projektu CMake?

Samo działanie target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so)daje błąd

make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'.  Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)

po skopiowaniu biblioteki do katalogu binarnego bin/res.

Próbowałem użyć find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)

Co się nie udaje RESULT-NOTFOUND.

główny
źródło

Odpowiedzi:

101

Najpierw ustaw ścieżkę wyszukiwania bibliotek:

LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/res)

A potem po prostu zrób

TARGET_LINK_LIBRARIES(GLBall mylib)
arrowd
źródło
44
Odradza się używanie programu link_directories, nawet w jego własnej dokumentacji. Myślę, że byłoby lepiej, gdybyśmy rozwiązali problem zawarty find_libraryw pierwotnym pytaniu lub skorzystali z rozwiązania @ Andre.
Fraser
4
Uważam, że „zaimportowana” biblioteka docelowa jest bardziej niezawodna, ponieważ kieruje ją na lokalizację konkretnej biblioteki, zamiast tego po prostu podaje globalną ścieżkę wyszukiwania. Zobacz odpowiedź Andre.
Mark Lakata
1
Powinieneś zawsze używać find_librarytej ścieżki i używać jej zamiast sztywnego kodowania, por. moja odpowiedź .
usr1234567
121

Odpowiedź arrowdodger jest poprawna i często preferowana. Chciałbym po prostu dodać alternatywę do jego odpowiedzi:

Zamiast katalogu dowiązań można dodać „zaimportowaną” bibliotekę docelową. Coś jak:

# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )

A następnie połącz tak, jakby ta biblioteka została zbudowana przez twój projekt:

TARGET_LINK_LIBRARIES(GLBall mylib)

Takie podejście zapewniłoby nieco większą elastyczność: spójrz na polecenie add_library () i wiele właściwości docelowych związanych z importowanymi bibliotekami .

Nie wiem, czy to rozwiąże problem ze „zaktualizowanymi wersjami bibliotek”.

André
źródło
2
Prawdopodobnie tak by było add_library( mylib SHARED IMPORTED )lub pojawi się add_library called with IMPORTED argument but no library typebłąd
Marvin
4
@Andre: Myślę, że po IMPORTED_LOCATIONnawiasie otwierającym jest źle
Ela782
5
musisz dodać GLOBALpo, IMPORTEDjeśli chcesz uzyskać dostęp do zaimportowanej biblioteki w katalogach powyżej bieżącego:add_library(breakpad STATIC IMPORTED GLOBAL)
Roman Kruglov
@Andre IMPORTED_LOCATION wydaje się wymagać ścieżki do pliku zamiast katalogu zawierającego plik
SOUser
1
@SOUser: Tak, IMPORTED_LOCATION powinno wskazywać na plik, a nie na katalog. Naprawiłem to, chyba autor nie będzie narzekał.
Tsyvarev
65

Zakładam, że chcesz utworzyć link do biblioteki o nazwie foo , jej nazwa to zwykle coś, co łączy foo.dlllub libfoo.so.

1. Znajdź bibliotekę
Musisz znaleźć bibliotekę. To dobry pomysł, nawet jeśli znasz ścieżkę do swojej biblioteki. CMake wyświetli błąd, jeśli biblioteka zniknie lub otrzyma nową nazwę. Pomaga to wcześnie wykryć błąd i wyjaśnić użytkownikowi (może samemu), co powoduje problem.
Aby znaleźć bibliotekę foo i zapisać FOO_LIBużywaną ścieżkę

    find_library(FOO_LIB foo)

CMake sam zorientuje się, jaka jest rzeczywista nazwa pliku. Sprawdza typowe miejsca takie jak /usr/lib, /usr/lib64i ścieżkami PATH.

Znasz już lokalizację swojej biblioteki. Dodaj go do CMAKE_PREFIX_PATHwywołania CMake, a następnie CMake będzie szukał Twojej biblioteki również w przekazanych ścieżkach.

Czasami trzeba dodać wskazówki lub sufiksy ścieżek, szczegółowe informacje można znaleźć w dokumentacji: https://cmake.org/cmake/help/latest/command/find_library.html

2. Połącz bibliotekę Od 1. masz pełną nazwę biblioteki w FOO_LIB. Używasz tego, aby połączyć bibliotekę z celem, GLBalljak w

  target_link_libraries(GLBall PRIVATE "${FOO_LIB}")

Należy dodać PRIVATE, PUBLIClub INTERFACEpo docelowej, cf. dokumentacja: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Jeśli nie dodasz jednego z tych specyfikatorów widoczności, będzie on zachowywał się tak jak PRIVATElub PUBLIC, w zależności od wersji CMake i ustawionych zasad.

3. Dodaj dołączenia (ten krok może nie być obowiązkowy).
Jeśli chcesz dołączyć również pliki nagłówkowe, użyj funkcji find_pathpodobnej do find_libraryi wyszukaj plik nagłówkowy. Następnie dodaj katalog include z target_include_directoriespodobnym do target_link_libraries.

Dokumentacja: https://cmake.org/cmake/help/latest/command/find_path.html i https://cmake.org/cmake/help/latest/command/target_include_directories.html

Jeśli jest dostępne dla oprogramowania zewnętrznego, możesz wymienić find_libraryi find_pathprzez find_package.

usr1234567
źródło
5
IMHO to najlepsza odpowiedź. Jednak miałem problem, ponieważ nie wywoływałem „find_library” po „project” i „target_link_libraries” po „add_executable”.
smoothware
1
find_packagejest o wiele prostsze niż wykonanie tych czynności
aktywny dzień
2
Chyba nie rozumiem kroku 2. W przypadku udostępnionej biblioteki $ {FOO_LIB} będzie wyglądać jak /full/path/to/libfoo.dylib. Jak to jest przydatne? target_link_libraries nie tworzy „-L / full / path / to -lfoo”, więc find_library nie zwraca niczego użytecznego poza sprawdzeniem, czy biblioteka znajduje się w miejscu, w którym już wiem, że się znajduje. czego mi brakuje?
guymac
target_link_libraries(mylib "${FOO_LIB}")? Cel jest mylibzamiast prawdziwego celu GLBall,? nie ma dla mnie większego sensu
Bersan
5

Jeszcze jedna alternatywa, w przypadku gdy pracujesz z Appstore, potrzebujesz „Entitlements” i jako takiej musisz połączyć się z Apple-Framework.

Aby uprawnienia działały (np. GameCenter), musisz mieć krok kompilacji „Link Binary with Libraries”, a następnie link do „GameKit.framework”. CMake „wstrzykuje” biblioteki na „niskim poziomie” do wiersza poleceń, stąd Xcode tak naprawdę nie wie o tym i jako taki nie włączysz GameKit na ekranie Możliwości.

Jednym ze sposobów używania CMake i posiadania „Link with Binaries” -buildstep jest wygenerowanie xcodeproj za pomocą CMake, a następnie użycie 'sed' do 'wyszukiwania i zamiany' oraz dodanie GameKit tak, jak lubi to XCode ...

Skrypt wygląda następująco (dla Xcode 6.3.1).

s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
    26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g

s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
    26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g

s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
    26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
        isa = PBXFrameworksBuildPhase;\
        buildActionMask = 2147483647;\
        files = (\
            26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
        );\
        runOnlyForDeploymentPostprocessing = 0;\
    };\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g

s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
            26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
            26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g

zapisz to na „gamecenter.sed”, a następnie „zastosuj” w ten sposób (zmieni to twój xcodeproj!)

sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj

Być może będziesz musiał zmienić polecenia skryptów, aby dopasować je do swoich potrzeb.

Ostrzeżenie: prawdopodobnie zepsuje się z inną wersją Xcode, ponieważ format projektu może się zmienić, unikalny numer (zakodowany na stałe) może nie być unikalny - i ogólnie rozwiązania innych osób są lepsze - więc chyba że musisz wspierać Appstore + Uprawnienia (i automatyczne kompilacje), nie rób tego.

To jest błąd CMake, zobacz http://cmake.org/Bug/view.php?id=14185 i http://gitlab.kitware.com/cmake/cmake/issues/14185

kalmiya
źródło
Konkretnie - przekonanie cmake do połączenia z zewnętrzną biblioteką nie jest problemem (powyżej jest kilka rozwiązań). Uruchomienie tego w sposób zautomatyzowany, tak aby działało z Apple Appstore i uprawnieniami, jest wyzwaniem. W tym konkretnym przypadku powyższe rozwiązania nie działają, ponieważ XCode nie „zobaczy” bibliotek połączonych w ten sposób, a uprawnienia po prostu nie będą działać. Afaik cmake nie może dodawać bibliotek tak, jak potrzebuje tego xcode, w sposób zgodny ze sklepem z aplikacjami - ponownie, nie krępuj się mnie oświecić.
kalmiya
1
Och, to smutne. Dla kompletności link do nowego narzędzia do śledzenia problemów, który obecnie nie zawiera żadnych komend: gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567
Problem został rozwiązany 5 miesięcy temu, więc w najnowszej wersji CMake nie powinien już być obecny. Zobacz gitlab.kitware.com/cmake/cmake/issues/14185
usr1234567,