{"action":"created","issue":{"url":"https://api.github.com/repos/arvidn/libtorrent/issues/44","labels_url":"https://api.github.com/repos/arvidn/libtorrent/issues/44/labels{/name}","comments_url":"https://api.github.com/repos/arvidn/libtorrent/issues/44/comments","events_url":"https://api.github.com/repos/arvidn/libtorrent/issues/44/events","html_url":"https://github.com/arvidn/libtorrent/pull/44","id":96131085,"number":44,"title":"Build shared with (hidden) Boost.Asio sources, static without.","user":{"login":"jpetso","id":88121,"avatar_url":"https://avatars.githubusercontent.com/u/88121?v=3","gravatar_id":"","url":"https://api.github.com/users/jpetso","html_url":"https://github.com/jpetso","followers_url":"https://api.github.com/users/jpetso/followers","following_url":"https://api.github.com/users/jpetso/following{/other_user}","gists_url":"https://api.github.com/users/jpetso/gists{/gist_id}","starred_url":"https://api.github.com/users/jpetso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpetso/subscriptions","organizations_url":"https://api.github.com/users/jpetso/orgs","repos_url":"https://api.github.com/users/jpetso/repos","events_url":"https://api.github.com/users/jpetso/events{/privacy}","received_events_url":"https://api.github.com/users/jpetso/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-07-20T18:43:57Z","updated_at":"2015-07-25T18:55:11Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/arvidn/libtorrent/pulls/44","html_url":"https://github.com/arvidn/libtorrent/pull/44","diff_url":"https://github.com/arvidn/libtorrent/pull/44.diff","patch_url":"https://github.com/arvidn/libtorrent/pull/44.patch"},"body":"Fixes #38. (See that issue for the detailed background explanation and analysis.)\r\n\r\nAlways set BOOST_ASIO_SEPARATE_COMPILATION.\r\n\r\nIn shared library builds, visibility hides the symbols and\r\nthe library user doesn't have any obligations or restrictions\r\nregarding their use of Asio. This minimizes ABI issues and\r\nsymbol clashes. (BOOST_ASIO_DYN_LINK is never used.)\r\n\r\nIn static library builds, the library user is expected to include\r\nAsio sources. Previously, if a user wanted to use Asio outside of\r\nlibtorrent, she would have to use the implementation embedded in\r\nlibtorrent, adding complexity to dependency management scenarios.\r\nNow it can be (...must be) linked in separately. There is no\r\ndownside to this since the static library user would have to\r\nensure matching Boost versions anyway."},"comment":{"url":"https://api.github.com/repos/arvidn/libtorrent/issues/comments/124872421","html_url":"https://github.com/arvidn/libtorrent/pull/44#issuecomment-124872421","issue_url":"https://api.github.com/repos/arvidn/libtorrent/issues/44","id":124872421,"user":{"login":"jpetso","id":88121,"avatar_url":"https://avatars.githubusercontent.com/u/88121?v=3","gravatar_id":"","url":"https://api.github.com/users/jpetso","html_url":"https://github.com/jpetso","followers_url":"https://api.github.com/users/jpetso/followers","following_url":"https://api.github.com/users/jpetso/following{/other_user}","gists_url":"https://api.github.com/users/jpetso/gists{/gist_id}","starred_url":"https://api.github.com/users/jpetso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpetso/subscriptions","organizations_url":"https://api.github.com/users/jpetso/orgs","repos_url":"https://api.github.com/users/jpetso/repos","events_url":"https://api.github.com/users/jpetso/events{/privacy}","received_events_url":"https://api.github.com/users/jpetso/received_events","type":"User","site_admin":false},"created_at":"2015-07-25T18:55:11Z","updated_at":"2015-07-25T18:55:11Z","body":"> I'm not a big fan of having to alter users of the library. Would you mind explaining why the user has to be the one linking against the asio source?\r\n\r\nThe short answer is that by including the asio source in a static libtorrent, you make life harder for library users instead of easier. It makes it nigh impossible to make clean dependency trees for projects with several asio-based libraries. Let me use dependency trees with exe at the top and libraries as leaves to demonstrate this point.\r\n\r\nHere is the one kind of project that minimally benefits from you including asio_src into libtorrent itself:\r\n```\r\nmyprog (might or might not use asio)\r\n|- libtorrent (includes asio_src)\r\n```\r\n\r\nImagine a slightly more complex project, initially without libtorrent:\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP)\r\n|- libcommon_stuff (depends on asio::io_service)\r\n```\r\n\r\nWhat I do for a static build is I follow the asio build guidelines and build libcommunication as well as libcommon_stuff with BOOST_ASIO_SEPARATE_COMPILATION, then link in the asio source through my parent project. This way, the two libraries don't have to coordinate who's providing the asio source, and can remain independent from each other:\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP, gets -lasio_src added by myprog build script)\r\n|- libcommon_stuff (depends on asio::io_service, gets -lasio_src added by myprog build script)\r\n|- libasio_src (provides asio source for both of the above)\r\n```\r\n\r\nNow add libtorrent to the mix, because I decided my app is going to have a DHT or also be a Bittorrent client in some way.\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP)\r\n|- libcommon_stuff (depends on asio::io_service)\r\n|- libmydht\r\n   |- libtorrent (provides asio source)\r\n```\r\n\r\nIn this situation, I had to remove my existing asio_src powering libcommunication and libcommon_stuff. Instead, I have to specify libmydht/libtorrent as linker flag for libcommunication and libcommon_stuff (I have to do this because otherwise it might put the `-lfoo` flags in the wrong order). In effect, I now made libtorrent a dependency of my other two libraries which have nothing to do with libtorrent. That's an ugly hack.\r\n\r\nNow let's see what would happen if every library was like libtorrent and included its asio source in static libraries:\r\n```\r\nmyprog\r\n|- libcommunication (provides asio source)\r\n|- libcommon_stuff (provides asio source)\r\n|- libmydht\r\n   |- libtorrent (provides asio source)\r\n```\r\n\r\nSymbol clashes all over the place. I have to apply patches to all of these libraries so they stop linking in the asio source, just so I can even use them together. This is worse if they're open source projects, because then I have to make my point to the maintainer instead of just removing it from a private repository. (See: this pull request.)\r\n\r\nEven assuming I only use a single library with embedded asio sources (libtorrent), but I want my libtorrent-based functionality to be conditional on an optional build flag:\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP)\r\n|- libcommon_stuff (depends on asio::io_service)\r\n|- libmydht (build if with-mydht=true)\r\n   |- libtorrent (provides asio source)\r\n|- libasio_src (build if with-mydht=false)\r\n```\r\nThis complicates my build script. Now assume I also have a build option for using either static or shared libraries:\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP, use -lmydht if that is built or -lasio_src otherwise)\r\n|- libcommon_stuff (depends on asio::io_service, use -lmydht if that is built or -lasio_src otherwise)\r\n|- libmydht (build if with-mydht=true)\r\n   |- libtorrent (provides asio source)\r\n|- libasio_src (build if with-mydht=false, OR if (use-shared-libs=true and libtorrent hides its asio symbols))\r\n```\r\n\r\nSo yeah, you could resolve some of the problems by saying that libtorrent always exports all asio symbols. That's sort of what you do now, except you make asio_ssl.cpp conditional on whether or not libtorrent is built with OpenSSL enabled or not. How about we build libtorrent without OpenSSL, because I only use the DHT anyway:\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP, also uses SSL features)\r\n|- libcommon_stuff (depends on asio::io_service)\r\n|- libmydht (build if with-mydht=true)\r\n   |- libtorrent (provides asio source... but encryption=false, so no asio_ssl linked in)\r\n|- libasio_src (provides asio source with SSL, build if with-mydht=false)\r\n```\r\nWell, bad luck for me? I guess I now have to build my own libasio_src but without asio.cpp and *only* asio_ssl.cpp, because libtorrent left that out while including the other one.\r\n\r\nSo you see there's a lot of stuff to consider. Some of these cases might be solvable with an ugly build script, others are impossible without patching libtorrent. Wouldn't it be nice if I didn't have to consider all of these options and there was one that worked for every combination listed above, static or shared, libtorrent enabled or not:\r\n```\r\nmyprog\r\n|- libcommunication (depends on asio for TCP/UDP, also uses SSL features)\r\n|- libcommon_stuff (depends on asio::io_service)\r\n|- libmydht (build if with-mydht=true)\r\n   |- libtorrent (depends on various asio features)\r\n|- libasio_src (always provides asio source as required by the sum of all libraries)\r\n```\r\n\r\nThis is easy for the user to do: `#include <boost/asio/(ssl/)impl/src.hpp>`, build with top-level target, done. No worrying about implementation details of this library's build system or that library's conditional options. If you use asio statically, the top-level target (executable or shared object) is responsible for including the asio source, everybody else relies on that. This is how asio (and boost in general) designed its SEPARATE_COMPILATION and DYN_LINK flags. Specifying those means that you defer to the top-level target, and all libraries can play well together with a simple build script.\r\n\r\nI considered adding an option `asio_source={ON,OFF}` to libtorrent, and that works around the problem by giving me an additional option. However, this only gives people more stuff to break their heads on, when there's a single option that works well for all cases:\r\n* If you use static libtorrent, it requires the asio source to be linked in.\r\n* If you use shared libtorrent, you can do whatever the frick you want, because hidden visibility.\r\n\r\nSince I use shared libraries as provided by my target system/distribution, I shouldn't have to care about anything except libtorrent's public API. You might not have eliminated all of the asio types from the public API yet, but I expect that the current situation with propagating build flags is not the way forward, and that you intend to fix this so libtorrent can provide an API that's actually stable.\r\n\r\nAsio can't be part of that API because it's ABI-unstable by the maintainer's definition. The memory layout of basic types like address, endpoint or io_service might be stable, but the API on the whole is not. My patch is a precondition to get rid of asio as a public API... and in the process, it makes life simpler for users of libtorrent because they can choose to go with a single option (always build asio_src as part of the top-level project) for any libtorrent build.\r\n\r\nFor static libraries, there's no way around making the user aware of asio symbols. You've got the option of trying to solve this issue in libtorrent, by taking specific build flags after educating the library user about why libtorrent does things differently than boost recommends, by making asio API dependent on libtorrent build flags. Or you can avoid another build option and state a simple rule, as mentioned in the bullet points above.\r\n\r\nBoost.Build might provide usage requirements, but you have to realize that these are not available for (a) people who use your shared library as installed by their system/distribution, and (b) people who don't want to convert all of their other code and dependent libraries to b2 and thus use something like CMake find macros instead. For those people, having a single way of building things is genuinely valuable.\r\n\r\nI hope that some of the above points resonate with you, and that the issues with the current state of things are sufficiently explained. If not, then I'll have to maintain a permanent patch / branch / GitHub fork of libtorrent because I'm not willing to to hack my build system with ugly conditionals."}}