-
Notifications
You must be signed in to change notification settings - Fork 61
Make visibility feature not propagated #333
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
Conversation
This allows to enable hidden visibility on a library without affecting its dependencies, which may not (yet) support hidden visibility by default.
|
Could someone take a look, please? I would like to use this feature in Boost.Log. |
|
Sorry about the delay.. Forgot I was waiting on the CI builds. |
|
Thanks! |
|
Consider reverting this. Now that the default is hidden, this makes it hard (to impossible) to use the UB sanitizer, which fails with |
|
Also, if you have a dependency chain of the form -> lib1/shared -> lib2/static -> lib3/static, it makes no sense for the visibility to not propagate from lib1 to lib2/lib3. |
I would say then we need to make Boost.Build aware of sanitizers and make a workaround just for the case when the sanitizers are used.
No, and this was why I made this PR. There was a moment when not all libraries supported hidden visibility (Boost.Regex) and I wanted to use this feature on one of the dependent libraries (Boost.Log). So the support for visibility is a property of a given library, not something users or upper-level libraries can blindly turn on or off for their dependencies. Granted, the visibility issues were (I think) fixed since then, so it should work even if reverted. I just don't think this is the right thing to do.
I disagree. Visibility of symbols defined by lib2 and lib3 is lib2's and lib3's business. If they require everything to be globally visible then there may be a reason to do so (like maybe they rely on comparing pointers to global stuff) and lib1 has to oblige. Now, you may want to complain to lib2 and lib3 authors if you think things can be improved. |
|
I see two use cases here:
I do not like the second use case at all. Issues should be fixed in appropriate places. The more it hurts to workaround the issue, the more effort will be put by the author to fix it rather than workaround. |
|
I really don't see how one can "disagree". If you have a dependency chain like I outlined, it's completely ridiculous for the visibility to not propagate from the shared library to its static dependencies. You end up with a shared library with half of its symbols hidden and no control over them. |
|
Tell you what, if you're so insistent on non-propagation, let's add another feature that does propagate, so that you can keep using your broken one, and others can use the correct one. That way everyone would be happy. |
That's not true because the visibility of lib3's symbols (as determined by the And if you have lib3 <- lib1 -> lib2 -> lib3, you now are forced in lib1 to match whatever lib2 does with lib3, or you'll have conflicting properties. No amount of complaining to the authors of lib2 and lib3 will fix it, because it's not their fault. You have to edit lib2's Jamfile for each build. ( If the visibility of lib3's symbols was supposed to be strictly lib3's business, there would be no need for a feature. Features are by definition what users of lib3 specify. |
Not exactly. It is determined by lib3, if it wants to (i.e. if it has
No, the intention is that lib3 always has visibility that written in its Jamfile (or the default, which is hidden; no pun intended). You just don't do the visibility override for the dependency and don't match anything. If there is a way to enforce this in Boost.Build (i.e. prohibit overrides) then I'd rather do that. I'm not aware of a way to do this.
If so then I don't understand how one should use Boost.Build to implement the feature, aside from hardcoding different compiler switches in every library's Jamfile. Remember, this feature appeared to (a) provide a portable way to control visibility in libraries and (b) make hidden visibility the default. It was never meant as a user configurable option.
Keeping the spite aside, this may actually be a viable option, except that the "propagating" feature would force the global visibility everywhere despite the library preference (i.e. if |
|
We're obviously not understanding each other. Features are settable by the user of the library. It makes no sense to have a feature that the user is not supposed to set. If lib3 does nothing visibility-related, then when the user builds it with visibility=global he should get a library built with global visibility, and when he builds it with visibility=hidden he should get a library built with hidden visibility. That's how all features work. And the reason features propagate, as a rule, is to ensure consistency across the graph. When you depend on both lib1 and lib2, and lib1 depends on lib2 itself, and you want to build lib1 and lib2 with somefeature=foo, propagation ensures that the lib2 dependency of lib1 gets that same foo, otherwise you have a conflict. This is basic Boost.Build stuff so I don't see where we split ways. If the goal is for the user to be able to control independently the visibility of lib1 and lib2's symbols, then that's not possible because he has no way of overriding lib2's visibility.
That's just not what features are. But let's step back for a minute. What specific problem does propagation cause for you? What's the scenario you're trying to avoid?
I don't want a "force global visibility" feature. I want a visibility feature that propagates. If I give it global, it propagates global. If I give it hidden, it propagates hidden. If I give it protected, it propagates protected. As it should. |
Well, then I guess I hijacked the features mechanism to achieve what we wanted. I just don't know of any other mechanism in Boost.Build that allows us to do this. If there is a better way to do this then by all means, let's revert this PR and #331 and do it "the right way". I just don't want it reverted without a definitive direction for further improvement. Also, if you revert this support then please let everyone know. For example, I would need to revert changes in my libraries and restore the previous code for forcing hidden visibility with hardcoded compiler options.
As I've already described, if lib2 doesn't support hidden visibility then any lib1 that depends on it cannot be built with hidden visibility (of lib1's symbols, at least), which is nonsense. This would also mean no default hidden visibility for Boost. |
|
If lib2 doesn't support hidden visibility it puts in its requirements |
Propagation does not affect the default. |
|
Or, if lib2 is visibility-unaware, everyone has to link to it with |
IIUC, that means if lib1 has
It does, in the sense that you can't make it default to |
Also, if lib1 doesn't have |
|
Propagation does not affect the default. If the default is hidden, it will be hidden either way.
I don't think it will. Let me check. |
|
Also, if lib1 has no |
|
Works. |
|
Yes, it will be global, as inherited. The author of lib2 can override it to If you have a library lib2 depending on lib3, and neither of those does anything visibility-related, and you want to set lib3's visibility, the only way to do it is with propagation. Yes, this will set lib2's visibility as well. There's no other way. |
Since visibility is (at the moment) an optional feature, this (at present) results in it not having a value at all. |
Yes, I've just tested it. And this is a problem, as we'd have to patch every library to add
If he overrides then it ruins user-configurability, doesn't it? Just forget about overriding. Can we prohibit it?
An optional feature has a default value. "Optional" means it is not present in the build specification, but the feature is still acted upon (i.e. the relevant compiler flags are added). https://boostorg.github.io/build/manual/develop/index.html#bbv2.reference.features |
|
When I say what happens, I mean what happens, as empirically tested. :-)
As I said, it's not possible to user-configure an indirect dependency unless the direct dependency doesn't override the feature and the feature propagates. If either of these doesn't hold, you can't. So if the feature doesn't propagate, you can't. So there's no regression. You couldn't before, and you still can't. |
|
Yes, what I mean is that overriding ruins everyone's day - in your case it prevents configurability, in my case it messes up the default visibility. It's a solution to noone, so noone should ever use it. So this leaves us with having to explicitly specify |
|
I have no idea what you're talking about here. What is the specific scenario in which things don't work with propagation, but work without it? What I see at the moment is that my day is ruined without propagation, and not ruined with it. What's the counterexample? Paste it here so I can test it with/without. |
This builds and uses lib3 with |
|
And changing it to is a problem because? |
|
Because:
|
|
If only Boost.Build supported a way to specify the default value for a (non-propagared) feature. I.e. for the case in #333 (comment)
|
|
As I said, without propagation there's no configurability, so nothing is lost. The correct way to achieve global visibility for only lib2's symbols (and potentially only some of them), while letting dependencies be, is Now whatever "comes from above" flows down to lib3 unimpeded, while lib2 still enforces global visibility on the symbols it wants. |
|
We already have a way to achieve what we both want - make (The default-build will remain |
This might be difficult to implement if lib2 is built as a shared library and lib2_impl needs to export symbols. But yes, this is one way to do this.
Yes, its the two features option you mentioned before. Having the support for changing just the default of a single feature would make it much easier. Anyway, I agree the two-feature approach is the most viable solution, although I'm not sure how to implement it in Boost.Build efficiently. I'm not sure how one would derive |
|
|
|
AMDG
On 09/30/2018 10:17 AM, Peter Dimov wrote:
`local-visibility` would need to have a (real) default (named `default` for instance) and then it's
You don't need to make the two features independent.
Just define the options using <local-visibility>, and
make <visibility> a composite feature that uses <local-visibility>.
```
feature local-visibility : hidden protected global : optional ;
feature visibility : hidden protected global :
optional composite propagated ;
feature.compose <visibility>hidden : <local-visibility>hidden ;
# Same for protected and global.
```
An explicit value of <local-visibility> will prevent <visibility>
from being expanded, but will not prevent it from propagating
to dependent targets. If <local-visibility> is set in the
default-build instead, then it will be overridden by <visibility>.
I think this gives the desired behavior in all circumstances.
Note: arguably, non-propagated features specified on the command
line should behave like this automatically. We already do it for
free features like cxxflags.
```
toolset.flags gcc.compile OPTIONS <local-visibility>hidden : -fvisibility=hidden ;
toolset.flags gcc.compile OPTIONS <local-visibility>protected : -fvisibility=protected ;
toolset.flags gcc.compile OPTIONS <local-visibility>global : -fvisibility=default ;
toolset.flags gcc.compile OPTIONS <local-visibility>default/<visibility>hidden : -fvisibility=hidden ;
...
```
In Christ,
Steven Watanabe
|
|
Thanks Steven and Peter. I've created #345 and boostorg/boost#198, which should be merged in that order. |
|
Thanks Andrey. I'm not entirely sure about the superproject change. The choice affects Boost libraries linking to non-Boost ones such as f.ex. Zlib built from source. With default Not sure which one we want here. |
|
I think having the same default visibility across all targets makes more sense. I will change #345 and cancel boostorg/boost#198. |
This allows to enable hidden visibility on a library without affecting its dependencies, which may not (yet) support hidden visibility by default.