Skip to content

Commit

Permalink
test cases/linuxlike/14 static dynamic linkage: Add matrix by static
Browse files Browse the repository at this point in the history
Not only Solaris doesn't ship static libraries, it's common practice for
Linux distributions in general (security updates, size/space, etc.).
See, for example 'test cases/frameworks/1 boost'.

Enhancements:

  * Added flexibility: Introduced the SKIP_STATIC_ZLIB environment
    variable to optionally skip static linkage (similar to SKIP_STATIC_BOOST).
  * Expanded testing: Included testing for dynamic linkage, which was previously
    limited to static in verify_static.py (renamed to verify_zlib_linkage.py).
  * Improved test coverage: Ensured that each case is tested for
    its specific linkage type and the opposite case fails.
  * Addressed Cygwin issue: Ensured that the corresponding symbol is
    present on Cygwin.

Renames, comments, etc.
  • Loading branch information
dememax committed Nov 4, 2024
1 parent 040c8d0 commit a509016
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 71 deletions.
5 changes: 3 additions & 2 deletions test cases/linuxlike/14 static dynamic linkage/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "zlib.h"

int main(void) {
printf("%s\n", zlibVersion());
return 0;
const char * const v = zlibVersion();
printf("%s\n", v ? v : "<NULL>");
return !v;
}
58 changes: 26 additions & 32 deletions test cases/linuxlike/14 static dynamic linkage/meson.build
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
project('static dynamic', 'c')
# This test uses zlib to verify static and dynamic linkages.
# A C program is used to call a single zlib function (zlibVersion).
# The nm utility is then used to check for the existence of this symbol
# in the resulting binary.

# Solaris does not ship static libraries
if host_machine.system() == 'sunos'
has_static = false
else
has_static = true
endif
project('zlib linkage', 'c')

s = get_option('static')

cc = meson.get_compiler('c')

z_default = cc.find_library('z')
if has_static
z_static = cc.find_library('z', static: true)
endif
z_dynamic = cc.find_library('z', static: false)

exe_default = executable('main_default', 'main.c', dependencies: [z_default])
if has_static
exe_static = executable('main_static', 'main.c', dependencies: [z_static])
endif
exe_dynamic = executable('main_dynamic', 'main.c', dependencies: [z_dynamic])

test('test default', exe_default)
if has_static
test('test static', exe_static)
endif
test('test dynamic', exe_dynamic)

if has_static
test('verify static linking', find_program('verify_static.py'),
args: ['--platform=' + host_machine.system(), exe_static.full_path()])
endif
test('verify dynamic linking', find_program('verify_static.py'),
args: ['--platform=' + host_machine.system(), exe_dynamic.full_path()],
should_fail: true)
z = cc.find_library('z', static: s)

exe = executable('print_zlib_version', 'main.c', dependencies: [z])

# first step: the executable should compile and work
test('test zlib', exe)

# to check the zlib static/dynamic symbols in the resulting binary
find_program('nm')

# second step: static linkage
test('verify static zlib linking', find_program('verify_zlib_linkage.py'),
args: ['--platform=' + host_machine.system(), '--static', exe.full_path()],
should_fail: not s)

# third step: dynamic linkage
test('verify dynamic zlib linking', find_program('verify_zlib_linkage.py'),
args: ['--platform=' + host_machine.system(), exe.full_path()],
should_fail: s)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
option('static', type: 'boolean', value: false)
10 changes: 10 additions & 0 deletions test cases/linuxlike/14 static dynamic linkage/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"matrix": {
"options": {
"static": [
{ "val": "true", "skip_on_env": [ "SKIP_STATIC_ZLIB" ] },
{ "val": "false" }
]
}
}
}
37 changes: 0 additions & 37 deletions test cases/linuxlike/14 static dynamic linkage/verify_static.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Test script that checks if zlib was statically or dynamically linked to executable"""
import subprocess
import sys
import argparse

def check_zlib_symbol_common(path, is_static):
"""Tests if the binary contains zlibVersion symbol (non-Cygwin version)."""
try:
sym_opt = '--defined-only' if is_static else '--undefined-only'
output = subprocess.check_output(['nm', sym_opt, '-P', '-A', path]).decode('utf-8')
except subprocess.CalledProcessError:
# some NMs only support -U. Older binutils only supports --defined-only.
opts = '-UPA' if is_static else '-uPA'
output = subprocess.check_output(['nm', opts, path]).decode('utf-8')
# POSIX format. Prints all *defined* symbols, looks like this:
# builddir/main_static: zlibVersion T 1190 39
# or
# builddir/main_static: zlibVersion D 1fde0 30
if ': zlibVersion ' in output:
return 0
return 1

def check_zlib_symbol_cygwin(path, is_static):
"""Tests if the binary contains zlibVersion symbol (Cygwin case)."""
output = subprocess.check_output(['nm', path]).decode('utf-8')
# No matter static or dynamic, the name must exist in nm output
if ' zlibVersion' not in output:
return 2
is_dynamic = ('I __imp_zlibVersion' in output) or ('D __imp_zlibVersion' in output)
if is_dynamic == is_static: # expected/got mismatch?
return 3
return 0

def main():
"""Main function"""
parser = argparse.ArgumentParser()
parser.add_argument('path', help='executable path')
parser.add_argument('-p', '--platform')
parser.add_argument('-s', '--static', action='store_true', default=False)
args = parser.parse_args()
if args.platform == 'cygwin':
return check_zlib_symbol_cygwin(args.path, args.static)
else:
return check_zlib_symbol_common(args.path, args.static)


if __name__ == '__main__':
sys.exit(main())

0 comments on commit a509016

Please sign in to comment.