Skip to content

Commit

Permalink
Add metadata support in shared library version
Browse files Browse the repository at this point in the history
Some projects use metadata when versioning, such as the OBS build system.
When trying to pass a version from such projects to the meson build
script and use it as a dynamic library version, an error is thrown that
the version should look like X.Y.Z.

However, Semantic Versioning 2.0 assumes the use of metadata in the
version, see: https://semver.org/#spec-item-10

This commit adds support for metadata in dynamic library versions.
Fixes an issue: mesonbuild#10565
  • Loading branch information
DieTime committed Jul 7, 2022
1 parent b88bec0 commit a00327a
Show file tree
Hide file tree
Showing 17 changed files with 146 additions and 3 deletions.
10 changes: 10 additions & 0 deletions docs/markdown/snippets/metadata-in-shared-library-version.md
@@ -0,0 +1,10 @@
# Metadata support in shared library version

The shared library version now may contain build metadata specified
by adding `+` and a series of dot-separated identifiers after the
version string.

For example:
```meson
lib = shared_library('example', 'main.c', version : '1.1.0+master.g21a338e')
```
6 changes: 6 additions & 0 deletions docs/yaml/functions/shared_library.yaml
Expand Up @@ -16,6 +16,12 @@ kwargs:
`libfoo.1.1.0.dylib`. If this is not specified, `soversion` is used
instead (see above).
*(since 0.64.0)* The shared library version may contain build
metadata specified by adding `+` and a series of dot-separated
identifiers after the version string. Identifiers must contain
only ASCII alphanumeric characters and hyphens. For example:
`1.1.0+master.g21a338e`.
soversion:
type: str | int
description: |
Expand Down
9 changes: 6 additions & 3 deletions mesonbuild/build.py
Expand Up @@ -2157,7 +2157,7 @@ def determine_filenames(self):
prefix = 'lib'
suffix = 'so'
if self.ltversion:
# libfoo.so.X[.Y[.Z]] (.Y and .Z are optional)
# libfoo.so.X[.Y[.Z]][+META] (.Y, .Z and +META are optional)
self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.ltversion}'
elif self.soversion:
# libfoo.so.X
Expand Down Expand Up @@ -2228,8 +2228,11 @@ def process_kwargs(self, kwargs):
self.ltversion = kwargs['version']
if not isinstance(self.ltversion, str):
raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__)
if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion):
raise InvalidArguments(f'Invalid Shared library version "{self.ltversion}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.')
if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}(\+[A-Za-z0-9-.]+)?', self.ltversion):
raise InvalidArguments(f'Invalid Shared library version "{self.ltversion}". Must be of the form X[.Y[.Z]][+META] where X, Y, Z are numbers\n'
'and META is a string starting with \'+\' and containing only [A-Za-z0-9-.] characters. Y, Z and META are optional.')
if "+" in self.ltversion:
FeatureNew.single_use('Metadata in shared library version', '0.64.0', self.subproject)
# Try to extract/deduce the soversion
if 'soversion' in kwargs:
self.soversion = kwargs['soversion']
Expand Down
14 changes: 14 additions & 0 deletions test cases/common/252 shared version meta/libfile.c
@@ -0,0 +1,14 @@
#if defined _WIN32 || defined __CYGWIN__
#define DLL_PUBLIC __declspec(dllexport)
#else
#if defined __GNUC__
#define DLL_PUBLIC __attribute__ ((visibility("default")))
#else
#pragma message ("Compiler does not support symbol visibility.")
#define DLL_PUBLIC
#endif
#endif

int DLL_PUBLIC libfunc(void) {
return 3;
}
8 changes: 8 additions & 0 deletions test cases/common/252 shared version meta/main.c
@@ -0,0 +1,8 @@
int libfunc(void);

int main() {
if (libfunc() != 3)
return 1;

return 0;
}
6 changes: 6 additions & 0 deletions test cases/common/252 shared version meta/meson.build
@@ -0,0 +1,6 @@
project('shared version meta', 'c')

lib = shared_library('mylib', 'libfile.c', version: '1.0.0+master.b88bec0')
exe = executable('prog', 'main.c', link_with: lib)

test('test', exe)
14 changes: 14 additions & 0 deletions test cases/failing/128 shared version meta no plus/libfile.c
@@ -0,0 +1,14 @@
#if defined _WIN32 || defined __CYGWIN__
#define DLL_PUBLIC __declspec(dllexport)
#else
#if defined __GNUC__
#define DLL_PUBLIC __attribute__ ((visibility("default")))
#else
#pragma message ("Compiler does not support symbol visibility.")
#define DLL_PUBLIC
#endif
#endif

int DLL_PUBLIC libfunc(void) {
return 3;
}
@@ -0,0 +1,3 @@
project('shared version meta no plus', 'c')

lib = shared_library('mylib', 'libfile.c', version: '1.0.0master.b88bec0')
7 changes: 7 additions & 0 deletions test cases/failing/128 shared version meta no plus/test.json
@@ -0,0 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/128 shared version meta no plus/meson.build:3:0: ERROR: Invalid Shared library version \"1.0.0master.b88bec0\". Must be of the form X[.Y[.Z]][+META] where X, Y, Z are numbers"
}
]
}
14 changes: 14 additions & 0 deletions test cases/failing/129 shared version meta only plus/libfile.c
@@ -0,0 +1,14 @@
#if defined _WIN32 || defined __CYGWIN__
#define DLL_PUBLIC __declspec(dllexport)
#else
#if defined __GNUC__
#define DLL_PUBLIC __attribute__ ((visibility("default")))
#else
#pragma message ("Compiler does not support symbol visibility.")
#define DLL_PUBLIC
#endif
#endif

int DLL_PUBLIC libfunc(void) {
return 3;
}
@@ -0,0 +1,3 @@
project('shared version meta no plus', 'c')

lib = shared_library('mylib', 'libfile.c', version: '1.0.0+')
@@ -0,0 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/129 shared version meta only plus/meson.build:3:0: ERROR: Invalid Shared library version \"1.0.0+\". Must be of the form X[.Y[.Z]][+META] where X, Y, Z are numbers"
}
]
}
@@ -0,0 +1,14 @@
#if defined _WIN32 || defined __CYGWIN__
#define DLL_PUBLIC __declspec(dllexport)
#else
#if defined __GNUC__
#define DLL_PUBLIC __attribute__ ((visibility("default")))
#else
#pragma message ("Compiler does not support symbol visibility.")
#define DLL_PUBLIC
#endif
#endif

int DLL_PUBLIC libfunc(void) {
return 3;
}
@@ -0,0 +1,3 @@
project('shared version meta unexpected characters', 'c')

lib = shared_library('mylib', 'libfile.c', version: '1.0.0+master%b88bec0')
@@ -0,0 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/130 shared version meta unexpected characters/meson.build:3:0: ERROR: Invalid Shared library version \"1.0.0+master%b88bec0\". Must be of the form X[.Y[.Z]][+META] where X, Y, Z are numbers"
}
]
}
9 changes: 9 additions & 0 deletions test cases/unit/1 soname/meson.build
Expand Up @@ -21,6 +21,15 @@ shared_library('settosame', 'versioned.c',
soversion : '7.8.9',
version : '7.8.9')

shared_library('vermeta', 'versioned.c',
install : true,
version : '0.1.0+master.20220705111300.6e4b184')

shared_library('bothmeta', 'versioned.c',
install : true,
soversion : '7.8.9',
version : '0.1.0+master.20220705111300.6e4b184')

shared_module('some_module', 'versioned.c',
install: true)

Expand Down
15 changes: 15 additions & 0 deletions unittests/linuxliketests.py
Expand Up @@ -452,6 +452,21 @@ def _test_soname_impl(self, libpath, install):
self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3)

# File with version with meta
vermeta = os.path.join(libpath, 'libvermeta.so')
self.assertPathExists(vermeta + '.0.1.0+master.20220705111300.6e4b184')
self.assertEqual(os.readlink(vermeta), 'libvermeta.so.0')
self.assertEqual(get_soname(vermeta), 'libvermeta.so.0')
self.assertEqual(len(self.glob_sofiles_without_privdir(vermeta[:-3] + '*')), 3)

# File with version with meta and soversion
bothmeta = os.path.join(libpath, 'libbothmeta.so')
self.assertPathExists(bothmeta + '.0.1.0+master.20220705111300.6e4b184')
self.assertEqual(os.readlink(bothmeta), 'libbothmeta.so.7.8.9')
self.assertEqual(os.readlink(bothmeta + '.7.8.9'), 'libbothmeta.so.0.1.0+master.20220705111300.6e4b184')
self.assertEqual(get_soname(bothmeta), 'libbothmeta.so.7.8.9')
self.assertEqual(len(self.glob_sofiles_without_privdir(bothmeta[:-3] + '*')), 3)

# A shared_module that is not linked to anything
module = os.path.join(libpath, 'libsome_module.so')
self.assertPathExists(module)
Expand Down

0 comments on commit a00327a

Please sign in to comment.