# wolfssl-commercial.bbclass # # This class provides helper functions for commercial wolfSSL bundles # including password-protected 7z extraction and autogen disabling # # Usage in recipe: # inherit wolfssl-commercial # # Required variables: # COMMERCIAL_BUNDLE_DIR - Directory containing the commercial archive # COMMERCIAL_BUNDLE_NAME - Logical bundle name (used as extracted directory) # COMMERCIAL_BUNDLE_PASS - Password for .7z bundles (optional for .tar.gz) # COMMERCIAL_BUNDLE_SHA - SHA256 checksum of the bundle # COMMERCIAL_BUNDLE_TARGET - Target directory for extraction (usually WORKDIR) # # Example: # COMMERCIAL_BUNDLE_DIR = "${@os.path.dirname(d.getVar('FILE'))}/commercial/files" # COMMERCIAL_BUNDLE_NAME = "${WOLFSSL_SRC}" # COMMERCIAL_BUNDLE_PASS = "${WOLFSSL_SRC_PASS}" # COMMERCIAL_BUNDLE_SHA = "${WOLFSSL_SRC_SHA}" # COMMERCIAL_BUNDLE_TARGET = "${WORKDIR}" # # Helper functions: # get_commercial_src_uri(d) - Generates conditional SRC_URI # get_commercial_source_dir(d) - Generates conditional source directory # get_commercial_bbclassextend(d) - Returns BBCLASSEXTEND only if bundle configured # get_commercial_bundle_archive(d) - Resolves bundle filename (supports .7z and .tar.gz) # # Optional format variables: # COMMERCIAL_BUNDLE_FILE - Bundle filename including extension (defaults to .7z) # COMMERCIAL_BUNDLE_GCS_URI - gs:// path to the protected bundle # COMMERCIAL_BUNDLE_SRC_DIR - Direct path to already-extracted source directory (skips fetch/extract) # Commercial bundles already ship generated configure scripts, so skip autoreconf AUTOTOOLS_AUTORECONF = "no" # Helper functions for conditional commercial bundle configuration def append_libtool_sysroot(d): """Override the default autotools helper to drop --with-libtool-sysroot for commercial bundles.""" if d.getVar('COMMERCIAL_BUNDLE_ENABLED') == "1": return '' import bb if not bb.data.inherits_class('native', d): return '--with-libtool-sysroot=${STAGING_DIR_HOST}' return '' def get_commercial_bundle_archive(d): """Resolve the bundle filename with extension.""" bundle_file = d.getVar('COMMERCIAL_BUNDLE_FILE') if bundle_file and bundle_file.strip() and not bundle_file.startswith('${'): return bundle_file bundle_name = d.getVar('COMMERCIAL_BUNDLE_NAME') if bundle_name and bundle_name.strip() and not bundle_name.startswith('${'): return f'{bundle_name}.7z' return '' def get_commercial_src_uri(d): """Generate SRC_URI for commercial bundle if configured, dummy file otherwise""" # Check for direct source directory first (skip fetch/extract) src_dir = d.getVar('COMMERCIAL_BUNDLE_SRC_DIR') if src_dir and src_dir.strip() and not src_dir.startswith('${'): # Direct source directory - no fetch needed return "" bundle_archive = d.getVar('COMMERCIAL_BUNDLE_ARCHIVE') bundle_sha = d.getVar('COMMERCIAL_BUNDLE_SHA') gcs_uri = d.getVar('COMMERCIAL_BUNDLE_GCS_URI') placeholder = d.getVar('COMMERCIAL_BUNDLE_PLACEHOLDER') or '' if gcs_uri and bundle_archive: unpack_flag = ';unpack=false' if bundle_archive.endswith('.7z') else '' sha_flag = f';sha256sum={bundle_sha}' if bundle_sha else '' filename_flag = f';downloadfilename={bundle_archive}' return f'{gcs_uri}{filename_flag}{unpack_flag}{sha_flag}' bundle_dir = d.getVar('COMMERCIAL_BUNDLE_DIR') if bundle_archive and not gcs_uri: unpack_flag = ';unpack=false' if bundle_archive.endswith('.7z') else '' return f'file://{bundle_dir}/{bundle_archive}{unpack_flag};sha256sum={bundle_sha}' # Return dummy placeholder file when not configured if placeholder: return f'file://{placeholder}' return "" def get_commercial_source_dir(d): """Get source directory for commercial bundle if configured, WORKDIR otherwise""" workdir = d.getVar('WORKDIR') bundle_name = d.getVar('COMMERCIAL_BUNDLE_NAME') # wrynose+ rejects S == bare WORKDIR; fall back to UNPACKDIR where it exists # (the no-bundle parse-time default), keeping WORKDIR on older releases. default_root = d.getVar('UNPACKDIR') or workdir # Check for direct source directory - return the copy location in WORKDIR src_dir = d.getVar('COMMERCIAL_BUNDLE_SRC_DIR') if src_dir and src_dir.strip() and not src_dir.startswith('${'): # do_commercial_extract will copy to WORKDIR/bundle_name if bundle_name and bundle_name.strip() and not bundle_name.startswith('${'): return f'{workdir}/{bundle_name}' # Fallback when bundle_name not set return default_root # Check if bundle_name is actually set (not empty, None, or unexpanded variable) if bundle_name and bundle_name.strip() and not bundle_name.startswith('${'): return f'{workdir}/{bundle_name}' return default_root def get_commercial_bbclassextend(d): """Return BBCLASSEXTEND variants only when commercial bundle is configured""" bundle_name = d.getVar('COMMERCIAL_BUNDLE_NAME') # Check if bundle_name is actually set (not empty, None, or unexpanded variable) if bundle_name and bundle_name.strip() and not bundle_name.startswith('${'): return 'native nativesdk' return '' # Generic variables for commercial bundle extraction COMMERCIAL_BUNDLE_ENABLED ?= "0" COMMERCIAL_BUNDLE_DIR ?= "" COMMERCIAL_BUNDLE_NAME ?= "" COMMERCIAL_BUNDLE_FILE ?= "" COMMERCIAL_BUNDLE_PASS ?= "" COMMERCIAL_BUNDLE_SHA ?= "" COMMERCIAL_BUNDLE_TARGET ?= "${WORKDIR}" COMMERCIAL_BUNDLE_PLACEHOLDER ?= "${WOLFSSL_LAYERDIR}/recipes-wolfssl/wolfssl/commercial/files/README.md" COMMERCIAL_BUNDLE_GCS_URI ?= "" COMMERCIAL_BUNDLE_SRC_DIR ?= "" COMMERCIAL_BUNDLE_ARCHIVE = "${@get_commercial_bundle_archive(d)}" # Auto-detect extracted directory name from WOLFSSL_VERSION if not explicitly set # This handles cases where tarball name differs from extracted directory WOLFSSL_SRC_SUBDIR ?= "${@'wolfssl-' + d.getVar('WOLFSSL_VERSION') if d.getVar('WOLFSSL_VERSION') else d.getVar('WOLFSSL_SRC') or ''}" # Task to extract commercial bundle python do_commercial_extract() { import os import bb import bb.process import bb.build enabled = d.getVar('COMMERCIAL_BUNDLE_ENABLED') src_dir = d.getVar('COMMERCIAL_BUNDLE_SRC_DIR') bundle_dir = d.getVar('COMMERCIAL_BUNDLE_DIR') bundle_archive = d.getVar('COMMERCIAL_BUNDLE_ARCHIVE') bundle_pass = d.getVar('COMMERCIAL_BUNDLE_PASS') target_dir = d.getVar('COMMERCIAL_BUNDLE_TARGET') bundle_sha = d.getVar('COMMERCIAL_BUNDLE_SHA') or '' if enabled != "1": bb.note("COMMERCIAL_BUNDLE_ENABLED=0; skipping commercial extraction (standard fetch/unpack will run).") return # If direct source directory is provided, skip extraction if src_dir and src_dir.strip() and not src_dir.startswith('${'): bb.note(f"COMMERCIAL_BUNDLE_SRC_DIR={src_dir}; copying source directory to WORKDIR.") # Copy source directory to WORKDIR to avoid polluting the original import shutil bundle_name = d.getVar('COMMERCIAL_BUNDLE_NAME') dest_dir = os.path.join(target_dir, bundle_name) if os.path.exists(dest_dir): bb.note(f"Removing existing build directory: {dest_dir}") shutil.rmtree(dest_dir) bb.note(f"Copying {src_dir} to {dest_dir}") shutil.copytree(src_dir, dest_dir, symlinks=True) bb.note("Source directory copied successfully") return if not bundle_dir: bb.fatal("COMMERCIAL_BUNDLE_DIR not set. Please set the directory containing the commercial bundle.") if not bundle_archive: bb.fatal("COMMERCIAL_BUNDLE_NAME/FILE not set. Please provide the bundle filename.") is_seven_zip = bundle_archive.endswith('.7z') is_tarball = bundle_archive.endswith('.tar.gz') or bundle_archive.endswith('.tgz') if is_seven_zip and not bundle_pass: bb.fatal("COMMERCIAL_BUNDLE_PASS not set. Please set bundle password for .7z archives.") if not is_seven_zip: bb.note("Non-7z commercial bundle detected; letting BitBake unpack the archive.") return bundle_path = os.path.join(bundle_dir, bundle_archive) if not os.path.exists(bundle_path): bb.fatal(f"Commercial bundle not found: {bundle_path}\n" + "Please download the commercial bundle and place it in the appropriate directory.\n" + "Contact support@wolfssl.com for access to commercial bundles.") # Verify checksum locally (BitBake's file:// fetcher may skip it) if bundle_sha: import hashlib h = hashlib.sha256() with open(bundle_path, 'rb') as f: for chunk in iter(lambda: f.read(1024 * 1024), b''): h.update(chunk) digest = h.hexdigest() if digest != bundle_sha: bb.fatal(f"SHA256 mismatch for {bundle_archive}:\n" f" expected: {bundle_sha}\n" f" actual: {digest}\n" "Update WOLFSSL_SRC_SHA/COMMERCIAL_BUNDLE_SHA to the correct value.") # Copy bundle to target directory bb.plain(f"Extracting commercial bundle: {bundle_archive}") ret = os.system(f'cp -f "{bundle_path}" "{target_dir}"') if ret != 0: bb.fatal(f"Failed to copy bundle to {target_dir}") archive_in_target = os.path.join(target_dir, bundle_archive) if is_seven_zip: # Locate 7zip binary from native sysroot or host path = d.getVar('PATH') seven_zip = bb.utils.which(path, '7za') or bb.utils.which(path, '7z') if not seven_zip: bb.fatal("Failed to find either '7za' or '7z' in PATH.\n" "Ensure p7zip-native is available or install p7zip on the build host.") cmd = [seven_zip, 'x', archive_in_target, f"-p{bundle_pass}", f"-o{target_dir}", '-aoa'] else: bb.fatal(f"Unsupported commercial bundle format: {bundle_archive}. Expected .7z.") try: bb.process.run(cmd) except bb.process.ExecutionError as exc: bb.fatal("Failed to extract bundle. Check credentials and bundle integrity.\n" + str(exc)) bb.plain("Commercial bundle extracted successfully") } # Add task after fetch, before patch (place before do_patch so it still runs even if do_unpack is skipped) addtask commercial_extract after do_fetch before do_patch # Conditionally add p7zip-native dependency only when commercial bundle variables are set python __anonymous() { enabled = d.getVar('COMMERCIAL_BUNDLE_ENABLED') if not enabled or enabled == '0': wolfssl_src = d.getVar('WOLFSSL_SRC') wolfssl_src_dir = d.getVar('COMMERCIAL_BUNDLE_SRC_DIR') if (wolfssl_src and wolfssl_src.strip()) or (wolfssl_src_dir and wolfssl_src_dir.strip() and not wolfssl_src_dir.startswith('${')): d.setVar('COMMERCIAL_BUNDLE_ENABLED', '1') enabled = '1' bb.debug(1, "COMMERCIAL_BUNDLE_ENABLED auto-detected as 1 based on WOLFSSL_SRC or COMMERCIAL_BUNDLE_SRC_DIR") src_dir = d.getVar('COMMERCIAL_BUNDLE_SRC_DIR') archive = d.getVar('COMMERCIAL_BUNDLE_ARCHIVE') # Skip p7zip and unpack tasks if using direct source directory # But keep commercial_extract to copy the source if enabled == "1" and src_dir and src_dir.strip() and not src_dir.startswith('${'): bb.build.deltask('do_fetch', d) bb.build.deltask('do_unpack', d) # do_commercial_extract will copy the source directory return if enabled == "1" and archive and archive.endswith('.7z'): d.appendVar('DEPENDS', ' p7zip-native') d.appendVarFlag('do_commercial_extract', 'depends', ' p7zip-native:do_populate_sysroot') # Older BitBake releases sometimes ignore 'unpack=false' on file:// URLs, # causing do_unpack to run with no password and wipe the extracted tree. # When we manage a passworded .7z ourselves, skip do_unpack entirely. bb.build.deltask('do_unpack', d) # Commercial bundles ship preconfigured scripts; drop libtool sysroot flag opts = d.getVar('CONFIGUREOPTS') or '' import shlex tokens = shlex.split(opts) tokens = [t for t in tokens if not t.startswith('--with-libtool-sysroot=')] d.setVar('CONFIGUREOPTS', ' '.join(tokens)) d.setVar('CONFIGUREOPT_SYSROOT', '') } # Skip autoreconf for commercial bundles and rely on bundled configure script do_configure() { bbnote "Commercial bundle detected, skipping autoreconf and running bundled configure" # Ensure libtool sysroot option is stripped (not accepted by commercial bundles) unset CONFIGUREOPT_SYSROOT CONFIGUREOPTS="$(echo ${CONFIGUREOPTS} | sed 's/--with-libtool-sysroot=[^ ]*//g')" if [ -e "${S}/configure.ac" ] && [ ! -f "${S}/stamp-h.in" ] && \ grep -q "AC_CONFIG_FILES(\\[stamp-h\\]" "${S}/configure.ac"; then bbnote "stamp-h.in missing; generating stub for preconfigured commercial source" echo "timestamp" > "${S}/stamp-h.in" fi if [ -e "${CONFIGURE_SCRIPT}" ]; then oe_runconf else bbfatal "configure script not found at ${CONFIGURE_SCRIPT}" fi } # Task to create stub autogen.sh for commercial bundles do_commercial_stub_autogen() { if [ "${COMMERCIAL_BUNDLE_ENABLED}" != "1" ]; then bbnote "Commercial bundle disabled; skipping autogen stub." exit 0 fi if [ ! -d "${S}" ]; then bbwarn "Source directory ${S} missing before autogen stub; skipping." exit 0 fi # Commercial bundles are pre-configured and don't need autogen.sh # Create a no-op autogen.sh to prevent automatic execution if [ ! -f ${S}/autogen.sh ]; then echo '#!/bin/sh' > ${S}/autogen.sh echo '# Commercial bundle - pre-configured, no autogen needed' >> ${S}/autogen.sh echo 'exit 0' >> ${S}/autogen.sh chmod +x ${S}/autogen.sh bbplain "Created stub autogen.sh for commercial bundle" else bbplain "Replacing existing autogen.sh with stub for commercial bundle" echo '#!/bin/sh' > ${S}/autogen.sh echo '# Commercial bundle - pre-configured, no autogen needed' >> ${S}/autogen.sh echo 'exit 0' >> ${S}/autogen.sh chmod +x ${S}/autogen.sh fi } # Add task after unpack (or commercial_extract for 7z), before configure addtask commercial_stub_autogen after do_unpack before do_configure