View Issue Details Jump to Notes ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0004383CMakeCMakepublic2007-01-31 13:212008-07-21 09:13
ReporterAlex Neundorf 
Assigned ToBrad King 
PrioritylowSeverityfeatureReproducibilityalways
StatusclosedResolutionfixed 
PlatformOSOS Version
Product Version 
Target VersionFixed in Version 
Summary0004383: dylib_compatibility_version and dylib_current_version not set
DescriptionHi,

when setting the VERSION and the SOVERSION property of a library target on OS X, also the compatibility and current version of the dylib should be set. Right now both are always 0 when built with cmake.
The options for ld are -dylib_compatibility_version and -dylib_current_version .
TagsNo tags attached.
Attached Files

 Relationships

  Notes
(0006820)
Brad King (manager)
2007-03-16 12:56

I'm not sure if there is a clear mapping from these properties to the correct values for these options. Eventaully I plan to add new versioning properties based on the libtool design. Perhaps something like

SET_TARGET_PROPERTIES(mysharedlib PROPERTIES
  VERSION_CURRENT 2
  VERSION_AGE 1
  VERSION_REVISION 1
  )

Then libtool-like behavior will be implemented on all platforms using these numbers.
(0006870)
Alex Neundorf (developer)
2007-03-20 18:04

libtool like behaviour ?
Like "-version-info 6:0:2" produces libkdecore.4.2.0 ?
Please don't do that, IMO that's absolutely un-understandable.

Maybe the mapping from VERSION and SOVERSION to the dylib versions isn't completely correct, but I think it should work good enough for most people.

Alex
(0009553)
Brad King (manager)
2007-10-25 10:05

CMake defines two properties for a target: VERSION and SOVERSION. The VERSION is the *implementation* version. The SOVERSION is the *interface* version.

On linux this maps to a file and two symlinks:

libfoo.so.<VERSION>
libfoo.so.<SOVERSION> -> libfoo.so.<VERSION>
libfoo.so -> libfoo.so.<SOVERSION>

The "soname" of the library is set to "libfoo.so.<SOVERSION>" so that the dynamic linker looks for this file.

Currently this is done on OSX too and creates these files:

libfoo.<VERSION>.dylib
libfoo.<SOVERSION>.dylib -> libfoo.<VERSION>.dylib
libfoo.dylib -> libfoo.<SOVERSION>.dylib

The filename component of the "install_name" is set to "libfoo.<SOVERSION>.dylib" to ensure that interface version is found at runtime. This is the behavior of libraries found in /usr/lib on OSX.

The purpose of this bug entry is to determine the proper values to pass to the -dylib_compatibility_version and -dylib_current_version flags. It looks from the libraries in /usr/lib that the correct mapping is SOVERSION->dylib_compatibility_version and VERSION->dylib_current_version.
(0009554)
Brad King (manager)
2007-10-25 10:38

The libtool versioning scheme is documented here:

  http://www.gnu.org/software/libtool/manual.html#Libtool-versioning [^]

On Linux the versions are converted to library names as follows:

  major = current - age
  minor = age
  patch = revision

libfoo.so.$major.$minor.$patch is the library
libfoo.so.$major is the soname and symlink name
libfoo.so is the linker name which is a symlink pointing at the soname
          matching the currently installed header files...therefore in
          a given prefix the lib/libfoo.so symlink should match the API
          provided in include/foo.h

Other encoding schemes can be seen in the /usr/bin/libtool shell script.

It is specified that the dynamic linker always chooses the latest version of a library from those providing a given interface number. The selection of the latest version from multiple libraries providing the same soname is handled by ldconfig:

  glibc-2.3.5/elf/ldconfig.c

The latest version is selected by the _dl_cache_libcmp function in

  glibc-2.3.5/sysdeps/generic/dl-cache.c

The ldconfig tool looks for all available libraries in a given directory. It then extracts the soname out of each library and creates a symlink named using the soname and pointing at the library file.

This scheme works under the assumption that once a binary has been built the library will only be upgraded and never downgraded. For example, if the current and age numbers are incremented because a function is added, the soname on linux does not actually change. If an executable is built on a machine with the newer interface and then distributed to a machine with the older version it will break. I have not been able to find any documentation on the web explaining this flaw.
(0009555)
Brad King (manager)
2007-10-25 10:56

My previous statement about the behavior of libraries in /usr/lib was incorrect. Here is what actually happens (derived from libtool script and actual libraries in /usr/lib with non-trivial version numbers).

On Darwin (Mac OSX) the versions are encoded like this:

  major = current - age
  minor = age
  patch = revision
  minor_current = current + 1

libfoo.$major.$minor.$patch.dylib is the library
libfoo.$major.dylib is the soname and symlink name
libfoo.dylib is the linker name which is a symlink pointing at the soname
             matching the currently installed header files

The binary version numbers of the library are set with the linker flags

  -compatibility_version ${minor_current}
  -current_version ${minor_current}.$patch
(0009556)
Brad King (manager)
2007-10-25 11:12

If the user has set VERSION to $major.$minor.$patch and SOVERSION to $major then we can compute the correct values for compatibility_version and current_version. They are

  compatibility_version = (major+minor+1).0.0
  current_version = (major+minor+1).patch.0

However I don't think this will commonly be done correctly and will surprise users. Other platforms use different encodings of the "current[:revision[:age]]" libtool version info. Therefore the VERSION and SOVERSION interface provided by CMake is wrong (which is my fault for creating them based on incorrect information originally).

The fix is to add the interface I proposed above with VERSION_CURRENT, VERSION_REVISION, and VERSION_AGE properties. Then the correct version computation can be done everywhere, and it will be compatible with old code because the previous behavior will be preserved when VERSION and SOVERSION are set. Perhaps another property can be added to decide whether the version number portion of the .dylib file name should be added (and never add it for frameworks).
(0009557)
Brad King (manager)
2007-10-25 11:57

The fix to the flaw I mentioned at the bottom of the comment at 10:38 this morning is to use an approach like IBM's:

http://www.ibm.com/developerworks/linux/library/l-shlibs.html [^]

There must be a symbolic link corresponding to every interface version number supported by an implementation. In the libtool on Linux world this transforms to the file

  libfoo.so.(current-age).(age).(revision)

which is the library implementation containing the soname "libfoo.so.(current)". There must also be the following symlinks to the file:

  libfoo.so.(current)
  libfoo.so.(current-1)
  ...
  libfoo.so.(current-age+1)
  libfoo.so.(current-age)

corresponding to each supported interface number's soname. This way any application originally linked using any of these soname-s will be able to load. The currently installed header files must present the "current" interface version number and the linkable symlink must be

  libfoo.so -> libfoo.so.(current)

since anyone that links to the "libfoo.so.(current-age).(age).(revision)" file will get an soname of "libfoo.so.(current)".
(0009558)
Alex Neundorf (developer)
2007-10-25 12:43

Brad,

I knew this calculation and I don't think it makes sense:
major = current - age
minor = age
patch = revision

We specify 3 variables (current, age, revision), and we need 3 variables (major, minor, patch), but instead of just using the values the user gives some IMO useless calculation is done (major = current -age). Even with this calculation the user can set up values however he wants to.

Currently with cmake we have 4 variables (VERSION with x.y.z and SOVERSION) which is IMO enough information to encode everything what's needed.

It would also be nice to stay with VERSION and SOVERSION (and maybe one more for the dylib stuff), so they don't become deprecated and some new things have to be used (which would be compatible since it's only properties but still it's something new for the user to learn).

Alex
(0009561)
Brad King (manager)
2007-10-25 12:56

The calculation is done this way because other platforms (irix, sun, etc.) have different rules for selecting the library version to link at runtime. Different calculations are done on those platforms. The current:revision:age breakdown is cross-platform in this sense. It may be confusing but once understood it produces the right thing. I think it is important to provide an interface that allows users that know what they are doing to do it right.

We cannot use the old VERSION and SOVERSION values to get this information without breaking compatibility. Another option is to look for a VERSION that is colon-separated and enable the new behavior by parsing current:revision:age out of it and ignoring SOVERSION (warning if it is set).

Of course we cannot just take the libtool encoding rules on each platform directly due to the flaw on Linux I mention above. I'm going to read through the documentation on each platform and design a proper encoding scheme.
(0009569)
Brad King (manager)
2007-10-25 17:58

After digging through more documentation and a bit of trial-and-error I now undertand OSX versioning a bit better. From the OSX libtool manpage:

  -current_version number
    For dynamic shared library files this specifies the current ver-
    sion number of the library. The program using the library can
    obtain the current version of the library programmatically to
    determine exactly which version of the library it is using. The
    format of number is X[.Y[.Z]] where X must be a positive non-
    zero number less than or equal to 65535, and .Y and .Z are
    optional and if present must be non-negative numbers less than
    or equal to 255. If this is not specified then it has a value
    of 0.

This means that the current_version is for reference only. It is not used in the decision to allow use of the library.

  -compatibility_version number
    For a dynamic shared library this specifies the compatibility
    version number of the library. When a library is used the com-
    patibility version is checked and if the user's version is
    greater that the library's version, an error message is printed
    and the using program exits. The format of number is X[.Y[.Z]]
    where X must be a positive non-zero number less than or equal to
    65535, and .Y and .Z are optional and if present must be non-
    negative numbers less than or equal to 255. If this is not
    specified then it has a value of 0 and no checking is done when
    the library is used.

This means that the compatibility_version is used to determine whether to allow a library to be used. It assumes that all future library versions are compatible with past ones...perfect backwards compatibility. It just prevents an application linked against a newer library from running with an older library. If the API is ever broken (which in libtool versioning does ++current,age=0,revision=0) then an application linked against an older version of the library will happily try to use a newer library version and fail with a bad message. Therefore install_name must still be responsible for preventing backwards-incompatible future versions of a library from being used by an application.

These observations reveal the strategy behind the libtool version encoding on Darwin. Note that the compatibility_version complements the Linux-scheme by fixing the flaw I mentioned above...an application linked against a newer but backward-compatible library will fail to run with an older library. The compatibility_version produces a nicer error message in this case. Unfortunately the application will simply fail to run instead of asking the dynamic loader to continue to search for a compatible version of the library. For this reason I think we should use the modified libtool approach I propose above on OSX through install_name in addition to setting compatibility_version and current_version. For normal .dylib libraries the version can be encoded into the filename portion of the install_name. For Frameworks the version can be encoded into the Framework version, which is effectively the directory portion of the install_name.

Given this information it is clear that we can map the VERSION property to current_version without creating strange behavior since it is for reference only. We may also map SOVERSION to compatibility_version because both are just an interface version number. However, the other discussion above in this note and others still justify adding the VERSION_CURRENT, VERSION_AGE, and VERSION_REVISION interface. When used it can override the VERSION and SOVERSION with the correct libtool-style behavior.
(0012551)
Christian Steineck (reporter)
2008-06-27 03:58

Is there something new about this?

It would be great to get a solution here.

greets

christian
(0012670)
Brad King (manager)
2008-07-09 10:12

I've committed changes to map

  VERSION -> current_version
  SOVERSION -> compatibility_version

as discussed above.

/cvsroot/CMake/CMake/Modules/Platform/Darwin.cmake,v <-- Modules/Platform/Darwin.cmake
new revision: 1.47; previous revision: 1.46
/cvsroot/CMake/CMake/Source/cmGlobalXCodeGenerator.cxx,v <-- Source/cmGlobalXCodeGenerator.cxx
new revision: 1.195; previous revision: 1.194
/cvsroot/CMake/CMake/Source/cmMakefileLibraryTargetGenerator.cxx,v <-- Source/cmMakefileLibraryTargetGenerator.cxx
new revision: 1.62; previous revision: 1.61
/cvsroot/CMake/CMake/Source/cmMakefileLibraryTargetGenerator.h,v <-- Source/cmMakefileLibraryTargetGenerator.h
new revision: 1.8; previous revision: 1.7
(0012685)
Brad King (manager)
2008-07-09 17:46

For older mac versions the flags should not have the dylib_ prefix.

/cvsroot/CMake/CMake/Modules/Platform/Darwin.cmake,v <-- Modules/Platform/Darwin.cmake
new revision: 1.48; previous revision: 1.47
(0012765)
Alex Neundorf (developer)
2008-07-20 17:16

Is there a reason why you didn't close the bugreport ?

Alex
(0012769)
Brad King (manager)
2008-07-21 09:13

I was leaving it open in case there were more fix commits and then forgot about it. I'll close it now.

 Issue History
Date Modified Username Field Change
2007-10-25 10:05 Brad King Note Added: 0009553
2007-10-25 10:38 Brad King Note Added: 0009554
2007-10-25 10:56 Brad King Note Added: 0009555
2007-10-25 11:12 Brad King Note Added: 0009556
2007-10-25 11:57 Brad King Note Added: 0009557
2007-10-25 12:43 Alex Neundorf Note Added: 0009558
2007-10-25 12:56 Brad King Note Added: 0009561
2007-10-25 17:58 Brad King Note Added: 0009569
2008-06-27 03:58 Christian Steineck Note Added: 0012551
2008-07-09 10:12 Brad King Note Added: 0012670
2008-07-09 17:46 Brad King Note Added: 0012685
2008-07-20 17:16 Alex Neundorf Note Added: 0012765
2008-07-21 09:13 Brad King Note Added: 0012769
2008-07-21 09:13 Brad King Status assigned => closed
2008-07-21 09:13 Brad King Resolution open => fixed


Copyright © 2000 - 2018 MantisBT Team