*
* $Id$
*
- * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
- * Copyright (C) 2009, 2010, 2011, 2012, MinGW Project
+ * Written by Keith Marshall <keith@users.osdn.me>
+ * Copyright (C) 2009-2012, 2020, MinGW.org Project
*
*
* Implementation of the package dependency resolver method, of the
#include <string.h>
#include "dmh.h"
+#include "dmhmsgs.h"
#include "debug.h"
#include "pkginfo.h"
while( package != NULL )
{
+ /* Collect any debugging messages, relating to this dependency,
+ * into a single message digest.
+ */
+ DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
+ dmh_control( DMH_BEGIN_DIGEST )
+ );
+
/* We have a valid XML entity, which may identify dependencies;
* check if it includes any "requires" specification...
*/
* we are performing a removal).
*/
DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
- dmh_printf( "%*s%s: schedule installation\n", indent + 2, "",
- selected->GetPropVal( tarname_key, value_unknown )
+ dmh_printf( "%*s%s: schedule installation (flags=0x%08x)\n",
+ indent + 2, "", selected->GetPropVal( tarname_key, value_unknown ),
+ promote( request, ACTION_INSTALL )
)
);
rank = Schedule( promote( request, ACTION_INSTALL ), wanted, rank );
/* Regardless of the action scheduled, we must recursively
* consider further dependencies of the resolved prerequisite;
+ * (but note that attempting to do this would be pointless if
+ * the prerequisite package could not be identified).
+ *
* FIXME: do we need to do this, when performing a removal?
* Right now, I (KDM) don't think so...
*/
- if( (request & ACTION_INSTALL) != 0 )
+ if( (selected != NULL) && ((request & ACTION_INSTALL) != 0) )
ResolveDependencies( selected, rank );
}
const char *requestor = refpkg->GetPropVal( tarname_key, value_unknown );
dmh_control( DMH_BEGIN_DIGEST );
+ dmh_notify( DMH_ERROR, PKGMSG_SPECIFICATION_ERROR );
dmh_notify( DMH_ERROR, "%s: requires...\n", requestor );
for( int i = 0; i < sizeof( key ) / sizeof( char* ); i++ )
if( (ref = dep->GetPropVal( key[i], NULL )) != NULL )
*/
dep = dep->FindNextAssociate( requires_key );
}
+
+ /* Flush out any digest of debugging messages, which may
+ * have been collected, in respect of this dependency.
+ */
+ DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
+ dmh_control( DMH_END_DIGEST )
+ );
+
/* Also consider any dependencies which may be common to
* all releases, or all components, of the current package;
* we do this by walking back through the XML hierarchy,
}
}
+pkgActionItem *pkgActionItem::GetReference( pkgXmlNode *package )
+{
+ /* Method to locate a scheduled action, if any, which relates to
+ * a specified package. Assume that the initial 'this' pointer is
+ * closer to the end, than to the beginning of the current list of
+ * scheduled actions, (which MUST NOT be empty), and...
+ */
+ pkgActionItem *item = this;
+ while( item->next != NULL )
+ /*
+ * ...advance, to locate the very last entry in the list.
+ */
+ item = item->next;
+
+ /* Now, working backward toward the beginning of the list...
+ */
+ while( item != NULL )
+ {
+ /* ...identify a "release" specification associated with
+ * each action item in turn...
+ */
+ pkgXmlNode *ref = item->Selection();
+ if( (ref != NULL) || ((ref = item->Selection( to_remove )) != NULL) )
+ {
+ /* ...convert this to an actual component package, or
+ * full package, reference...
+ */
+ while( ref->IsElementOfType( release_key ) )
+ ref = ref->GetParent();
+
+ /* ...and, if it matches the search target, return it.
+ */
+ if( ref == package ) return item;
+ }
+ /* ...or, when we haven't yet found a matching package,
+ * try the preceding scheduled action item, if any.
+ */
+ item = item->prev;
+ }
+ /* If we fall through to here, then we found no action to be
+ * performed on the specified package; report accordingly.
+ */
+ return NULL;
+}
+
static void dmh_notify_no_match
( const char *name, pkgXmlNode *package, const char *bounds )
{
dmh_control( DMH_END_DIGEST );
}
-void pkgXmlDocument::Schedule( unsigned long action, const char* name )
+pkgActionItem* pkgXmlDocument::Schedule( unsigned long action, const char* name )
{
/* Task scheduler interface; schedules actions to process all
* dependencies for the package specified by "name", honouring
* any appended version bounds specified for the parent.
*/
- char scratch_pad[strlen( name )];
- const char *bounds_specification = get_version_bounds( name );
- if( bounds_specification != NULL )
- {
- /* Separate any version bounds specification from
- * the original package name specification.
- */
- size_t scratch_pad_len = bounds_specification - name;
- name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
- scratch_pad[scratch_pad_len] = '\0';
- }
- pkgXmlNode *release;
- if( (release = FindPackageByName( name )) != NULL )
+ /* We may call this method without any assigned package name,
+ * in which case, we interpret it as a request to return the
+ * list of previously scheduled actions...
+ */
+ if( name != NULL )
{
- /* We found the specification for the named package...
+ /* ...but when a package name is specified, then we
+ * proceed to schedule the specified action on it.
*/
- pkgXmlNode *component = release->FindFirstAssociate( component_key );
- if( component != NULL )
- /*
- * When it is subdivided into component-packages,
- * we need to consider each as a possible candidate
- * for task scheduling.
+ char scratch_pad[strlen( name )];
+ const char *bounds_specification = get_version_bounds( name );
+ if( bounds_specification != NULL )
+ {
+ /* Separate any version bounds specification from
+ * the original package name specification.
*/
- release = component;
+ size_t scratch_pad_len = bounds_specification - name;
+ name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
+ scratch_pad[scratch_pad_len] = '\0';
+ }
- while( release != NULL )
+ pkgXmlNode *release;
+ if( (release = FindPackageByName( name )) != NULL )
{
- /* Within each candidate package or component-package...
+ /* We found the specification for the named package...
*/
- pkgXmlNode *package = release;
- if( (release = release->FindFirstAssociate( release_key )) != NULL )
- {
- /* ...initially assume it is not installed, and that
- * no installable upgrade is available.
- */
- pkgActionItem latest;
- pkgXmlNode *installed = NULL, *upgrade = NULL;
-
- /* Establish the action for which dependency resolution is
- * to be performed; note that this may be promoted to a more
- * inclusive class, during resolution, so we need to reset
- * it for each new dependency which may be encountered.
- */
- request = action;
-
- /* Any action request processed here is, by definition,
- * a request for a primary action; mark it as such.
- */
- action |= ACTION_PRIMARY;
-
- /* When the user has given a version bounds specification,
- * then we must assign appropriate action item requirements.
+ pkgXmlNode *component = release->FindFirstAssociate( component_key );
+ if( component != NULL )
+ /*
+ * When it is subdivided into component-packages,
+ * we need to consider each as a possible candidate
+ * for task scheduling.
*/
- if( bounds_specification != NULL )
- latest.ApplyBounds( release, bounds_specification );
+ release = component;
- /* For each candidate release in turn...
+ while( release != NULL )
+ {
+ /* Within each candidate package or component-package...
*/
- while( release != NULL )
+ pkgXmlNode *package = release;
+ if( (release = release->FindFirstAssociate( release_key )) != NULL )
{
- /* ...inspect it to identify any which is already installed,
- * and also the latest available...
+ /* ...initially assume it is not installed, and that
+ * no installable upgrade is available.
*/
- if( is_installed( release ) )
- /*
- * ...i.e. here we have identified a release
- * which is currently installed...
- */
- latest.SelectPackage( installed = release, to_remove );
+ pkgActionItem latest;
+ pkgXmlNode *installed = NULL, *upgrade = NULL;
- if( latest.SelectIfMostRecentFit( release ) == release )
- /*
- * ...while this is the most recent we have
- * encountered so far.
- */
- upgrade = release;
+ /* Establish the action for which dependency resolution is
+ * to be performed; note that this may be promoted to a more
+ * inclusive class, during resolution, so we need to reset
+ * it for each new dependency which may be encountered.
+ */
+ request = action;
- /* Continue with the next specified release, if any.
+ /* Any action request processed here is, by definition,
+ * a request for a primary action; mark it as such.
*/
- release = release->FindNextAssociate( release_key );
- }
+ action |= ACTION_PRIMARY;
- if( (installed = assert_installed( upgrade, installed )) == NULL )
- {
- /* There is no installed version...
- * therefore, there is nothing to do for any action
- * other than ACTION_INSTALL...
+ /* When the user has given a version bounds specification,
+ * then we must assign appropriate action item requirements.
*/
- if( (action & ACTION_MASK) == ACTION_INSTALL )
+ if( bounds_specification != NULL )
+ latest.ApplyBounds( release, bounds_specification );
+
+ /* For each candidate release in turn...
+ */
+ while( release != NULL )
{
- /*
- * ...in which case, we must recursively resolve
- * any dependencies for the scheduled "upgrade".
- */
- if( latest.Selection() == NULL )
- dmh_notify_no_match( name, package, bounds_specification );
- else
- ResolveDependencies(
- upgrade, Schedule( with_download( action ), latest )
- );
- }
- else
- { /* attempting ACTION_UPGRADE or ACTION_REMOVE
- * is an error; diagnose it.
+ /* ...inspect it to identify any which is already installed,
+ * and also the latest available...
*/
- if( component == NULL )
+ if( is_installed( release ) )
/*
- * In this case, the user explicitly specified a single
- * package component, so it's a simple error...
+ * ...i.e. here we have identified a release
+ * which is currently installed...
*/
- dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
- action_name( action & ACTION_MASK ), name
- );
- else
- {
- /* ...but here, the user specified only the package name,
- * which implicitly applies to all associated components;
- * since some may be installed, prefer to issue a warning
- * in respect of any which aren't.
+ latest.SelectPackage( installed = release, to_remove );
+
+ if( latest.SelectIfMostRecentFit( release ) == release )
+ /*
+ * ...while this is the most recent we have
+ * encountered so far.
*/
- const char *extname = component->GetPropVal( class_key, "" );
- char full_package_name[2 + strlen( name ) + strlen( extname )];
- sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+ upgrade = release;
- dmh_control( DMH_BEGIN_DIGEST );
- dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
- extname = action_name( action & ACTION_MASK ), full_package_name
- );
- dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
- full_package_name
- );
- dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
- full_package_name
- );
- dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
- full_package_name
- );
- dmh_control( DMH_END_DIGEST );
- }
- }
- }
- else if( upgrade && (upgrade != installed) )
- {
- /* There is an installed version, but an upgrade to a newer
- * version is available; when performing ACTION_UPGRADE...
- */
- if( (action & ACTION_MASK) == ACTION_UPGRADE )
- /*
- * ...we must recursively resolve any dependencies...
+ /* Continue with the next specified release, if any.
*/
- ResolveDependencies( upgrade,
- Schedule( with_download( action ), latest )
- );
+ release = release->FindNextAssociate( release_key );
+ }
- else if( (action & ACTION_MASK) == ACTION_REMOVE )
+ if( (installed = assert_installed( upgrade, installed )) == NULL )
{
- /* ...while for ACTION_REMOVE, we have little to do,
- * beyond scheduling the removal; (we don't extend the
- * scope of a remove request to prerequisite packages,
- * so there is no need to resolve dependencies)...
+ /* There is no installed version...
+ * therefore, there is nothing to do for any action
+ * other than ACTION_INSTALL...
*/
- latest.SelectPackage( installed );
- Schedule( action, latest );
- }
- else
- { /* ...but, we decline to proceed with ACTION_INSTALL
- * unless the --reinstall option is enabled...
- */
- if( pkgOptions()->Test( OPTION_REINSTALL ) )
+ if( (action & ACTION_MASK) == ACTION_INSTALL )
{
- /* ...in which case, we resolve dependencies for,
- * and reschedule a reinstallation of the currently
- * installed version...
+ /*
+ * ...in which case, we must recursively resolve
+ * any dependencies for the scheduled "upgrade".
*/
- latest.SelectPackage( installed );
- ResolveDependencies( installed,
- Schedule( with_download( action | ACTION_REMOVE ), latest )
- );
+ if( latest.Selection() == NULL )
+ dmh_notify_no_match( name, package, bounds_specification );
+ else
+ ResolveDependencies(
+ upgrade, Schedule( with_download( action ), latest )
+ );
}
else
- { /* ...otherwise, we reformulate the appropriate
- * fully qualified package name...
+ { /* attempting ACTION_UPGRADE or ACTION_REMOVE
+ * is an error; diagnose it.
*/
- const char *extname = ( component != NULL )
- ? component->GetPropVal( class_key, "" )
- : "";
- char full_package_name[2 + strlen( name ) + strlen( extname )];
- sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+ if( component == NULL )
+ /*
+ * In this case, the user explicitly specified a single
+ * package component, so it's a simple error...
+ */
+ dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
+ action_name( action & ACTION_MASK ), name
+ );
+ else
+ {
+ /* ...but here, the user specified only the package name,
+ * which implicitly applies to all associated components;
+ * since some may be installed, prefer to issue a warning
+ * in respect of any which aren't.
+ */
+ const char *extname = component->GetPropVal( class_key, "" );
+ char full_package_name[2 + strlen( name ) + strlen( extname )];
+ sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+
+ dmh_control( DMH_BEGIN_DIGEST );
+ dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
+ extname = action_name( action & ACTION_MASK ), full_package_name
+ );
+ dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
+ full_package_name
+ );
+ dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
+ full_package_name
+ );
+ dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
+ full_package_name
+ );
+ dmh_control( DMH_END_DIGEST );
+ }
+ }
+ }
+ else if( upgrade && (upgrade != installed) )
+ {
+ /* There is an installed version, but an upgrade to a newer
+ * version is available; when performing ACTION_UPGRADE...
+ */
+ if( (action & ACTION_MASK) == ACTION_UPGRADE )
/*
- * ...which we then incorporate into an advisory
- * diagnostic message, which serves both to inform
- * the user of this error condition, and also to
- * suggest appropriate corrective action.
+ * ...we must recursively resolve any dependencies...
*/
- dmh_control( DMH_BEGIN_DIGEST );
- dmh_notify( DMH_ERROR, "%s: package is already installed\n",
- full_package_name
- );
- dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
- full_package_name
- );
- dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
- full_package_name
+ ResolveDependencies( upgrade,
+ Schedule( with_download( action ), latest )
);
- dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n"
- );
- dmh_control( DMH_END_DIGEST );
+
+ else if( (action & ACTION_MASK) == ACTION_REMOVE )
+ {
+ /* ...while for ACTION_REMOVE, we have little to do,
+ * beyond scheduling the removal; (we don't extend the
+ * scope of a remove request to prerequisite packages,
+ * so there is no need to resolve dependencies)...
+ */
+ latest.SelectPackage( installed );
+ Schedule( action, latest );
+ }
+ else
+ { /* ...but, we decline to proceed with ACTION_INSTALL
+ * unless the --reinstall option is enabled...
+ */
+ if( pkgOptions()->Test( OPTION_REINSTALL ) )
+ {
+ /* ...in which case, we resolve dependencies for,
+ * and reschedule a reinstallation of the currently
+ * installed version...
+ */
+ latest.SelectPackage( installed );
+ ResolveDependencies( installed,
+ Schedule( with_download( action | ACTION_REMOVE ), latest )
+ );
+ }
+ else
+ { /* ...otherwise, we reformulate the appropriate
+ * fully qualified package name...
+ */
+ const char *extname = ( component != NULL )
+ ? component->GetPropVal( class_key, "" )
+ : "";
+ char full_package_name[2 + strlen( name ) + strlen( extname )];
+ sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
+ /*
+ * ...which we then incorporate into an advisory
+ * diagnostic message, which serves both to inform
+ * the user of this error condition, and also to
+ * suggest appropriate corrective action.
+ */
+ dmh_control( DMH_BEGIN_DIGEST );
+ dmh_notify( DMH_ERROR, "%s: package is already installed\n",
+ full_package_name
+ );
+ dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
+ full_package_name
+ );
+ dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
+ full_package_name
+ );
+ dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n"
+ );
+ dmh_control( DMH_END_DIGEST );
+ }
}
}
- }
- else
- { /* In this case, the package is already installed,
- * and no more recent release is available; we still
- * recursively resolve its dependencies, to capture
- * any potential upgrades for them.
- */
- if( latest.Selection() == NULL )
- dmh_notify_no_match( name, package, bounds_specification );
else
- ResolveDependencies( upgrade, Schedule( action, latest ));
+ { /* In this case, the package is already installed,
+ * and no more recent release is available; we still
+ * recursively resolve its dependencies, to capture
+ * any potential upgrades for them.
+ */
+ if( latest.Selection() == NULL )
+ dmh_notify_no_match( name, package, bounds_specification );
+ else
+ ResolveDependencies( upgrade, Schedule( action, latest ));
+ }
}
+ if( (component != NULL)
+ && ((component = component->FindNextAssociate( component_key )) != NULL) )
+ /*
+ * When evaluating a component-package, we extend our
+ * evaluation, to consider for any further components of
+ * the current package.
+ */
+ release = component;
}
-
- if( (component = component->FindNextAssociate( component_key )) != NULL )
- /*
- * When evaluating a component-package, we extend our
- * evaluation, to consider for any further components of
- * the current package.
- */
- release = component;
}
- }
- else
- /* We found no information on the requested package;
- * diagnose as a non-fatal error.
- */
- dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
+ else
+ /* We found no information on the requested package;
+ * diagnose as a non-fatal error.
+ */
+ dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
+ }
+ /* Finally, we return a pointer to the currently scheduled
+ * actions list, if any.
+ */
+ return actions;
}
void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )