8000 libssh2 iOS link by tiennou · Pull Request #4604 · libgit2/libgit2 · GitHub
[go: up one dir, main page]

Skip to content

libssh2 iOS link #4604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed

Conversation

tiennou
Copy link
Contributor
@tiennou tiennou commented Mar 29, 2018

This is the changes needed on libgit2's side for libgit2/objective-git#645.

Note that I'm basing this on master so it's mergeable, but I would really appreciate if someone with push access could bring fix/libssh2-ios-link-v0.27 in the main repo (which is those 2 commits, on top of maint/v0.27), just so I could update OCGit's submodule without switching to my fork (which would be unfortunate and "weird").

@@ -292,7 +292,18 @@ IF (LIBSSH2_FOUND)
LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
#SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")

CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
# We might need to provide additional libraries so libssh2 links
SET(CMAKE_REQUIRED_LIBRARIES_OLD ${CMAKE_REQUIRED_LIBRARIES})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMAKE_REQUIRED_LIBRARIES is only used for CMake's internals such that it can successfully link test executables, right? If so, I'd expect this dance around CMAKE_REQUIRED_LIBRARIES_OLD to be useless. Just set CMAKE_REQUIRED_LIBRARIES and unset it afterwards again. Or does it have a default value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it is there for that express purpose, yes. I'll just unconditionally unset.

SET(CMAKE_REQUIRED_LIBRARIES_OLD ${CMAKE_REQUIRED_LIBRARIES})
IF(BUILD_SHARED_LIBS)
SET(CMAKE_REQUIRED_LIBRARIES ${LIBSSH2_LIBRARIES})
SET(LIBSSH2_TMP_LIBRARY_DIRS ${LIBSSH2_LIBRARY_DIRS})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this in case of shared libraries? If not, we could probably make this much simpler:

IF(BUILD_SHARED_LIBS)
    CHECK_LIBRARY_EXISTS(...) # The old code previous to this commit
ELSE()
    SET(CMAKE_REQUIRED_LIBRARIES ${LIBSSH2_STATIC_LIBRARIES})
    CHECK_LIBRARY_EXISTS(ssh2 libssh2_userauth_publickey_frommemory "${LIBSSH2_STATIC_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
    UNSET(CMAKE_REQUIRED_LIBRARIES)
ENDIF()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I was trying to keep a one-and-only-one CHECK_LIBRARY_EXISTS call.

Note that I feel like it means something is wrong w.r.t our pkg-config setup in that static-link case (which I'm ignoring because macOS doesn't actually depend on the built pc file). Also, licensing 😟.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm all for avoiding code duplication. But in this case, it's a lot easier to read with the duplicated code, so I'd say in this case duplication is preferable

"warning: values of type 'OSStatus' should not be used as format arguments; add an explicit cast to 'int' instead [-Wformat]"
@tiennou tiennou force-pushed the fix/libssh2-ios-link branch from 6447e85 to 16a6e21 Compare April 9, 2018 20:35
@tiennou
Copy link
Contributor Author
tiennou commented Apr 9, 2018

Rebased, should be 👍. Thanks for reining in my duplication frenzy @pks-t, as it sure looks nicer that way !

@pks-t
Copy link
Member
pks-t commented Apr 12, 2018

Small nit: would be nice to move the unset into the conditional block, as well. Looks good otherwise, thanks!

@tiennou tiennou force-pushed the fix/libssh2-ios-link branch from 16a6e21 to d0ee33f Compare April 19, 2018 21:09
@tiennou
Copy link
Contributor Author
tiennou commented Apr 19, 2018

Unset moved !

As per the docs[1], another set of variables will be provided by PKG_CHECK_MODULES when static linking (with the additional libraries required for static links). We need to pass those libraries to CHECK_LIBRARY_EXISTS so *its* link succeeds.

1 - https://cmake.org/cmake/help/v3.1/module/FindPkgConfig.html#command:pkg_check_modules

Ref: libgit2/objective-git#645
@tiennou tiennou force-pushed the fix/libssh2-ios-link branch from d0ee33f to 860d31e Compare April 23, 2018 19:38
@tiennou
Copy link
Contributor Author
tiennou commented Apr 23, 2018

@pks-t It would seem I completely botched the change you had asked me, so now it's working again. I'll give a spin to the OCGit build as soon as I can get rid of the Swift issue I'm facing…

@tiennou
Copy link
Contributor Author
tiennou commented Apr 23, 2018

This should hopefully be green everywhere (here and in OCGit). At least locally it's green 🤷‍♂️ .

So, for a little bit of OCGit-related context about that last commit : the issue was that running ./script/update_libgit2 would register libssh2_userauth_publickey_frommemory (henceforth the function), while running ./script/cibuild.sh would not.

Here are the respective link commands from CMake's log :
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -D_GNU_SOURCE -Wall -Wextra -Wdocumentation -Wno-missing-field-initializers -Wstrict-aliasing -Wstrict-prototypes -Wdeclaration-after-statement -Wshift-count-overflow -Wno-unused-const-variable -Wno-unused-function -Wno-deprecated-declarations -DCHECK_FUNCTION_EXISTS=libssh2_userauth_publickey_frommemory -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/cmTC_bf609.dir/CheckFunctionExists.c.o -o cmTC_bf609 -L/usr/local/Cellar/openssl/1.0.2o_1/lib -Wl,-rpath,/usr/local/Cellar/openssl/1.0.2o_1/lib -lssh2 -lssh2 -lssl -lcrypto -lz
vs
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -D_GNU_SOURCE -Wall -Wextra -Wdocumentation -Wno-missing-field-initializers -Wstrict-aliasing -Wstrict-prototypes -Wdeclaration-after-statement -Wshift-count-overflow -Wno-unused-const-variable -Wno-unused-function -Wno-deprecated-declarations -DCHECK_FUNCTION_EXISTS=libssh2_userauth_publickey_frommemory -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.8 -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/cmTC_8cfd1.dir/CheckFunctionExists.c.o -o cmTC_8cfd1 -L/usr/local/Cellar/openssl/1.0.2o_1/lib -Wl,-rpath,/usr/local/Cellar/openssl/1.0.2o_1/lib -lssh2 -lssh2 -lssl -lcrypto -lz
(resulting in ld: library not found for -lssh2)

The only difference is thus the -isysroot parameter appearing in the second, which makes $SYSROOT be used as the prefix, which skips what's brewed in /usr/local/.

On top of that, here are the variables set when pkg-finding static libssh2 :

-- LIBSSH2_LIBS: ssh2;ssl;crypto;z
-- LIBSSH2_LIBDIRS: /usr/local/Cellar/openssl/1.0.2o_1/lib;/usr/local/Cellar/libssh2/1.8.0/lib

If you compare that to the above, you'll see CMake almost tries to use the first part in both cases (openssl), but the 2nd part is nowhere to be found. I heavily suspect this is a bug in CMake, not expecting a list or something, but passing LIBSSH2_LIBDIRS as a space-separated list resulted in -L"$openssl $libssh2"

Hopefully just above this line was the Shiny Sun Of Salvation, hence the fix : activate CMP0056, which pass CMAKE_EXE_LINKER_FLAGS to whatever TRY_COMPILE is doing, and munge the LIBSSH2_LIBDIRS line into it so it links.

Maybe there's a better way 🙄…

SET(CMAKE_EXE_LINKER_FLAGS ${EXE_LINKER_FLAGS})
CHECK_LIBRARY_EXISTS(ssh2 libssh2_userauth_publickey_frommemory "${LIBSSH2_STATIC_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
UNSET(CMAKE_EXE_LINKER_FLAGS)
UNSET(CMAKE_REQUIRED_LIBRARIES)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that's a gross hack. If necessary I'll merge that, but I'm not yet convinced the problem isn't actually us.

Previously, we've been using CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" ...), which works just right with dynamic libraries. Sure, we don't need to pass in dependencies of libssh2, as they're picked up by the linker anyway. That's why ${LIBSSH2_LIBRARIES} will only contain the path to the libssh2.so.

Now the case with static archives. We have to link not only against libssh2, but also explicitly against its dependencies. That's why ${LIBSSH2_LIBRARIES} will be something like "ssh2;ssl;crypto;z". Passing that one again to CHECK_LIBRARY_EXISTS is obviously not gonna work: the first parameter of that function shall be the library we're searching in, not a list of libraries. So what we'd have to do is to only pass ssh2 into CHECK_LIBRARY_EXISTS, but set CMAKE_REQUIRED_LIBRARIES up accordingly to contain the other libraries, as well.

The old commits of yours arent available anymore, so I can't check and don't remember if you did that. I know you used CMAKE_REQUIRED_LIBRARIES, but did you also modify CHECK_LIBRARY_EXISTS to only pass in "ssh2" as first argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's unsightly 😭. IIRC the old code did the same SET(CMAKE_REQUIRED_LIBRARIES…) as it is now (ie. direct passthrough of whatever PKG_CHECK_MODULE found for the static case), with only ssh2 as the first argument (lest CMake fails anyway, with Looking for libssh2_userauth_publickey_frommemory in ssh2;ssl;crypto;z - not found)

For reference, here's the implementation of CHECK_LIBRARY_EXISTS. As you can see, it will stringify whatever is passed as the LOCATION argument, which will output as a -L"${STATIC_LIBDIRS}", erroring because of file not found (which is one of the issues I encountered the 1st time).

  • Passing STATIC_LIBDIRS in CMAKE_REQUIRED_LIBRARIES will result in :
-- Looking for libssh2_userauth_publickey_frommemory in ssh2
WARNING: Target "cmTC_8106a" requests linking to directory "/usr/local/Cellar/openssl/1.0.2o_1/lib".  Targets may link only to libraries.  CMake is dropping the item.
WAR
8000
NING: Target "cmTC_8106a" requests linking to directory "/usr/local/Cellar/libssh2/1.8.0/lib".  Targets may link only to libraries.  CMake is dropping the item.
WARNING: Target "cmTC_8106a" requests linking to directory "/usr/local/Cellar/openssl/1.0.2o_1/lib".  Targets may link only to libraries.  CMake is dropping the item.
WARNING: Target "cmTC_8106a" requests linking to directory "/usr/local/Cellar/libssh2/1.8.0/lib".  Targets may link only to libraries.  CMake is dropping the item.
-- Looking for libssh2_userauth_publickey_frommemory in ssh2 - not found

Everything results int the same underlying error (ld: library not found for -lssh2). What's maddening is that the last few arguments passed to ld are -L/usr/local/Cellar/openssl/1.0.2o_1/lib -Wl,-rpath,/usr/local/Cellar/openssl/1.0.2o_1/lib -lssh2 -lssh2 -lssl -lcrypto -lz so it sees the openssl libdir coming from LOCATION, but it only processes the first item (the CMake "bug").

There are a bunch of alternatives :

  • (OCGit) Make cmake forget it's running in an Xcode build environment (that -isysroot is IMHO the curx of the issue). That would be the cleanest IMHO, but I haven't been able to find what causes it to grab this environment.
  • (OCGit) Build a Mac version of openssl/libssh2 and link against that instead of depending on Homebrew (which is a bad idea anyway because it only builds for $LATEST_OSX and the linker warns that 1) it's bad 2) it will become an error Soon™). Cons are Travis build times.
  • (libgit2) Paper over the issue here.

@tiennou
Copy link
Contributor Author
tiennou commented Apr 26, 2018

Took another stab at it. I got rid of the argument building with some stringification. I've also moved the policy check to where it's relevant (CMP0056 is CMake 3.2+), so I can use it if available, and revert to the "next best thing" if not.

I don't think it can be fixed without CMP0056 : a static link of libssh2 using OpenSSL as its backend will always fail, because of the truncated LIBDIRS (thus this should impact static on Linux and friends).

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

I just tried to reproduce the issue on Linux, but I'm unable to do so. I've changed all the ${LIBSSH2_*} variables to instead use ${LIBSSH2_STATIC_*} and instructed CMake to find only static libraries via SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a"). CMake now picks up only static archives, but CHECK_LIBRARY_EXISTS is able to identify the function correctly without any further changes:

Looking for libssh2_userauth_publickey_frommemory in ssh2;ssl;dl;z;crypto;dl;z - found

So it does even work when passing in a list of static libraries, which is unexpected. And I didn't have to set/unset CMAKE_REQUIRED_LIBRARIES . In fact, it shouldn't make any difference at all whether we're passing in the libraries via the library parameter of CMAKE_REQUIRED_LIBRARIES:

set(CHECK_LIBRARY_EXISTS_LIBRARIES ${LIBRARY})
if(CMAKE_REQUIRED_LIBRARIES)
  set(CHECK_LIBRARY_EXISTS_LIBRARIES
       ${CHECK_LIBRARY_EXISTS_LIBRARIES} ${CMAKE_REQUIRED_LIBRARIES})
endif()

So they're getting concatenated in the CheckLibraryExists module anyway. So let's just scrap that and pass in ${LIBSSH2_LIBRARIES} again.

So why doesn't this still work... your workaround will pass the linker flags directly to the compiler. So the reason has to be that the compiler is still unable to correctly locate the library. We're already passing in ${LIBSSH2_LIBRARIES} in a correct way, so I doubt that's the issue. As I'm not seeing the issue on Linux, where the library is in one of the standard link directories, but you're seeing it with Homebrew where it's not, I strongly suspect that we're mishandling the link directory.

So we're passing in the location of the library via ${LIBSSH2_STATIC_LIBRARY_DIRS} here, which we got directly from pkg-config. In your case, it has a format like "/usr/local/Cellar/openssl/1.0.2o_1/lib;/usr/local/Cellar/libssh2/1.8.0/lib". CMake passes it directly to try_compile via -DLINK_DIRECTORIES:STRING=${LOCATION}, which results in a line link_directories(${LINK_DIRECTORIES}) in the generated CMake file. The thing is though that link_directories wants to have a space-separated list of directories, not a semicolon-separated list. So I bet you're ending up with a flag -L/usr/local/Cellar/openssl/1.0.2o_1/lib;/usr/local/Cellar/libssh2/1.8.0/lib, which is plain wrong.

This should be rather easy to fix, I hope:

STRING(REPLACE ";" " " LIBRARY_DIRS "${LIBSSH2_STATIC_LIBRARY_DIRS}")
CHECK_LIBRARY_EXISTS("${LIBSSH2_STATIC_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
UNSET(LIBRARY_DIRS)

This is all an (educated) shot in the dark. Xcode is installing in the background in case this does not solve your issue.

@tiennou
Copy link
Contributor Author
tiennou commented Apr 27, 2018

The difference is caused by the apparition of -isysroot in the compiler/linker arguments, pointing to the local SDK. I doubt -isysroot is useful enough on Linux that you would be able to reproduce its "environment", but the crux of the issue is that we're not re-adding / afterward…

Your reasoning about what is happening try_compile-wise is the same as mine.

CMakeError.log of your shot in the dark :

Determining if the function libssh2_userauth_publickey_frommemory exists in the ssh2;ssl;crypto;z failed with the following output:
Change Dir: /Users/tiennou/Projects/objective-git/External/libgit2/build/CMakeFiles/CMakeTmp

Run Build Command:"/usr/local/bin/gmake" "cmTC_1ff21/fast"
/usr/local/bin/gmake -f CMakeFiles/cmTC_1ff21.dir/build.make CMakeFiles/cmTC_1ff21.dir/build
gmake[1]: Entering directory '/Users/tiennou/Projects/objective-git/External/libgit2/build/CMakeFiles/CMakeTmp'
Building C object CMakeFiles/cmTC_1ff21.dir/CheckFunctionExists.c.o
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc   -D_GNU_SOURCE  -Wall -Wextra -Wdocumentation -Wno-missing-field-initializers -Wstrict-aliasing -Wstrict-prototypes -Wdeclaration-after-statement -Wshift-count-overflow -Wno-unused-const-variable -Wno-unused-function -Wno-deprecated-declarations -DCHECK_FUNCTION_EXISTS=libssh2_userauth_publickey_frommemory -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.8   -o CMakeFiles/cmTC_1ff21.dir/CheckFunctionExists.c.o   -c /usr/local/Cellar/cmake/3.11.1/share/cmake/Modules/CheckFunctionExists.c
Linking C executable cmTC_1ff21
/usr/local/Cellar/cmake/3.11.1/bin/cmake -E cmake_link_script CMakeFiles/cmTC_1ff21.dir/link.txt --verbose=1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -D_GNU_SOURCE  -Wall -Wextra -Wdocumentation -Wno-missing-field-initializers -Wstrict-aliasing -Wstrict-prototypes -Wdeclaration-after-statement -Wshift-count-overflow -Wno-unused-const-variable -Wno-unused-function -Wno-deprecated-declarations -DCHECK_FUNCTION_EXISTS=libssh2_userauth_publickey_frommemory -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -mmacosx-version-min=10.8 -Wl,-search_paths_first -Wl,-headerpad_max_install_names   CMakeFiles/cmTC_1ff21.dir/CheckFunctionExists.c.o  -o cmTC_1ff21  -L"/usr/local/Cellar/openssl/1.0.2o_1/lib /usr/local/Cellar/libssh2/1.8.0/lib" -Wl,-rpath,"/usr/local/Cellar/openssl/1.0.2o_1/lib /usr/local/Cellar/libssh2/1.8.0/lib" -lssh2 -lssl -lcrypto -lz 
ld: warning: directory not found for option '-L/usr/local/Cellar/openssl/1.0.2o_1/lib /usr/local/Cellar/libssh2/1.8.0/lib'
ld: library not found for -lssh2
clang: error: linker command failed with exit code 1 (use -v to see invocation)
gmake[1]: *** [CMakeFiles/cmTC_1ff21.dir/build.make:87: cmTC_1ff21] Error 1
gmake[1]: Leaving directory '/Users/tiennou/Projects/objective-git/External/libgit2/build/CMakeFiles/CMakeTmp'
gmake: *** [Makefile:126: cmTC_1ff21/fast] Error 2

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

Okay, so my analysis (ignoring -isysroot) is in fact correct. So one of actual issues is that CMake's try_compile cannot handle multiple link directories

@tiennou
Copy link
Contributor Author
tiennou commented Apr 27, 2018

Yes, at least using the obvious ways.

As for your analysis, it might be possible to cause Linux to fail by doing source-installs of dependencies in non-standard, separate locations (like the /usr/opt/local/$dep brew does).

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

Could you please give my branch at https://github.com/pks-t/libgit2/tree/pks/cmake-resolve-pkgconfig a go? The fix in there needs to be merged anyway, disregarding if it fixes your issue or not. We are currently plain mishandling libraries found by pkg-config -- we have to resolve them against the found library dirs instead of only resolving them at the end, when linking libgit2.

@tiennou
Copy link
Contributor Author
tiennou commented Apr 27, 2018

Running that branch against my OCGit buildsys change gets me an SSH-enabled macOS framework, but a SSH-missing iOS one (here's the CMakeError.log relevant excerpt for one of the archs).

@tiennou
Copy link
Contributor Author
tiennou commented Apr 27, 2018

And doing the ${PKG}_STATIC_${VAR} dance when building static seems to fix the iOS build, so yay !

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

Okay, that's good news, thanks for testing. I'll open a PR for that.

I wonder about that static-dance, though. I seem to recall that @carlosmn told me once that -DBUILD_SHARED_LIBS=OFF should not imply us linking against static libs ourselves. I wonder whether that's something that needs to be set up in the build environment. I don't know if CMake itself has any ability to get told to search for static libs. Otherwise, we should maybe add a new build flag...?

@tiennou
Copy link
Contributor Author
tiennou commented Apr 27, 2018

As per FindPkgConfig, it unconditionally does both checks.

So maybe the fix is to try to use the "dynamic" variables first, and fallback to _STATIC_ ones if those are not set — meaning we've only found a static only version of the "module" ?

If that looks correct, I say the best way would be to DRY all those LIST calls via a LIBGIT2_ADD_DEP(MODULE_NAME) which hides that check and perform the list management ?

@carlosmn
Copy link
Member

I seem to recall that @carlosmn told me once that -DBUILD_SHARED_LIBS=OFF should not imply us linking against static libs ourselves

As a library, we don't know what's supposed to happen around us. It's the final binary which defines what should get linked together. There might be other dependencies that want to use libssh2 or whatever else, and we don't want to conflict. The application might also only want to link libgit2 statically but link to everything else dynamically, so they can ship their build (or whatever) artifact around but still make use of their system's update mechanisms for more general or stable dependencies.

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

But we'd have to provide some way to the user of libgit2 to have libgit2 use either static or dynamic dependencies. Otherwise, we'll just always link libgit2.so against the found set of shared libraries. Or is there a mechanism here that I am not thinking of?

@carlosmn
Copy link
Member

Hm... so we're trying to build libgit2 dynamically so the app can link dynamically against libgit2, but then have our dependencies be static?

I don't think I know of a pattern for doing that but it ends up having the same issues as if we were static. The rest of the code could be trying to link against the same libraries again leading again to symbol conflicts.

Admittedly we kinda do that with our vendored libraries but we only use them (by default) if we don't find them on the system.

This seems to be pretty specific for 🍎 ☎️ so I think overall it might be more elegant for the project dealing with that to create an intermediate .so that links together all this code. Then again, the whole vendor/ directory shows we're not against people statically linking stuff into libgit2 if they claim to know what they're doing, so I suppose it's OK for us to include such an option which defaults to off for our dependencies.

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

I'm more interested in the other case. Let's say we are building libgit2 as static only, and the system has both dynamic and static libs for all dependencies. Our build system will only ever pick up the dynamic dependencies in this case. As we're only generating a static archive, we won't link those dynamic dependencies in, but only write them into the libgit2.pc file.

This is the problem, though: the user of libgit2 is now unable to easily do a completely static build. When using dynamic deps, we only put in the immediate library dependencies into the pkgconfig. If the users wants to do a static link, he'd also need to link against transitive dependencies.

Okay, that now just made clear to me that the real culprit is us not setting up the pkg-config file correctly. We'd also have to add a Requires.private variable that lists tranisitve dependencies.

So the other problem that is kind of unrelated is how we determine symbol availability in case there are no dynamic archives. This seems to be the case for iOS builds of Objective-Git, where we need to link against a specific libssh2. Right now, we just use LIBSSH2_LIBRARIES, ignoring LIBSSH2_STATIC_LIBRARIES, and thus fail to see that libssh2_userauth_publickey_frommemory would in fact be available if we used that static lib. So should we just fall back to the static one in case no dynamic deps exist?

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

The funny thing is here that we cannot even distinguish between transitive and non-transitive dependencies via CMake. It will only set a <PKG>_LIBRARIES variable containing all required libs (without transitive deps in the dynamic case, with transitive deps in the static case).

@pks-t
Copy link
Member
pks-t commented Apr 27, 2018

I amended my PR to do what @tiennou proposed: by default, we're trying to use <PKG>_LIBRARIES now and fall back to <PKG>_STATIC_LIBRARIES in case the first one is unset. I've also extracted into a module. tiennou, could you please give my PR another try and see if it also fixes the iOS build now?

@tiennou
Copy link
Contributor Author
tiennou commented May 3, 2018

I gave a spin to the changes. The macOS build is ok, but the iOS builds failed :

CMake output :

-- Checking for module 'libssh2'
--   Found libssh2, version 1.7.0_DEV
--   Resolved libraries: /Users/tiennou/Projects/objective-git/External/build/lib/libssh2.a
-- Looking for libssh2_userauth_publickey_frommemory in /Users/tiennou/Projects/objective-git/External/build/lib/libssh2.a
-- Looking for libssh2_userauth_publickey_frommemory in /Users/tiennou/Projects/objective-git/External/build/lib/libssh2.a - not found

CMakeError.log :

Determining if the function libssh2_userauth_publickey_frommemory exists in the /Users/tiennou/Projects/objective-git/External/build/lib/libssh2.a failed with the following output:
Change Dir: /Users/tiennou/Projects/objective-git/External/build/iphoneos10.2-armv7.sdk/libgit2/CMakeFiles/CMakeTmp

Run Build Command:"/usr/local/bin/gmake" "cmTC_68d93/fast"
/usr/local/bin/gmake -f CMakeFiles/cmTC_68d93.dir/build.make CMakeFiles/cmTC_68d93.dir/build
gmake[1]: Entering directory '/Users/tiennou/Projects/objective-git/External/build/iphoneos10.2-armv7.sdk/libgit2/CMakeFiles/CMakeTmp'
Building C object CMakeFiles/cmTC_68d93.dir/CheckFunctionExists.c.o
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc   -D_GNU_SOURCE -fembed-bitcode -Wall -Wextra -Wdocumentation -Wno-missing-field-initializers -Wstrict-aliasing -Wstrict-prototypes -Wdeclaration-after-statement -Wshift-count-overflow -Wno-unused-const-variable -Wno-unused-function -Wno-deprecated-declarations -DCHECK_FUNCTION_EXISTS=libssh2_userauth_publickey_frommemory -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk   -o CMakeFiles/cmTC_68d93.dir/CheckFunctionExists.c.o   -c /usr/local/Cellar/cmake/3.11.1/share/cmake/Modules/CheckFunctionExists.c
Linking C executable cmTC_68d93
/usr/local/Cellar/cmake/3.11.1/bin/cmake -E cmake_link_script CMakeFiles/cmTC_68d93.dir/link.txt --verbose=1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -D_GNU_SOURCE -fembed-bitcode -Wall -Wextra -Wdocumentation -Wno-missing-field-initializers -Wstrict-aliasing -Wstrict-prototypes -Wdeclaration-after-statement -Wshift-count-overflow -Wno-unused-const-variable -Wno-unused-function -Wno-deprecated-declarations -DCHECK_FUNCTION_EXISTS=libssh2_userauth_publickey_frommemory -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names   CMakeFiles/cmTC_68d93.dir/CheckFunctionExists.c.o  -o cmTC_68d93 /Users/tiennou/Projects/objective-git/External/build/lib/libssh2.a /Users/tiennou/Projects/objective-git/External/build/lib/libssh2.a 
ld: warning: -headerpad_max_install_names is ignored when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES)
Undefined symbols for architecture armv7:
  "_BIO_ctrl", referenced from:
      __libssh2_pub_priv_keyfile in libssh2.a(openssl.o)
      __libssh2_pub_priv_keyfilememory in libssh2.a(openssl.o)
  "_BIO_free", referenced from:
      _read_private_key_from_memory in libssh2.a(openssl.o)
      _read_private_key_from_file in libssh2.a(openssl.o)
      __libssh2_pub_priv_keyfile in libssh2.a(openssl.o)
      __libssh2_pub_priv_keyfilememory in libssh2.a(openssl.o)
  "_BIO_new_file", referenced from:
      _read_private_key_from_file in libssh2.a(openssl.o)
      __libssh2_pub_priv_keyfile in libssh2.a(openssl.o)
  "_BIO_new_mem_buf", referenced from:
      _read_private_key_from_memory in libssh2.a(openssl.o)
[snip]

This is somewhat like the state before I added the CMAKE_REQUIRED_LIBRARIES change, apart from the missing -l flags and the absolute path to libssh2.a. What's strange is that I've tried to set CMAKE_REQ_LIB again, but it made no difference to the linker invocation 😕 .

@pks-t
Copy link
Member
pks-t commented May 4, 2018

Thanks for your feedback.

I've now finally had the ability to test on macOS. The issue here is simply that PKG_CHECK_MODULES seems to just fall back to the system-installed libssh2 library, making it impossible for me to distinguish whether I should use static or dynamic libraries.

There's two ways to go forward here:

The first way is to tell the user (objective-git) to use a workaround: simply set PKG_CONFIG=/path/to/wrapper.sh, where wrapper.sh contains:

#!/bin/sh
pkg-config --static "$@"

So we simply force pkg-config to always return static libraries. This should just work, but is simply working around an issue in our own build scripts.

So this does convince me that there should be a second way, which is a new switch called e.g. -DUSE_STATIC_DEPS, that allows the user to have us use static dependencies only. This would get passed on to FIND_PKGLIBRARIES(), which then prefers the static set of libraries returned by PKG_CHECK_MODULES. This should finally solve this issue.

@pks-t
Copy link
Member
pks-t commented May 4, 2018

Okay, so whenever I start tackling the issue of linking against static libraries in our build instructions I'm getting aware of the issues it brings along:

  • we do have to handle FIND_PACKAGE and PKG_CHECK_MODULE along with potential other ways of discovering libraries
  • we should not link libgit2.{a,so} against (external) static libraries
  • we need to make sure our libgit2.pc correctly declare these dependencies as private

In short, we just aren't ready right now to introduce static library support, it'd need some more time. I'm not opposed, but I don't want to have a half-hearted solution here, and doing it right takes time I don't have right now. That's why I went forward and pushed the previously mentioned hack of using a pkg-config wrapper to your OCGit pull request, which causes the CI to pass.

I hope we can address this shortcoming soon, though. Several people have been asking for this over the years, and we never had a real solution to this situation.

A8FE

@tiennou
Copy link
Contributor Author
tiennou commented Aug 26, 2018

I'm closing this, as the underlying iOS-specific issue has been fixed (though I haven't been able to find a "static build" issue I could #ref it from, because there's helpful context in here).

@tiennou tiennou closed this Aug 26, 2018
@tiennou tiennou deleted the fix/libssh2-ios-link branch August 26, 2018 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0