Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions dkms.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,24 @@ This optional directive is an array that allows you to specify other modules as
dependencies for your module. Each array element should be the
.B PACKAGE_NAME
of another module that is managed by dkms. Do not specify a version or
architecture in the dependency. Note that this directive is only advisory;
missing or broken dependencies cause non-fatal warnings.
architecture in the dependency. A missing dependency requires the parameter
.OP --force
to still attempt the build of the module.
.TP
.B BUILD_DEPENDS_REBUILD=
If this directive is set to
.B yes
then the module which has
.B BUILD_DEPENDS
being set, will get rebuilt every time a dependency changes version. This is usually provided in a specific
.I /etc/dkms/<module>.conf
on a per installation basis.

To keep track of the dependency change, the version of the module that was used at build time is recorded in the file
.I /var/lib/dkms/<module>/<kernel_version>-<arch>/.dep_<dependency>.
As soon as the module version changes, this is compared and the dependent module is rebuilt. With the parameter
.I --force
the dependency is not tracked.
.TP
.B BUILD_EXCLUSIVE_KERNEL=
This optional directive allows you to specify a regular expression which defines
Expand Down
167 changes: 164 additions & 3 deletions dkms.in
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ readonly dkms_conf_variables="PACKAGE_NAME
built_module_name BUILT_MODULE_LOCATION built_module_location
DEST_MODULE_NAME dest_module_name
DEST_MODULE_LOCATION dest_module_location
STRIP strip AUTOINSTALL NO_WEAK_MODULES
STRIP strip AUTOINSTALL NO_WEAK_MODULES BUILD_DEPENDS_REBUILD

CLEAN
REMAKE_INITRD MODULES_CONF MODULES_CONF_OBSOLETES
Expand Down Expand Up @@ -582,8 +582,7 @@ safe_source() {
# Source a dkms.conf file and perform appropriate postprocessing on it.
# Do our best to not repeatedly source the same .conf file -- this can happen
# when chaining module installation functions or autoinstalling.
read_conf()
{
read_conf() {
# $1 kernel version (required)
# $2 arch (required)
# $3 dkms.conf location (optional)
Expand Down Expand Up @@ -848,6 +847,21 @@ read_conf()
;;
esac

# Check for allowed BUILD_DEPENDS_REBUILD values: "yes", "no", ""
case "$BUILD_DEPENDS_REBUILD" in
"")
;;
[Yy][Ee][Ss])
;;
[Nn][Oo])
BUILD_DEPENDS_REBUILD=""
;;
*)
echo "dkms.conf: Error! Unsupported BUILD_DEPENDS_REBUILD value '$BUILD_DEPENDS_REBUILD'" >&2
return_value=1
;;
esac

((return_value == 0)) && last_mvka="$module/$module_version/$1/$2" && last_mvka_conf="$(readlink -f "$read_conf_file")"
return $return_value
}
Expand Down Expand Up @@ -1542,6 +1556,20 @@ do_build()

# Clean the build directory
rm -rf "${build_dir:?}"

# After successful build, save dependency versions
if [[ $BUILD_DEPENDS_REBUILD ]] && [[ ! $force ]]; then
mkdir -p "$base_dir"
for bd in "${BUILD_DEPENDS[@]}"; do
local dep_version
dep_version=$(module_status "$bd" "*" "$kernelver" "$arch" | while read -r status mvka; do
[[ $status ]] || continue
IFS='/' read -r m v k a <<< "$mvka"
echo "$v"
done | sort -V | tail -n1)
[[ $dep_version ]] && echo "$dep_version" > "$base_dir/.dep_${bd}"
done
fi
}

prepare_kernel_and_signing()
Expand Down Expand Up @@ -1755,6 +1783,9 @@ do_install()

# Restore the status of $force
force="$tmp_force"

# First check and rebuild any modules with updated dependencies
check_and_rebuild_dependent_modules "${kernelver[0]}" "${arch[0]}"
}

# List each kernel object that has been installed for a particular module.
Expand Down Expand Up @@ -2969,6 +3000,136 @@ kernel_prerm()
[[ ! $failed ]] || die 14 "dkms kernel_prerm for kernel ${kernelver[0]} (${arch[0]}) failed for module(s)$failed."
}

# Check if a module's dependencies have been updated
check_dependencies_updated() {

local module=$1
local module_version=$2

# $1 = module
# $2 = module version
# $3 = kernel version
# $4 = arch

local deps_updated=0

# Read the module's configuration
set_module_suffix "$3"
read_conf_strict_or_die "$3" "$4"

# Check each dependency
for bd in "${BUILD_DEPENDS[@]}"; do
# Get the latest version of the dependency
local dep_version
dep_version=$(module_status "$bd" "*" "$3" "$4" | while read -r status mvka; do
[[ $status ]] || continue
IFS='/' read -r m v k a <<< "$mvka"
echo "$v"
done | sort -V | tail -n1)

# If dependency is not installed, skip it
[[ $dep_version ]] || continue

# Check if dependency's version has changed since last build
local dep_version_file="$dkms_tree/$module/$module_version/$3/$4/.dep_${bd}"
if [[ -f "$dep_version_file" ]]; then
local old_version
old_version=$(cat "$dep_version_file")
if [[ "$old_version" != "$dep_version" ]]; then
deps_updated=1
break
fi
else
deps_updated=1
break
fi
done

return $deps_updated
}

# Check and rebuild all modules with updated dependencies
check_and_rebuild_dependent_modules() {

# $1 = kernel version
# $2 = arch

local -a modules_to_rebuild
local -a rebuilt_modules
local progress=1

# First pass: collect all modules that need rebuilding
while read -r status mvka; do
[[ $status ]] || continue
IFS='/' read -r m v k a <<< "$mvka"
# Skip if not built for this kernel/arch
[[ "$k" = "$1" && "$a" = "$2" ]] || continue

# Read module's configuration by passing module and version of the dependent module)
module=$m module_version=$v read_conf_or_die "$1" "$2"

# Skip if no BUILD_DEPENDS, BUILD_DEPENDS_REBUILD is not set or --force is passed
[[ ${#BUILD_DEPENDS[@]} -eq 0 ]] || [[ -z $BUILD_DEPENDS_REBUILD ]] || [[ $force ]] && continue

# Check if dependencies have been updated
if ! check_dependencies_updated "$m" "$v" "$1" "$2"; then
modules_to_rebuild+=("$m/$v")
fi
done <<< "$(module_status)"

# Second pass: rebuild modules in dependency order
while (( progress > 0 )); do
progress=0
local -a next_rebuild

# Remove already rebuilt modules from dependency lists
for mv in "${modules_to_rebuild[@]}"; do
IFS=/ read -r m v <<< "$mv"
module=$m module_version=$v read_conf_or_die "$1" "$2"

# Check if all dependencies are satisfied
# shellcheck disable=SC2034
local can_rebuild=1
for bd in "${BUILD_DEPENDS[@]}"; do
local dep_found=0
for rm in "${rebuilt_modules[@]}"; do
IFS=/ read -r dm <<< "$rm"
[[ "$dm" = "$bd" ]] && dep_found=1 && break
done
if (( ! dep_found )); then
can_rebuild=0
break
fi
done

if (( ! can_rebuild )); then
module=$m module_version=$v
echo ""
echo "Rebuilding module $m/$v due to updated dependencies"
echo ""
do_uninstall "$1" "$2"
do_unbuild "$1" "$2"
if do_build; then
do_install
rebuilt_modules+=("$m/$v")
progress=1
fi
else
next_rebuild+=("$m/$v")
fi
done
modules_to_rebuild=("${next_rebuild[@]}")
done

# Report any modules that couldn't be rebuilt
if (( ${#modules_to_rebuild[@]} > 0 )); then
echo "Warning: The following modules could not be rebuilt due to dependency cycles:"
for mv in "${modules_to_rebuild[@]}"; do
echo " $mv"
done
fi
}

#############################
#### ####
#### Program Starts Here ####
Expand Down
Loading