Skip to content

Conversation

@jimklimov
Copy link
Member

@jimklimov jimklimov commented Jan 9, 2026

Follows up on issue #2800 by minimizing the repetitions of binary code in NUT programs, by delivering a few SO/DLL/... libraries with libcommon* contents.

Probably the existing static libs will still be built to optionally produce shared ones from them, in particular for libnutscanner and libupsclient consumers to work unmodified (whatever symbols were exported from the well-known library names before should continue being exported and not need consumer rebuilds). In my short experiments it seems the symbol visibility is not transitive, at least not on all platforms (so having a libupsclient known dynamically linked to some libnutcommon does not help final programs see debugging etc. methods from common code).

Hopefully, on most operating systems the use of shared dynamic objects (loaded by several running NUT programs) should also use less RAM, than when each binary carries its own copy.

@jimklimov jimklimov changed the title Issue 2800 Experiment building NUT with shared private libraries Jan 9, 2026
@jimklimov jimklimov added packaging CI Entries related to continuous integration infrastructure (here CI = tools + scripts + recipes) portability We want NUT to build and run everywhere possible labels Jan 10, 2026
@jimklimov jimklimov added this to the 2.8.5 milestone Jan 10, 2026
@jimklimov jimklimov force-pushed the issue-2800 branch 2 times, most recently from 9b07638 to 0c2b095 Compare January 10, 2026 15:56
@jimklimov
Copy link
Member Author

jimklimov commented Jan 10, 2026

Interim result on a Linux build:

:; ./configure --enable-Wcolor --enable-configure-debug --enable-warnings --enable-Werror --enable-keep_nut_report_feature --with-all=auto --with-cgi=auto --with-serial=auto --with-dev=auto --with-nut_monitor=auto --with-pynut=auto --disable-force-nut-version-header --enable-check-NIT --enable-maintainer-mode --with-doc=skip --enable-docs-man-for-progs-built-only=no PKG_CONFIG_PATH=/usr/local/lib/x86_64-linux-gnu/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig --disable-silent-rules --enable-docs-changelog=no --with-unmapped-data-points --with-debuginfo=auto CC=/usr/lib/ccache/clang CXX=/usr/lib/ccache/clang++ CPP='clang -E' \
    --enable-shared-private-libs

Toggling the flag got about 2x savings:

:; du -ks /dev/shm/nut-inst-*
51556   /dev/shm/nut-inst-big
23768   /dev/shm/nut-inst-small

Several private libs delivered:

:; ls -la /dev/shm/nut-inst-small/usr/local/ups/lib/*.so.*.*
-rwxr-xr-x 1 jim jim 1248016 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutclient.so.2.0.2
-rwxr-xr-x 1 jim jim  491376 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutclientstub.so.1.0.1
-rwxr-xr-x 1 jim jim 1496888 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutconf.so.0.0.1
-rwxr-xr-x 1 jim jim  239280 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutprivate-common-all.so.0.0.0
-rwxr-xr-x 1 jim jim  214616 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutprivate-common-client.so.0.0.0
-rwxr-xr-x 1 jim jim  189448 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutprivate-drivers-common.so.0.0.0
-rwxr-xr-x 1 jim jim   40112 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutprivate-drivers-serial.so.0.0.0
-rwxr-xr-x 1 jim jim  425360 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libnutscan.so.4.0.0
-rwxr-xr-x 1 jim jim  251152 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/lib/libupsclient.so.7.0.0

:;  ldd /dev/shm/nut-inst-small/usr/local/ups/lib/libnutprivate-drivers-common.so.0
        linux-vdso.so.1 (0x00007ffc163bb000)
        libnutprivate-common-all.so.0 => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007eb37f400000)
        /lib64/ld-linux-x86-64.so.2 (0x00007eb37f850000)

:; ldd /dev/shm/nut-inst-small/usr/local/ups/bin/usbhid-ups
        linux-vdso.so.1 (0x00007fff0d9ea000)
        libnutprivate-drivers-common.so.0 => not found
        libnutprivate-common-all.so.0 => not found
        libusb-1.0.so.0 => /lib/x86_64-linux-gnu/libusb-1.0.so.0 (0x00007c579b1b9000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007c579b0d2000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007c579ae00000)
        libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007c579b0a6000)
        /lib64/ld-linux-x86-64.so.2 (0x00007c579b249000)

:; ldd /dev/shm/nut-inst-small/usr/local/ups/bin/nut-scanner
        linux-vdso.so.1 (0x00007fff37ff8000)
        libnutscan.so.4 => not found
        libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x000074e5d3f29000)
        libltdl.so.7 => /lib/x86_64-linux-gnu/libltdl.so.7 (0x000074e5d3f1e000)
        libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x000074e5d3e7a000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x000074e5d3a00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000074e5d3600000)
        liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x000074e5d3e4d000)
        libzstd.so.1 => /lib/x86_64-linux-gnu/libzstd.so.1 (0x000074e5d3931000)
        liblz4.so.1 => /lib/x86_64-linux-gnu/liblz4.so.1 (0x000074e5d3911000)
        libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x000074e5d3906000)
        libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x000074e5d34c2000)
        /lib64/ld-linux-x86-64.so.2 (0x000074e5d401a000)
        libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x000074e5d38e0000)

:; ldd /dev/shm/nut-inst-small/usr/local/ups/bin/upsc
        linux-vdso.so.1 (0x00007fffed598000)
        libnutprivate-common-client.so.0 => not found
        libupsclient.so.7 => not found
        libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007df80bf6a000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007df80ba00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007df80b600000)
        /lib64/ld-linux-x86-64.so.2 (0x00007df80c028000)

Some lessons learned:

  • It is complicated to make sure the originally shared libraries (libupsclient, libnutclient, libnutscan...) remain drop-in usable, so contain and export all needed symbols. Ideally they would further refer to a shared libnutcommon* for those (and so be smaller themselves), but it seems dynamic linking does not work transiently like that.
  • It would be nice for each NUT binary to still carry the version elements inside, otherwise we would report the version of the shared library even if an individual program was updated. It may make sense to differentiate the two somehow, though.
  • If several NUT libraries are linked in dynamically at run-time (e.g. libnutscan and libupsclient), are there any conflicts for same-named symbols (debug level etc.)?

@jimklimov
Copy link
Member Author

Missed updating some further recipes in that iteration. Here's the example change for upsd footprint after fixing the LDADD in server/Makefile.am to respect the new option:

:; ls -la /dev/shm/nut-inst-smaller/usr/local/ups/sbin/upsd /dev/shm/nut-inst-small/usr/local/ups/sbin/upsd /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutprivate-common-all.so.0.0.0
-rwxr-xr-x 1 jim jim 412136 Jan 10 17:03 /dev/shm/nut-inst-small/usr/local/ups/sbin/upsd
-rwxr-xr-x 1 jim jim 207840 Jan 10 17:33 /dev/shm/nut-inst-smaller/usr/local/ups/sbin/upsd
-rwxr-xr-x 1 jim jim 239280 Jan 10 17:32 /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutprivate-common-all.so.0.0.0

@jimklimov
Copy link
Member Author

Regarding the concern of binaries losing the version identification: as of current commit, they do (when built with shared libs mode), as seen below. Files that retain the ID include the new libs and libnutscan, the nut-scanner and nutconf programs, upssched (bug to fix), and text files.

:; grep '2\.8\.4' /dev/shm/nut-inst-smaller/usr/local/ups/*/* 2>&1 | grep -v 'Is a directory'

grep: /dev/shm/nut-inst-smaller/usr/local/ups/bin/nut-scanner: binary file matches
grep: /dev/shm/nut-inst-smaller/usr/local/ups/bin/nutconf: binary file matches

grep: /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutprivate-common-all.a: binary file matches
grep: /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutprivate-common-all.so.0.0.0: binary file matches
grep: /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutprivate-common-client.a: binary file matches
grep: /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutprivate-common-client.so.0.0.0: binary file matches
grep: /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutscan.a: binary file matches
grep: /dev/shm/nut-inst-smaller/usr/local/ups/lib/libnutscan.so.4.0.0: binary file matches

grep: /dev/shm/nut-inst-smaller/usr/local/ups/sbin/upssched: binary file matches

/dev/shm/nut-inst-smaller/usr/local/ups/sbin/upsdrvsvcctl:PACKAGE_VERSION='2.8.4.1063.3'
/dev/shm/nut-inst-smaller/usr/local/ups/share/config.nut_report_feature.log:* configured version:       2.8.4.1063.3 (2.8.4.1063.3-1066+g0c2b09571) development iteration
/dev/shm/nut-inst-smaller/usr/local/ups/share/config.nut_report_feature.log:* would generate ChangeLog* file formats:   text:no adoc:no html-single:no html-chunked:no pdf:no starting from 'v2.8.4' to 'HEAD'
/dev/shm/nut-inst-smaller/usr/local/ups/share/driver.list:# Network UPS Tools - 2.8.4.1063.3 - Hardware Compatibility List

For comparison, the build in default (legacy) mode has a lot more hits; notably only libnutscan had the version info built in, so one headache less for backwards compatibility:

:; grep '2\.8\.4' /dev/shm/nut-inst-big/usr/local/ups/*/* 2>&1 | grep -v 'Is a directory'
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/adelsystem_cbi: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/al175: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/apc_modbus: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/apcsmart: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/apcsmart-old: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/apcupsd-ups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/asem: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bcmxcp: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bcmxcp_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/belkin: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/belkinunv: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bestfcom: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bestfortress: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bestuferrups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bestups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/bicker_ser: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/blazer_ser: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/blazer_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/clone: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/clone-outlet: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/dummy-ups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/etapro: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/everups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/failover: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/gamatronic: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/generic_gpio_libgpiod: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/generic_modbus: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/genericups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/huawei-ups2000: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/hwmon_ina219: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/isbmex: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/ivtscd: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/liebert: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/liebert-esp2: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/liebert-gxe: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/masterguard: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/meanwell_ntu: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/metasys: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/mge-shut: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/mge-utalk: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/microdowell: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/microsol-apc: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/must_ep2000pro: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/netxml-ups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nhs_ser: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nut-ipmipsu: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nutconf: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nutdrv_atcl_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nutdrv_hashx: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nutdrv_qx: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/nutdrv_siemens-sitop: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/oneac: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/optiups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/phoenixcontact_modbus: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/pijuice: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/powercom: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/powerman-pdu: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/powerpanel: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/powervar_cx_ser: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/powervar_cx_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/rhino: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/richcomm_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/riello_ser: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/riello_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/safenet: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/skel: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/sms_ser: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/snmp-ups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/socomec_jbus: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/solis: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/tripplite: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/tripplite_usb: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/tripplitesu: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/upsc: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/upscmd: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/upscode2: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/upslog: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/upsrw: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/usbhid-ups: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/ve-direct: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/bin/victronups: binary file matches

grep: /dev/shm/nut-inst-big/usr/local/ups/cgi-bin/upsset.cgi: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/cgi-bin/upsstats.cgi: binary file matches

grep: /dev/shm/nut-inst-big/usr/local/ups/lib/libnutscan.a: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/lib/libnutscan.so.4.0.0: binary file matches

grep: /dev/shm/nut-inst-big/usr/local/ups/libexec/sockdebug: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/sbin/upsd: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/sbin/upsdrvctl: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/sbin/upsmon: binary file matches
grep: /dev/shm/nut-inst-big/usr/local/ups/sbin/upssched: binary file matches

/dev/shm/nut-inst-big/usr/local/ups/sbin/upsdrvsvcctl:PACKAGE_VERSION='2.8.4.1063.3'
/dev/shm/nut-inst-big/usr/local/ups/share/config.nut_report_feature.log:* configured version:   2.8.4.1063.3 (2.8.4.1063.3-1066+g9b076384d) development iteration
/dev/shm/nut-inst-big/usr/local/ups/share/config.nut_report_feature.log:* would generate ChangeLog* file formats:       text:no adoc:no html-single:no html-chunked:no pdf:no starting from 'v2.8.4' to 'HEAD'
/dev/shm/nut-inst-big/usr/local/ups/share/driver.list:# Network UPS Tools - 2.8.4.1063.3 - Hardware Compatibility List

@jimklimov
Copy link
Member Author

Footprint changes with Windows seem more drastic, about 6x for some drivers e.g.:

$ find /c/Users/jim/Desktop/nut-* -name 'liebert*' -ls
16888498602748174  16076 -rwxr-xr-x   1 jim      None     16460689 Jan 10 17:16 /c/Users/jim/Desktop/nut-inst/mingw64/bin/liebert-esp2.exe
 7599824371500022  16112 -rwxr-xr-x   1 jim      None     16495347 Jan 10 17:16 /c/Users/jim/Desktop/nut-inst/mingw64/bin/liebert-gxe.exe
 7036874418078701  16056 -rwxr-xr-x   1 jim      None     16440932 Jan 10 17:16 /c/Users/jim/Desktop/nut-inst/mingw64/bin/liebert.exe

 7036874418565682   2640 -rwxr-xr-x   1 jim      None      2699675 Jan 11 19:18 /c/Users/jim/Desktop/nut-inst-small/mingw64/bin/liebert-esp2.exe
 4222124651459855   2676 -rwxr-xr-x   1 jim      None      2739516 Jan 11 19:18 /c/Users/jim/Desktop/nut-inst-small/mingw64/bin/liebert-gxe.exe
 6473924465144369   2620 -rwxr-xr-x   1 jim      None      2679230 Jan 11 19:18 /c/Users/jim/Desktop/nut-inst-small/mingw64/bin/liebert.exe

$ find /c/Users/jim/Desktop/nut-* -name 'blazer*' -ls
14355223812346528  17372 -rwxr-xr-x   1 jim      None     17788500 Jan 10 17:16 /c/Users/jim/Desktop/nut-inst/mingw64/bin/blazer_ser.exe
17169973579662336  18684 -rwxr-xr-x   1 jim      None     19128528 Jan 10 17:16 /c/Users/jim/Desktop/nut-inst/mingw64/bin/blazer_usb.exe
 9288674232252331   4156 -rwxr-xr-x   1 jim      None      4255138 Jan 11 19:19 /c/Users/jim/Desktop/nut-inst-small/mingw64/bin/blazer_ser.exe
 2814749767907332   6844 -rwxr-xr-x   1 jim      None      7006678 Jan 11 19:20 /c/Users/jim/Desktop/nut-inst-small/mingw64/bin/blazer_usb.exe

There is also decent potential in re-using USB code, and maybe some other popular inner and third-party dependency groups that apply to several drivers but not to all of them.

Overall in this iteration for Windows builds, this saved 3x the space:

$ du -ks /c/Users/jim/Desktop/nut-inst*
1592627 /c/Users/jim/Desktop/nut-inst
590551  /c/Users/jim/Desktop/nut-inst-small

This hopefully might lead to faster Appveyor CI builds (especially the late part about archival and compression, eventually MSI packaging) that nowadays too often times out beyond the 1hr allowance.

@AppVeyorBot
Copy link

@jimklimov jimklimov force-pushed the issue-2800 branch 2 times, most recently from 198c186 to 684e886 Compare January 11, 2026 22:34
@AppVeyorBot
Copy link

@jimklimov
Copy link
Member Author

jimklimov commented Jan 12, 2026

With latest commit, flipped the switch for default WIN32 build via ci_build.sh (e.g. on Appveyor CI) to use the new feature, will see soon how much it helps with the build timeouts and resulting footprint...

UPDATE: got a 2x reduction in installation footprint, and a bit in the packed archive too:

7-Zip 25.01 (x64) : Copyright (c) 1999-2025 Igor Pavlov : 2025-08-03
Scanning the drive:
20 folders, 257 files, 1734454844 bytes (1655 MiB)
Creating archive: ../NUT-for-Windows-x86_64-SNAPSHOT-2.8.4.3966-master.7z
Add new data to archive: 20 folders, 257 files, 1734454844 bytes (1655 MiB)
Files read from disk: 257
Archive size: 54914322 bytes (53 MiB)
Everything is Ok
7-Zip 25.01 (x64) : Copyright (c) 1999-2025 Igor Pavlov : 2025-08-03
Scanning the drive:
20 folders, 278 files, 842871597 bytes (804 MiB)
Creating archive: ../NUT-for-Windows-x86_64-SNAPSHOT-2.8.4.3970-master.7z
Add new data to archive: 20 folders, 278 files, 842871597 bytes (804 MiB)
Files read from disk: 278
Archive size: 50219815 bytes (48 MiB)
Everything is Ok

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@jimklimov
Copy link
Member Author

After some back-and-forth in the sources, went for several ways of securing this design:

  • Revised structure for passing callbacks to include identifiable magic data, so incorrect linking/referencing can be discarded early and safely (at least the kinds that happen by mistake, not deliberate attack);
  • Revised shared private file naming to constrain to the expected NUT release:
:; find . -name 'libnutpri*' -ls
   475314      4 -rw-r--r--   1 jim      jim          1874 Jan 12 15:16 ./drivers/libnutprivate-2_8_4-drivers-serial.la
   475112      4 -rw-r--r--   1 jim      jim          1224 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-common.lai
   475251    180 -rw-r--r--   1 jim      jim        181068 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-common.a
   406739      0 lrwxrwxrwx   1 jim      jim            43 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-common.so.1 -> libnutprivate-2_8_4-drivers-common.so.1.0.0
   405284     28 -rwxr-xr-x   1 jim      jim         26632 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-serial.so.1.0.0
   475001      0 lrwxrwxrwx   1 jim      jim            43 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-serial.so -> libnutprivate-2_8_4-drivers-serial.so.1.0.0
   453014      0 lrwxrwxrwx   1 jim      jim            43 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-common.so -> libnutprivate-2_8_4-drivers-common.so.1.0.0
   412494      0 lrwxrwxrwx   1 jim      jim            43 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-serial.so.1 -> libnutprivate-2_8_4-drivers-serial.so.1.0.0
   475426      0 lrwxrwxrwx   1 jim      jim            40 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-serial.la -> ../libnutprivate-2_8_4-drivers-serial.la
   475078    132 -rwxr-xr-x   1 jim      jim        131192 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-common.so.1.0.0
   475368      4 -rw-r--r--   1 jim      jim          1284 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-serial.lai
   475138      0 lrwxrwxrwx   1 jim      jim            40 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-common.la -> ../libnutprivate-2_8_4-drivers-common.la
   475358     16 -rw-r--r--   1 jim      jim         14282 Jan 12 15:16 ./drivers/.libs/libnutprivate-2_8_4-drivers-serial.a
   475054      4 -rw-r--r--   1 jim      jim          1842 Jan 12 15:16 ./drivers/libnutprivate-2_8_4-drivers-common.la
   416892      4 -rw-r--r--   1 jim      jim          1150 Jan 12 15:16 ./common/libnutprivate-2_8_4-common-client.la
   472956      0 lrwxrwxrwx   1 jim      jim            39 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-all.so -> libnutprivate-2_8_4-common-all.so.1.0.0
   473440    160 -rw-r--r--   1 jim      jim        162454 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-all.a
   472777      4 -rw-r--r--   1 jim      jim          1140 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-all.lai
   473138    128 -rwxr-xr-x   1 jim      jim        127288 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-all.so.1.0.0
   472828      0 lrwxrwxrwx   1 jim      jim            42 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-client.so -> libnutprivate-2_8_4-common-client.so.1.0.0
   471600      4 -rw-r--r--   1 jim      jim          1151 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-client.lai
   473187    112 -rwxr-xr-x   1 jim      jim        113112 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-client.so.1.0.0
   473405    136 -rw-r--r--   1 jim      jim        138802 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-client.a
    12677      0 lrwxrwxrwx   1 jim      jim            42 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-client.so.1 -> libnutprivate-2_8_4-common-client.so.1.0.0
   473082      0 lrwxrwxrwx   1 jim      jim            39 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-client.la -> ../libnutprivate-2_8_4-common-client.la
   411828      0 lrwxrwxrwx   1 jim      jim            39 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-all.so.1 -> libnutprivate-2_8_4-common-all.so.1.0.0
   473098      0 lrwxrwxrwx   1 jim      jim            36 Jan 12 15:16 ./common/.libs/libnutprivate-2_8_4-common-all.la -> ../libnutprivate-2_8_4-common-all.la
   472732      4 -rw-r--r--   1 jim      jim          1139 Jan 12 15:16 ./common/libnutprivate-2_8_4-common-all.la

@AppVeyorBot
Copy link

@jimklimov jimklimov mentioned this pull request Jan 12, 2026
2 tasks
@gdt
Copy link
Contributor

gdt commented Jan 12, 2026

A few questions:

  • Is the PR cleanly rebased, so that reviewing commit by commit is sensible? If not, is that reasonable?
  • Is this a mechanism that is intended to be usable on all systems, or just Windows?
  • Is it opt-in? opt-out? (as a configure option)
  • What systems has it been tested on besides Windows?

@jimklimov
Copy link
Member Author

  • should be recently rebased
  • opt-in, off by default (on in CI scenarios, not configure itself); I suppose it can remain opt-in for a few releases while brave souls build differently and look for sharp edges in practice
  • extensively tested on Windows and Linux (x86, arm) so far; expected to be functional and useful everywhere (at least where shared libs are a thing; hopefully no-op otherwise as it goes through libtool)
  • not in scope right here right now - revision which exactly programs need the "client" or "all" libcommon variant (this just followed existing precedent)
  • not quite in scope but related and might follow soon - stacking libcommon "all" as a superset of "client", and/or grouping of driver USB libs similarly to Serial ones (at least these changes make more sense now than they did with static builds)
  • to think: maybe want to ensure that a copy of libcommonversion.la is linked always into binaries, not shared libs.

@jimklimov
Copy link
Member Author

jimklimov commented Jan 12, 2026

Reviewing commit-by-commit should be reasonable, at least I fixed-up most of the found glaring errors into commits that originally introduced them. A few fixes come as separate commits though, as it was easier to sync multi-system development and testing sometimes.

jimklimov and others added 28 commits January 18, 2026 22:15
…ing a newly made structure (with expected NULL references) [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…rix with BUILD_TYPE=default-all-errors and explicit requests via WITH_LIBNUTPRIVATE envvar [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…ivate* dependency definitions (also wants libcommonversion) [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…AVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_MISSING_FIELD_INITIALIZERS{,_BESIDEFUNC} to quiesce stuff={0} initializations (warnings with ancient compilers) [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…io_utest.c, tests/driver_methods_utest.c: wrap calls to register/validate callbacks with ignorance of "address" and "unreachable code" warnings [networkupstools#2800]

Some compilers detect that a persistent structure's address would never
be NULL so some conditions are always FALSE and some code never runs.
The downside of using macros vs. methods...

Signed-off-by: Jim Klimov <[email protected]>
…ds into a separate shell call

Signed-off-by: Jim Klimov <[email protected]>
…n-private.la for builds where we ENABLE_SHARED_PRIVATE_LIBS so programs have their versions built in and can consult what the library thinks about itself [networkupstools#2800]

Costs about 30Kb (Linux with debug) per binary (upsc) for the
now re-embedded version data and a few methods to show it.
But still saves 200K compared to a build without libnutprivate.

Signed-off-by: Jim Klimov <[email protected]>
…ibcommonclient.la for ENABLE_SHARED_PRIVATE_LIBS mode [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…e ABS_TOP_{BUILD,SRC}DIR values

Signed-off-by: Jim Klimov <[email protected]>
…h libdummy_mockdrv and libcommonversion [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…naries keeping their version info inside, libnutscan is also such a binary as far as third-party consumers are concerned [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…lting of WITH_LIBNUTPRIVATE envvar toggle for WIN32 (cross-)builds [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
… on MacOS for now, stick with default (no) [networkupstools#2800]"

This reverts commit 520539e :
issue was fixed by passing version data/methods into drivers
using the callback mechanism made for upsdrv_*() implementations.

Signed-off-by: Jim Klimov <[email protected]>
…ARED_PRIVATE_LIBS more conservatively [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
…n we configure --without-serial

Signed-off-by: Jim Klimov <[email protected]>
…ot currently mean disabling modbus drivers nor libnutscan ability to talk serial

Signed-off-by: Jim Klimov <[email protected]>
…g of suggest_NDE_conflict() when we ENABLE_SHARED_PRIVATE_LIBS [networkupstools#2800]

Signed-off-by: Jim Klimov <[email protected]>
@jimklimov
Copy link
Member Author

Rebased for a cleaner merge and easier final review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI Entries related to continuous integration infrastructure (here CI = tools + scripts + recipes) packaging portability We want NUT to build and run everywhere possible

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants