apm_dependency_check_new (public)

 apm_dependency_check_new -repository_array repository_array \
    -package_keys package_keys

Defined in packages/acs-tcl/tcl/apm-install-procs.tcl

Checks dependencies and finds out which packages are required to install the requested packages. In case some packages cannot be installed due to failed dependencies, it returns which packages out of the requested can be installed, and which packages, either originally requested or required by those, could not be installed, and why.

Switches:
-repository_array (required)
Name of an array in the caller's namespace containing the repository of available packages as returned by apm_get_package_repository.
-package_keys (required)
The list of package_keys of the packages requested to be installed.
Returns:
An array list with the following elements:
  • status: 'ok' or 'failed'.
  • install: If status is 'ok', this is the complete list of packages that need to be installed, in the order in which they need to be installed. If status is 'failed', the list of packages that can be installed.
  • failed: If status is 'failed', an array list keyed by package_key of 2-tuples of (required-uri, required-version) of requirements that could not be satisfied.
  • packages: The list of package_keys of the packages touched upon, either because they were originally requested, or because they were required. If status is 'ok', will be identical to 'install'.
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 packages/acs-admin/www/apm/packages-install-2.tcl packages/acs-admin/ www/apm/packages-install-2.tcl apm_dependency_check_new apm_dependency_check_new packages/acs-admin/www/apm/packages-install-2.tcl->apm_dependency_check_new packages/acs-admin/www/install/install-2.tcl packages/acs-admin/ www/install/install-2.tcl packages/acs-admin/www/install/install-2.tcl->apm_dependency_check_new apm_get_installed_provides apm_get_installed_provides (public) apm_dependency_check_new->apm_get_installed_provides apm_version_names_compare apm_version_names_compare (public) apm_dependency_check_new->apm_version_names_compare

Testcases:
No testcase defined.
Source code:
    upvar 1 $repository_array repository

    array set result {
        status failed
        install {}
        failed {}
        packages {}
    }

    # 'pending_packages' is an array keyed by package_key with a value of 1 for each package pending installation
    # When dependencies have been met, the entry will be unset
    array set pending_packages [list]
    foreach package_key $package_keys {
        set pending_packages($package_key) 1
    }

    # 'installed_packages' is an array keyed by package_key with a value of 1 for each package
    # whose dependencies have been met and is ready to be installed
    array set installed_packages [list]

    # 'provided' will keep track of what we've provided with the currently installed packages
    # combined with the packages which we're already able to install
    apm_get_installed_provides -array provided

    # 'required' will keep track of unsatisfied dependencies
    # keyed by (service-uri) and will contain the largest version number required
    array set required [list]

    # 'required_by' will keep track of unsatisfied dependencies
    # keyed by (service-uri) and will contain the largest version number required
    array set required_by [list]

    # Just to get us started
    set updated_p 1

    ns_log notice "apm_dependency_check_new: STARTING DEPENDENCY CHECK [array names pending_packages]"

    # Outer loop tries to find a package from the repository to add if
    # we're stuck because of unsatisfied dependencies
    while { $updated_p } {

        # Keep looping over pending_package_keys, trying to add packages
        # So long as we've added another, try looping again, as there may be cross-dependencies
        while { $updated_p && [array size pending_packages] > 0 } {
            set updated_p 0

            # Try to add a package from
            foreach package_key [array names pending_packages] {

                if {![info exists repository($package_key)]} continue

                set version $repository($package_key)

                set satisfied_p 1
                foreach req [concat  [dict get $version embeds]  [dict get $version extends]  [dict get $version requires]] {
                    lassign $req req_uri req_version

                    if { ![info exists provided($req_uri)]
                         || [apm_version_names_compare $provided($req_uri) $req_version] == -1 } {

                        ns_log Debug "apm_dependency_check_new: $package_key embeds, extends or requires $req_uri $req_version => failed"

                        set satisfied_p 0

                        # Mark this as a requirement
                        if { ![info exists required($req_uri)]
                             || [apm_version_names_compare $required($req_uri) $req_version] == -1 } {
                            set required($req_uri$req_version
                        }
                    } else {
                        ns_log Debug "apm_dependency_check_new: $package_key embeds, extends or requires $req_uri $req_version => OK"
                    }
                }

                if { $satisfied_p } {
                    # Record as set to go
                    set installed_packages($package_key) 1

                    # Remove from pending list
                    unset pending_packages($package_key)

                    # Add to install-list, as this is important for ordering the installation of packages correctly
                    lappend result(install) $package_key

                    # Add to list of packages touched
                    lappend result(packages) $package_key

                    # Record what this package provides, and remove it from the required list, if appropriate
                    foreach prov [dict get $version provides] {
                        lassign $prov prov_uri prov_version
                        # If what we provide is not already provided, or the alredady provided version is
                        # less than what we provide, record this new provision
                        if { ![info exists provided($prov_uri)]
                             || [apm_version_names_compare $provided($prov_uri) $prov_version] == -1
                         } {
                            set provided($prov_uri$prov_version
                        }
                        # If what we provide is required, and the required version is less than what we provide,
                        # drop the requirement
                        if { [info exists required($prov_uri)]
                             && [apm_version_names_compare $required($prov_uri) $prov_version] <= 0
                         } {
                            unset required($prov_uri)
                        }
                    }

                    # Another package has been added, so repeat
                    set updated_p 1
                }
            }
        }

        # Inner loop completed. Either we're done, or there are packages that have dependencies
        # not currently on the pending_package_keys list.

        set updated_p 0

        if { [array size pending_packages] > 0 } {
            # There are packages that have unsatisfied dependencies
            # Those unmet requirements will be registered in the 'required' array

            # Let's find a package which satisfies at least one of the requirements in 'required'

            foreach package_key [array names repository] {
                if { [info exists pending_packages($package_key)]
                     || [info exists installed_packages($package_key)] } {
                    # Packages already on the pending list, or already verified ok won't help us any
                    continue
                }

                if {![info exists repository($package_key)]} {
                    ns_log notice "package $package_key is apparently missing"
                    set pending_packages($package_key) 1
                    set updated_p 1
                    break
                }

                set version $repository($package_key)

                ns_log Debug "apm_dependency_check_new: Considering $package_key: $version"

                # Let's see if this package provides anything we need
                foreach prov [dict get $version provides] {
                    lassign $prov prov_uri prov_version

                    if { [info exists required($prov_uri)]
                         && [apm_version_names_compare $required($prov_uri) $prov_version] <= 0
                     } {
                        ns_log Debug "apm_dependency_check_new: Adding $package_key, as it provides $prov_uri $prov_version"

                        # If this package provides something that's required in a version high enough
                        # add it to the pending list
                        set pending_packages($package_key) 1

                        # We've changed something
                        set updated_p 1

                        # Let's try for another go at installing packages
                        break
                    }
                }

                # Break all the way back to installing pending packages again
                if { $updated_p } {
                    break
                }
            }
        }
    }

    if { [array size pending_packages] == 0 } {
        set result(status) ok
    } else {
        set result(status) failed

        array set failed [list]

        # There were problems, now be helpful

        # Find out which packages couldn't be installed and why
        foreach package_key [array names pending_packages] {

            # Add to touched packages
            lappend result(packages) $package_key

            if {![info exists repository($package_key)]} {
                lappend failed($package_key) [list Unknown "package $package_key"]
                continue
            }

            set version $repository($package_key)

            # Find unsatisfied requirements
            foreach req [concat  [dict get $version embeds]  [dict get $version extends]  [dict get $version requires]] {
                lassign $req req_uri req_version
                if { ![info exists provided($req_uri)]
                     || [apm_version_names_compare $provided($req_uri) $req_version] == -1 } {
                    lappend failed($package_key) [list $req_uri $req_version]
                    if { [info exists provided($req_uri)] } {
                        ns_log Debug "apm_dependency_check_new: Failed dependency: $package_key embeds/extends/requires $req_uri $req_version, but we only provide $provided($req_uri)"
                    } else {
                        ns_log Debug "apm_dependency_check_new: Failed dependency: $package_key embeds/extends/requires $req_uri $req_version, but we don't have it"
                    }
                }
            }
        }

        set result(failed) [array get failed]
    }

    return [array get result]
Generic XQL file:
packages/acs-tcl/tcl/apm-install-procs.xql

PostgreSQL XQL file:
packages/acs-tcl/tcl/apm-install-procs-postgresql.xql

Oracle XQL file:
packages/acs-tcl/tcl/apm-install-procs-oracle.xql

[ hide source ] | [ make this the default ]
Show another procedure: