-
Notifications
You must be signed in to change notification settings - Fork 689
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
Foreign library versioning #4074
Conversation
5841300
to
2fad4d2
Compare
@ezyang The code is now ready for review. What's missing is documentation: I'll write something in Cabal/doc/developing-packages.rst. Anywhere else? Is the code documented clearly? After review I can clean up the history a little bit. Or just make it one big commit? |
You're not done until there's documentation ;) I think something nestled with the existing foreign library docs would make sense. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After having finally read up on how libtool versioning works, I'm a bit torn about adopting it wholesale for Cabal.
The good thing is that it is "standard".
The bad things... well, it's hella confusing. Here's a quip from https://autotools.io/libtool/version.html
To set the version of the library, libtool provides the -version-info parameter, which accepts three numbers, separated by colons, that are called respectively, current, revision and age. Both their name and their behaviour, nowadays, have to be considered fully arbitrary, as the explanation provided in the official documentation is confusing to say the least, and can be, in some cases, considered completely wrong.
It continues:
The main reason for which libtool uses this scheme for version information, is that it allows to have multiple version of a given library installed, with both link editor and dynamic linker choosing the latest available at build and run time. With modern distributions packaging standards, this situation should not be happening anyway.
TBH, I'm not even sure how libtool style version numbers help in this case.
More fun:
- Here is a C library maintainer getting confused by libtool's versioning scheme, and doing something very silly to fix it: https://sourceforge.net/p/dar/bugs/152/ (and some fallout on Debian https://lists.debian.org/debian-devel/2012/05/msg00378.html )
- Here is a mailing list post about how all of Ports is misusing libtool versioning (and getting X+Y major versions): https://lists.freebsd.org/pipermail/freebsd-ports/2014-May/092082.html
- Here's a project that accidentally bumped age rather than revision when they did updates: http://markmail.org/message/oo6gudzuleotubyx
- Here's an SO answer, where libtool's behavior on other non-Linux platforms is.... just not right at all! http://stackoverflow.com/questions/15215898/why-is-libtools-current-used-as-soversion-on-bsd-rather-than-major If libtool's special versioning scheme is meant as an abstraction layer to make it work on other platforms... they are doing a really bad job. (BTW, OS X behavior seems to coincide with Linux: http://lists.apple.com/archives/darwin-dev/2005/Apr/msg00063.html )
So.... I am seriously tempted to ditch libtool numbers, and just use the good, old, simple, "here is the version number that will get put in the SONAME", and tell users to update the major if they break ABI-compatibility, and minor if they add a new interface. (Honestly, we should probably have an option to compute the patchlevel automatically from the Cabal version.) As a comparison, CMake parses out major.minor from the soname and uses that on Windows: https://cmake.org/cmake/help/cmake2.6docs.html#prop_tgt:SOVERSION
But I am open to being convinced.
when (isJust (foreignLibELFVersion flib)) $ do | ||
let (Platform _ os) = hostPlatform lbi | ||
when (os /= Linux) $ die | ||
"Can't install foreign-library symlink on non-Linux OS" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it true that it should be impossible to get here (the checks during configure should catch it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I can add a comment to that effect.
#ifndef mingw32_HOST_OS | ||
-- createSymbolicLink file1 file2 creates a symbolic link | ||
-- named file2 which points to the file file1. | ||
createSymbolicLink name (dstDir </> flibBuildName lbi flib) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NB: it's name, not dst, because the symlink will be relative to the directory it's created in, i.e. distDir. Tricky!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add a comment to that effect.
(Linux, ForeignLibNativeShared) | ||
= case foreignLibELFVersion flib of | ||
Nothing -> "lib" ++ nm <.> "so" | ||
Just v -> "lib" ++ nm <.> "so" <.> show (head (versionNumbers v)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am very glad you got rid of this 'head' subsequently ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I agree, it couldn't have resulted in errors, since a user can only set v
to a non-null version.
@@ -42,16 +53,81 @@ data ForeignLib = ForeignLib { | |||
} | |||
deriving (Generic, Show, Read, Eq, Typeable, Data) | |||
|
|||
data LibVersionInfo = LibVersionInfo Int Int Int deriving (Data, Eq, Generic, Typeable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a comment (esp https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html). And although in the standard format the parameters are positional, it would be far better if we had some accessors here to make it clear.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will refer to libtool more clearly here.
I can also add these accessors. There is also libVersionInfoCRA
which gives you a tuple of the three numbers, but I can see how that is not sufficient and/or clear enough.
@@ -878,20 +878,20 @@ flibTargetName lbi flib = | |||
platformOS :: Platform -> OS | |||
platformOS (Platform _arch os) = os | |||
|
|||
-- If a foreign lib foo has elf-version 3.2.1, it should be built | |||
-- If a foreign lib foo has lib-version-info 5:1:2, it should be built | |||
-- as libfoo.so.3.2.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This comment is pretty confusing if you're not fluent in libtool's versioning scheme)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add [an appropriate reference to] an explanation.
@ezyang Thanks for your review.
Well, the advantage of version-info over version numbers is that it is more likely we will be able to parse version-info into the data required for non-Linux OSes. For example, we can't reliably deduce an age from major.minor.build (as people will just be randomly bumping version numbers). But it is important that we know both "current" and "current-age" on OSX. So while I agree with your worries about version-info, if there is any potential future wish for OSX support, this is the more general solution. I can add documentation on how to set these values, if that'd help (basically repeating the three golden rules of version-info).
No! ABI versions are NOT equal to package versions, and this is already an important distinction made in C. This does cause problems. And besides, in my use case this is absolutely undesirable. |
Even the revision level? According to the libtool guidance, you are supposed to bump revision whenever there is a source code change. I guess, maybe not if the C library didn't change (even though the hs library did)? |
(Also as discussed on IRC:) |
We need to build the library under a different name to set the SONAME, and can't set it directly, since GHC overrides -soname linker options.
When a foreign-library has a version, during installation, we need to install appropriate symlinks. This is very platform-dependent.
2fad4d2
to
06a36ea
Compare
Libtool sets library version data by the -version-info flag, which takes current[:revision[:age]] data. This is then parsed into a major.minor.build version number. We copy this approach so that library versioning may be generalised to other operating systems than Linux.
06a36ea
to
c2a20f4
Compare
This looks reasonable. |
4b071dc
to
bdbd834
Compare
I wrote some documentation. So this work now seems ready. Final feedback welcome. |
Ship it! |
See #4052