bazel: add release structure improvements (versioning, SONAME, vars.sh, pkg-config)#3513
bazel: add release structure improvements (versioning, SONAME, vars.sh, pkg-config)#3513napetrov wants to merge 4 commits intouxlfoundation:mainfrom
Conversation
- Add binary ABI version (binary_major=2, binary_minor=9) to VersionInfo provider in config.bzl, separate from product version (2026.x). Matches MAJORBINARY/MINORBINARY variables in the Make build. - Add library versioning and symlinks to release rule: libonedal_core.so.2.9 (real file) libonedal_core.so.2 -> libonedal_core.so.2.9 (symlink) libonedal_core.so -> libonedal_core.so.2 (symlink) Uses ctx.actions.symlink() for hermetic, platform-aware symlink creation. - Embed SONAME into shared libraries via -Wl,-soname,libonedal_core.so.2 on Linux. Uses macro+select() pattern (select() in macro, resolved value passed via attr.string_list to rule impl). Matches Make's -Wl,-soname flag. - Add vars.sh generation from deploy/local/vars_lnx.sh template using ctx.actions.expand_template() with binary version substitutions. - Add pkg-config file generation from deploy/pkg-config/pkg-config.cpp via cpp -P preprocessor invocation. - Add release_extra_file() helper and extra_files/extra_files_dst attrs to the release() rule for declarative extra file placement. - Add BUILD files for deploy/local/ and deploy/pkg-config/ to expose template files to Bazel. - Add shell test script for release structure validation: checks versioned .so files, symlinks, SONAME (readelf -d), vars.sh substitutions, and pkg-config fields. TODO: --exclude-libs for MKL symbol hiding (tracked separately, requires build validation on larger machine). Relates to: TODO.md 'Release to oneAPI structure'
- Fix Starlark syntax: implicit string concatenation is forbidden, use single-line doc strings in attr definitions - Add Apache 2.0 copyright headers to new files: deploy/local/BUILD, deploy/pkg-config/BUILD, dev/bazel/tests/release_structure_test.sh - Add label for PR (needed by CI label check)
There was a problem hiding this comment.
Pull request overview
Adds Bazel-side “oneAPI-style” release output structure support, aiming to match the existing Make release layout (versioned shared libraries + symlinks, SONAME, generated vars.sh, and generated pkg-config file).
Changes:
- Introduces a separate binary ABI version (major/minor) in Bazel config to drive SONAME and
.sosymlink naming. - Enhances the Bazel
releaserule to emit versioned shared libraries and create.so.<major>and.sosymlinks. - Adds Bazel rules to generate
env/vars.shandlib/pkgconfig/onedal.pc, plus a shell script to validate the release tree.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| dev/bazel/tests/release_structure_test.sh | Adds a release-structure validation script (versioned libs, symlinks, SONAME, vars.sh, pkg-config). |
| dev/bazel/scripts.bzl | New Starlark rules to generate vars.sh and onedal.pc into the release tree. |
| dev/bazel/release.bzl | Adds .so versioning/symlinks and support for copying additional generated files into the release. |
| dev/bazel/daal.bzl | Extends dynamic-lib macro API to accept a binary ABI major for SONAME. |
| dev/bazel/config/config.tpl.BUILD | Extends version_info() instantiation to include binary ABI version fields. |
| dev/bazel/config/config.bzl | Extends VersionInfo provider and hardcodes binary ABI version substitutions. |
| dev/bazel/cc/link.bzl | Threads through extra linker flags to the dynamic link action. |
| dev/bazel/cc.bzl | Adds SONAME link-flag generation and plumbs it into dynamic-library linking. |
| deploy/pkg-config/BUILD | Exposes pkg-config.cpp as a Bazel-exported file. |
| deploy/local/BUILD | Exposes vars_*.{sh,bat} templates as Bazel-exported files. |
| BUILD | Wires new script-generation targets into //:release via extra_files. |
| # Find actual versioned file | ||
| versioned=$(ls "${LIB_DIR}/${lib_base}.so."* 2>/dev/null | grep -v ".so.[0-9]*$" || true) | ||
|
|
||
| if [ -z "$versioned" ]; then | ||
| _fail "${lib_base}.so.X.Y not found in ${LIB_DIR}" | ||
| continue | ||
| fi | ||
|
|
There was a problem hiding this comment.
The logic that finds the versioned .so file is fragile: it relies on parsing ls output and a negative grep, and will break if more than one match exists (versioned becomes multi-line and -f "$versioned" fails). Consider using a glob that matches only .so.<major>.<minor> (or use find and enforce exactly one match).
| # Find actual versioned file | |
| versioned=$(ls "${LIB_DIR}/${lib_base}.so."* 2>/dev/null | grep -v ".so.[0-9]*$" || true) | |
| if [ -z "$versioned" ]; then | |
| _fail "${lib_base}.so.X.Y not found in ${LIB_DIR}" | |
| continue | |
| fi | |
| # Find actual versioned file (.so.<major>.<minor>) using globbing instead of ls/grep | |
| shopt -s nullglob | |
| versioned_candidates=( "${LIB_DIR}/${lib_base}.so."[0-9]*.[0-9]* ) | |
| shopt -u nullglob | |
| if [ "${#versioned_candidates[@]}" -eq 0 ]; then | |
| _fail "${lib_base}.so.X.Y not found in ${LIB_DIR}" | |
| continue | |
| elif [ "${#versioned_candidates[@]}" -gt 1 ]; then | |
| _fail "Multiple ${lib_base}.so.X.Y candidates found in ${LIB_DIR}: ${versioned_candidates[*]}" | |
| continue | |
| fi | |
| versioned="${versioned_candidates[0]}" |
dev/bazel/config/config.bzl
Outdated
| "%{version_binary_major}": "2", | ||
| "%{version_binary_minor}": "9", |
There was a problem hiding this comment.
The Bazel config hardcodes binary ABI version to 2.9, but makefile.ver currently defines MAJORBINARY=3 and MINORBINARY=0. This will generate incorrect SONAMEs/symlinks and makes Bazel releases diverge from Make. Please source these values from the same place as Make (or at minimum update them to match makefile.ver) so the ABI version stays in sync.
| "%{version_binary_major}": "2", | |
| "%{version_binary_minor}": "9", | |
| "%{version_binary_major}": "3", | |
| "%{version_binary_minor}": "0", |
dev/bazel/daal.bzl
Outdated
| def daal_dynamic_lib(name, lib_tags=["daal"], binary_major = None, **kwargs): | ||
| """Build a oneDAL shared library. | ||
|
|
||
| Automatically sets SONAME to lib<lib_name>.so.<binary_major> on Linux | ||
| when binary_major is provided. | ||
| """ | ||
| cc_dynamic_lib( | ||
| name = name, | ||
| lib_tags = lib_tags, | ||
| binary_major = binary_major, | ||
| **kwargs, | ||
| ) |
There was a problem hiding this comment.
binary_major defaults to None here, and none of the existing dynamic library targets pass it (e.g., cpp/daal:core_dynamic and cpp/oneapi/dal:dynamic). As a result, the new SONAME embedding logic in cc_dynamic_lib never activates and the release_structure_test SONAME check is likely to fail. Consider wiring binary_major automatically from @config//:version (VersionInfo.binary_major) inside the cc_dynamic_lib rule implementation, or update the dynamic library targets/macros so they always provide binary_major.
| out = out.path, | ||
| ), | ||
| mnemonic = "GenPkgConfig", | ||
| progress_message = "Generating pkg-config file {}".format(out.short_path), |
There was a problem hiding this comment.
This action invokes cpp via run_shell without use_default_shell_env = True (and without declaring a tool), so it may fail in Bazel sandboxes/CI with cpp: command not found. Consider either enabling use_default_shell_env, or (preferably) making the preprocessor an explicit tool dependency so the action is reproducible.
| progress_message = "Generating pkg-config file {}".format(out.short_path), | |
| progress_message = "Generating pkg-config file {}".format(out.short_path), | |
| use_default_shell_env = True, |
| # pkg-config.cpp uses #if/#define blocks and the cpp preprocessor | ||
| # to emit different content for static vs dynamic variants. | ||
| # expand_template is not used here because the template relies on | ||
| # cpp's conditional compilation, which cannot be replicated with | ||
| # simple string substitution. | ||
| ctx.actions.run_shell( | ||
| inputs = [ctx.file.template], | ||
| outputs = [out], | ||
| command = ( | ||
| "cpp -P " + | ||
| "-DDAL_MAJOR_BINARY={binary_major} " + | ||
| "-DDAL_MINOR_BINARY={binary_minor} " + | ||
| "-DDAL_MAJOR={major} " + | ||
| "-DDAL_MINOR={minor} " + | ||
| "{template} > {out}" | ||
| ).format( | ||
| binary_major = vi.binary_major, | ||
| binary_minor = vi.binary_minor, | ||
| major = vi.major, | ||
| minor = vi.minor, | ||
| template = ctx.file.template.path, | ||
| out = out.path, | ||
| ), |
There was a problem hiding this comment.
The comment says pkg-config.cpp relies on the defines passed here, but deploy/pkg-config/pkg-config.cpp currently doesn't reference DAL_MAJOR(_BINARY)/DAL_MINOR(_BINARY) at all (Version is hardcoded). Either update the template to consume these defines or drop the unused -D flags to avoid giving a false impression that the .pc version is generated from config.
|
|
||
| # Check unversioned symlink | ||
| if [ -L "$so" ]; then | ||
| _pass "${lib_base}.so unversioned symlink exists" | ||
| else | ||
| _fail "${lib_base}.so unversioned symlink missing" | ||
| fi | ||
| done | ||
|
|
||
| # --------------------------------------------------------------------------- |
There was a problem hiding this comment.
The SONAME test only asserts that a SONAME exists, but doesn’t validate it matches the expected major ABI (e.g., libonedal_core.so.<binary_major>). To catch regressions, compare the extracted SONAME against the expected value derived from the versioned filename or from config.
| versioned=$(ls "${LIB_DIR}/${lib_base}.so."* 2>/dev/null | grep -v ".so.[0-9]*$" || true) | ||
|
|
||
| if [ -z "$versioned" ]; then | ||
| _fail "${lib_base}.so.X.Y not found in ${LIB_DIR}" | ||
| continue | ||
| fi | ||
|
|
||
| # Check versioned file is a real file | ||
| if [ -f "$versioned" ] && [ ! -L "$versioned" ]; then | ||
| _pass "${lib_base}.so.X.Y is a real file" | ||
| else | ||
| _fail "${lib_base}.so.X.Y should be a regular file, not a symlink" | ||
| fi | ||
|
|
||
| # Check major symlink exists and points correctly | ||
| major_ver=$(echo "$versioned" | grep -oP '\.\d+\.\d+$' | cut -d. -f2) | ||
| major_link="${LIB_DIR}/${lib_base}.so.${major_ver}" | ||
| if [ -L "$major_link" ]; then | ||
| _pass "${lib_base}.so.${major_ver} symlink exists" | ||
| else | ||
| _fail "${lib_base}.so.${major_ver} symlink missing" | ||
| fi | ||
|
|
||
| # Check unversioned symlink | ||
| if [ -L "$so" ]; then | ||
| _pass "${lib_base}.so unversioned symlink exists" |
There was a problem hiding this comment.
This script uses grep -P (PCRE), which isn’t available in some environments (e.g., busybox grep, some minimal distros). Prefer POSIX ERE (grep -E), sed, or bash pattern matching for portability.
| name = name, | ||
| template = select({ | ||
| # TODO: add Windows condition when Windows toolchain is ready. | ||
| # "@platforms//os:windows": "@onedal//deploy/local:vars_win.bat", |
There was a problem hiding this comment.
generate_vars_sh is documented as Linux/macOS, but the template select() always uses vars_lnx.sh. Either add an OS-specific select branch for macOS (to use deploy/local:vars_mac.sh) or adjust the docstring/comments to reflect Linux-only behavior for now.
| # "@platforms//os:windows": "@onedal//deploy/local:vars_win.bat", | |
| # "@platforms//os:windows": "@onedal//deploy/local:vars_win.bat", | |
| "@platforms//os:osx": "@onedal//deploy/local:vars_mac.sh", |
| def _copy_lib(ctx, prefix, version_info): | ||
| """Copy libraries to release directory with versioning and symlinks for .so files. | ||
|
|
||
| For each shared library (.so / .dylib) on Linux/macOS, this creates: | ||
| libonedal_core.so.{binary_major}.{binary_minor} (real file) | ||
| libonedal_core.so.{binary_major} -> .so.{major}.{minor} (symlink) | ||
| libonedal_core.so -> .so.{major} (symlink) | ||
|
|
||
| Static libraries and Windows DLLs are copied as-is without versioning. | ||
| """ | ||
| lib_prefix = paths.join(prefix, "lib", "intel64") | ||
| libs = _collect_default_files(ctx.attr.lib) | ||
| dst_files = [] | ||
|
|
||
| for lib in libs: | ||
| dst_path = paths.join(lib_prefix, lib.basename) | ||
| dst_file = _copy(ctx, lib, dst_path) | ||
| dst_files.append(dst_file) | ||
| # Determine if this is a shared library that needs versioning. | ||
| # On Linux: .so extension; on macOS: .dylib (handled separately). | ||
| is_shared_lib = lib.extension == "so" | ||
|
|
There was a problem hiding this comment.
_copy_lib docstring says it versions both .so and .dylib, but the implementation only treats .so as a shared library (lib.extension == "so"). Either extend the logic to handle .dylib (macOS) or update the docstring to match current behavior.
| for dep, dst_subpath in zip(ctx.attr.extra_files, ctx.attr.extra_files_dst): | ||
| srcs = dep[DefaultInfo].files.to_list() | ||
| if len(srcs) != 1: | ||
| fail("extra_files entry '{}' must produce exactly one file, got {}".format( | ||
| dep.label, len(srcs))) | ||
| src = srcs[0] | ||
| dst_path = paths.join(prefix, dst_subpath) | ||
| dst_files.append(_copy(ctx, src, dst_path)) | ||
| return dst_files |
There was a problem hiding this comment.
_copy_extra_files iterates with zip(extra_files, extra_files_dst), which silently drops entries if the lists are different lengths. Since these attrs must be 1:1, add an explicit length check and fail with a clear message when they differ.
- Fix binary ABI version: update to MAJORBINARY=3, MINORBINARY=0 to match makefile.ver (was incorrectly set to 2.9) - Fix pkg-config generation: use 'gcc -E -P -x c' instead of 'cpp' directly, as cpp may fail to find cc1plus in some CI environments; add use_default_shell_env=True for portability - Fix SONAME wiring: cc_dynamic_lib rule now reads binary_major from @config//:version (VersionInfo) as fallback when soname_linkopts is not explicitly set by the macro, ensuring SONAME is always in sync with config version without requiring manual binary_major parameter at each call site - Simplify daal_dynamic_lib: remove binary_major parameter since SONAME is now auto-configured inside the rule implementation - Add macOS select branch for vars_mac.sh in generate_vars_sh - Add length check in _copy_extra_files (fail on lists length mismatch)
Test script improvements (release_structure_test.sh): - Use bash globbing instead of ls+grep for finding versioned .so files (more robust, handles edge cases like multiple matches) - Validate symlink targets with readlink: check .so.X → .so.X.Y and .so → .so.X point to correct files - Validate SONAME matches expected value (libonedal_core.so.X) instead of just checking existence - Replace grep -P (PCRE, not portable) with grep -E and bash string ops Release rule improvements (release.bzl): - Add explicit length check in _copy_extra_files: fail with clear message if extra_files and extra_files_dst lists differ in length - Update _copy_lib docstring to reflect Linux-only .so versioning (macOS .dylib not yet implemented) Scripts improvements (scripts.bzl): - Add comment explaining pkg-config.cpp hardcoded version (DAL_MAJOR/MINOR defines passed for future compatibility)
| # limitations under the License. | ||
| #=============================================================================== | ||
| # Tests for Bazel release structure: versioning, symlinks, vars.sh, pkg-config. | ||
| # Run after: bazel build //:release :release_vars_sh :release_pkgconfig |
There was a problem hiding this comment.
The comment on line 18 states the test should run after building three targets (release, release_vars_sh, release_pkgconfig), but the release target already includes release_vars_sh and release_pkgconfig as dependencies via extra_files. Users only need to run 'bazel build //:release' to get all the required outputs. The comment should be updated to reflect this to avoid confusion.
| # Run after: bazel build //:release :release_vars_sh :release_pkgconfig | |
| # Run after: bazel build //:release (which also builds env/vars.sh and pkg-config files) |
| # Use gcc -E -P (C preprocessor) instead of cpp directly. | ||
| # 'cpp' may not find cc1plus in some CI environments; 'gcc -E -P' is more portable. | ||
| ctx.actions.run_shell( | ||
| inputs = [ctx.file.template], | ||
| outputs = [out], | ||
| command = ( | ||
| "gcc -E -P -x c " + |
There was a problem hiding this comment.
The command uses 'gcc -E -P' hardcoded without checking if gcc is available. This could fail on systems where gcc is not installed or not in PATH. Consider using the cc_toolchain to get the C compiler path, or add a comment explaining that gcc is a required dependency for the build system.
| # Use gcc -E -P (C preprocessor) instead of cpp directly. | |
| # 'cpp' may not find cc1plus in some CI environments; 'gcc -E -P' is more portable. | |
| ctx.actions.run_shell( | |
| inputs = [ctx.file.template], | |
| outputs = [out], | |
| command = ( | |
| "gcc -E -P -x c " + | |
| # Use the C preprocessor via the C compiler instead of cpp directly. | |
| # Prefer the CC environment variable (falling back to gcc) because | |
| # 'cpp' may not find cc1plus in some CI environments. | |
| ctx.actions.run_shell( | |
| inputs = [ctx.file.template], | |
| outputs = [out], | |
| command = ( | |
| "${CC:-gcc} -E -P -x c " + |
| libonedal_core.so.{binary_major}.{binary_minor} (real file) | ||
| libonedal_core.so.{binary_major} -> .so.{major}.{minor} (symlink) | ||
| libonedal_core.so -> .so.{major} (symlink) |
There was a problem hiding this comment.
The comment states 'libonedal_core.so.{binary_major} -> .so.{major}.{minor}' but this is incorrect. According to the code on line 127, the major symlink points to the versioned file which has the pattern libonedal_core.so.{binary_major}.{binary_minor}, not .so.{major}.{minor}. The comment should be updated to reflect the actual symlink structure: 'libonedal_core.so.{binary_major} -> libonedal_core.so.{binary_major}.{binary_minor}'.
| libonedal_core.so.{binary_major}.{binary_minor} (real file) | |
| libonedal_core.so.{binary_major} -> .so.{major}.{minor} (symlink) | |
| libonedal_core.so -> .so.{major} (symlink) | |
| libonedal_core.so.{binary_major}.{binary_minor} (real file) | |
| libonedal_core.so.{binary_major} -> libonedal_core.so.{binary_major}.{binary_minor} (symlink) | |
| libonedal_core.so -> libonedal_core.so.{binary_major} (symlink) |
| # If not set by macro, falls back to empty list (no SONAME). | ||
| soname_linkopts = ctx.attr.soname_linkopts | ||
| # If lib_name is set and version info available, build SONAME from config version. | ||
| # This ensures SONAME is always in sync with @config//:version.binary_major. | ||
| if ctx.attr.lib_name and not soname_linkopts and ctx.attr._version_info: | ||
| vi = ctx.attr._version_info[VersionInfo] | ||
| if vi.binary_major: | ||
| soname = "lib{}.so.{}".format(ctx.attr.lib_name, vi.binary_major) | ||
| soname_linkopts = ["-Wl,-soname,{}".format(soname)] |
There was a problem hiding this comment.
The fallback logic on lines 191-197 will always generate a Linux-specific SONAME flag ('-Wl,-soname,...') without platform checking. This could cause build failures on Windows or macOS if the macro doesn't provide soname_linkopts. The fallback should either use the same select() pattern as the macro or be removed entirely to ensure consistent platform handling. Consider removing this fallback and requiring the macro to always set soname_linkopts (even if empty).
| # If not set by macro, falls back to empty list (no SONAME). | |
| soname_linkopts = ctx.attr.soname_linkopts | |
| # If lib_name is set and version info available, build SONAME from config version. | |
| # This ensures SONAME is always in sync with @config//:version.binary_major. | |
| if ctx.attr.lib_name and not soname_linkopts and ctx.attr._version_info: | |
| vi = ctx.attr._version_info[VersionInfo] | |
| if vi.binary_major: | |
| soname = "lib{}.so.{}".format(ctx.attr.lib_name, vi.binary_major) | |
| soname_linkopts = ["-Wl,-soname,{}".format(soname)] | |
| # If not set by macro, this remains empty (no SONAME flags are added here). | |
| soname_linkopts = ctx.attr.soname_linkopts |
| echo "" | ||
| echo "=== SONAME check ===" | ||
|
|
||
| for lib in "${LIB_DIR}"/libonedal_core.so.*.* "${LIB_DIR}"/libonedal.so.*.* ; do |
There was a problem hiding this comment.
The SONAME check loop only validates libonedal_core.so.. and libonedal.so.., but the symlink check on line 42 includes libonedal_thread. If libonedal_thread is expected to be a versioned shared library with symlinks, it should also be included in the SONAME validation. If it's intentionally excluded (e.g., because it's a static library), add a comment explaining why.
| for lib in "${LIB_DIR}"/libonedal_core.so.*.* "${LIB_DIR}"/libonedal.so.*.* ; do | |
| for lib in "${LIB_DIR}"/libonedal_core.so.*.* "${LIB_DIR}"/libonedal.so.*.* "${LIB_DIR}"/libonedal_thread.so.*.* ; do |
| def daal_dynamic_lib(name, lib_tags=["daal"], **kwargs): | ||
| """Build a oneDAL shared library with SONAME automatically set from @config//:version.""" | ||
| cc_dynamic_lib( | ||
| name = name, | ||
| lib_tags = lib_tags, | ||
| **kwargs, | ||
| ) |
There was a problem hiding this comment.
The comment claims SONAME is automatically set from @config//:version, which is true, but this happens through the rule's fallback mechanism (cc.bzl lines 191-197) rather than through the macro's binary_major parameter. For clarity and consistency, daal_dynamic_lib should extract binary_major from VersionInfo and pass it to cc_dynamic_lib explicitly, similar to how dal_dynamic_lib should work. This would make the code flow more explicit and remove reliance on the fallback mechanism.
| # On Linux: .so extension; on macOS: .dylib (handled separately). | ||
| is_shared_lib = lib.extension == "so" | ||
|
|
||
| if is_shared_lib and version_info: |
There was a problem hiding this comment.
If version_info is None (line 117), the function will skip versioning and symlink creation for all .so files, silently copying them as-is like static libraries. This could lead to subtle bugs where libraries are built without proper versioning if the version provider isn't available. Consider failing fast with a clear error message if version_info is None and is_shared_lib is True, rather than silently falling back to unversioned behavior.
| if is_shared_lib and version_info: | |
| if is_shared_lib: | |
| if not version_info: | |
| fail("Shared library '{}' requires VersionInfo for proper SONAME versioning, " | |
| "but no version_info was provided.".format(lib.basename)) |
|
/intelci: run |
Response to Copilot Review CommentsAll 11 review comments addressed across commits 3 and 4: Commit 3: 209d7c9 - Critical fixesBinary ABI version mismatch - Fixed: Updated to MAJORBINARY=3, MINORBINARY=0 SONAME not wired - Fixed: cc_dynamic_lib now auto-reads from @config//:version cpp command fails - Fixed: Changed to gcc -E -P with use_default_shell_env=True macOS vars.sh missing - Fixed: Added select branch for osx Commit 4: 83f9353 - Test improvementsFragile .so finding - Fixed: bash globbing instead of ls+grep Symlinks not validated - Fixed: readlink validation added SONAME not validated - Fixed: Compare against expected value grep -P not portable - Fixed: Replaced with grep -E pkg-config version hardcoded - Documented with comment _copy_lib docstring incorrect - Fixed: Linux-only noted _copy_extra_files length check - Fixed: Added explicit fail Status: All 11 addressed. LinuxBazel CI passing. |
Summary
Closes part of TODO.md:
Release to oneAPI structure — Bazel shall write headers, binaries, examples and scripts to oneAPI release structure as the current make does.Changes
1. Binary ABI version in config (
dev/bazel/config/config.bzl)Added
binary_majorandbinary_minorfields toVersionInfoprovider, separate from product version (2026.x). Matches Make'sMAJORBINARY=2/MINORBINARY=9variables used for SONAME and symlinks.2. Library versioning + symlinks (
dev/bazel/release.bzl)bazel build //:releasenow produces:Uses
ctx.actions.symlink()(hermetic Bazel API, no shell invocation).3. SONAME embedding (
dev/bazel/cc.bzl,dev/bazel/cc/link.bzl)Shared libraries now get
-Wl,-soname,libonedal_core.so.2on Linux, matching Make's behavior. Uses macro+select() pattern (select() evaluated in macro, resolved string list passed to rule via attr).4.
vars.shgeneration (dev/bazel/scripts.bzl)Generates
env/vars.shfromdeploy/local/vars_lnx.shtemplate usingctx.actions.expand_template()with binary version substitutions.5.
pkg-configgeneration (dev/bazel/scripts.bzl)Generates
lib/pkgconfig/onedal.pcfromdeploy/pkg-config/pkg-config.cppviacpp -Ppreprocessor.6. Test script (
dev/bazel/tests/release_structure_test.sh)Shell test validating:
.so.X.Yfiles exist.so.Xand.soare correctreadelf -dvars.shsubstitutions correctonedal.pchas required fieldsNot in this PR
--exclude-libsfor MKL symbol hiding (tracked separately, requires build validation on a machine with MKL installed)vars.bat(after Windows toolchain PR)install_name_tool(separate PR)Testing