6 * Written by Keith Marshall <keith@users.osdn.me>
7 * Copyright (C) 2009-2012, 2020, MinGW.org Project
10 * Implementation of the package dependency resolver method, of the
11 * "pkgXmlDocument" class; includes the interface to the action item
12 * task scheduler, which is called to ensure that processing for any
13 * identified prerequisite packages is appropriately scheduled.
16 * This is free software. Permission is granted to copy, modify and
17 * redistribute this software, under the provisions of the GNU General
18 * Public License, Version 3, (or, at your option, any later version),
19 * as published by the Free Software Foundation; see the file COPYING
20 * for licensing details.
22 * Note, in particular, that this software is provided "as is", in the
23 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
24 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
25 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
26 * MinGW Project, accept liability for any damages, however caused,
27 * arising from the use of this software.
42 /* Define supplementary action codes, which may be used exclusively
43 * by pkgXmlDocument::ResolveDependencies(), to ensure that recursive
44 * actions are appropriately scheduled; these apply to user specified
45 * actions "upgrade --recursive", "install --recursive --reinstall",
46 * and "upgrade --recursive --reinstall" respectively.
48 #define ACTION_RECURSIVE_UPGRADE (ACTION_UPGRADE | OPTION_RECURSIVE)
49 #define ACTION_RECURSIVE_REINSTALL (ACTION_INSTALL | OPTION_ALL_DEPS)
50 #define ACTION_RECURSIVE_REPLACE (ACTION_UPGRADE | OPTION_ALL_DEPS)
52 /* FIXME: the following declaration belongs in a future "pkgmsgs.h"
53 * header file, with the function implementation in a separate C or C++
54 * messages source file, with appropriate internationalisation...
56 EXTERN_C const char *pkgMsgUnknownPackage( void );
58 const char *pkgMsgUnknownPackage( void )
60 /* FIXME: (see note above); return English language only, for now.
62 return "%s: unknown package\n";
65 /* Provide a convenience macro for declaring static inline functions
66 * which we want to always expand inline.
68 #define STATIC_INLINE static inline __attribute__((__always_inline__))
70 static bool is_installed( pkgXmlNode *release )
72 /* Helper to check installation status of a specified package release.
76 * First, check for any 'installed' attribute which may have been
77 * explicitly specified for the 'release' record...
79 if( (status = release->GetPropVal( installed_key, NULL )) != NULL )
81 * ...immediately returning the status deduced from it,
84 return (strcmp( status, yes_value ) == 0);
86 /* When the package definition itself doesn't bear an explicit
87 * 'installed' attribute, then we must check the system map for
88 * an associated installation record...
91 if( ((pkgname = release->GetPropVal( tarname_key, NULL )) != NULL)
92 && (release->GetInstallationRecord( pkgname ) != NULL) )
94 /* ...and, when one is found, we can mark this package with
95 * an explicit status attribute for future reference, before
96 * we return confirmation of the installed status...
98 release->SetAttribute( installed_key, yes_value );
102 /* If we get to here, we may deduce that the package is not
103 * installed; once again, we set the explicit attribute value
104 * to convey this for future reference, before returning the
105 * appropriate status flag.
107 release->SetAttribute( installed_key, no_value );
111 pkgXmlNode *pkgXmlNode::GetInstallationRecord( const char *pkgname )
113 /* Retrieve the installation record, if any, for the package
114 * specified by fully qualified canonical 'pkgname'.
116 * First, break down the specified package name, and retrieve
117 * the sysroot database entry for its associated subsystem.
120 pkgSpecs lookup( pkgname );
121 if( (sysroot = GetSysRoot( lookup.GetSubSystemName() )) != NULL )
123 /* We successfully retrieved a sysroot entry; now we must
124 * search the associated list of installed packages, for one
125 * with the appropriate canonical package name.
127 pkgXmlNode *pkg = sysroot->FindFirstAssociate( installed_key );
130 /* We found an installed package entry; check if it has
131 * the correct canonical name...
133 const char *installed = pkg->GetPropVal( tarname_key, NULL );
134 if( (installed != NULL) && (strcmp( installed, pkgname ) == 0) )
136 * ...returning this entry if so...
140 /* ...otherwise, move on to the next entry, if any.
142 pkg = pkg->FindNextAssociate( installed_key );
146 /* If we get to here, we didn't find an entry for the required
147 * package; return NULL, indicating that it is not installed.
152 const char *pkgXmlNode::GetContainerAttribute( const char *key, const char *sub )
154 /* Walk the XML path from current element, back towards the document root,
155 * until we find the innermost element which has an attribute matching "key";
156 * if such an element is found, return the value of the attribute; if we have
157 * traversed the entire path, all the way to the document root, and we have
158 * not found any element with the "key" attribute, return "sub".
160 pkgXmlNode *pkg = this;
161 pkgXmlNode *root = pkg->GetDocumentRoot();
164 /* We haven't yet tried to search beyond the document root;
165 * try matching "key" to an attribute of the current element...
167 const char *retval = pkg->GetPropVal( key, NULL );
170 * ...returning its value, if such an attribute is found...
175 * take a further step back towards the document root.
177 pkg = (pkg == root) ? NULL : pkg->GetParent();
180 /* If we get to here, then no element with the required "key"
181 * attribute could be found; substitute the specified default.
186 DEBUG_INVOKED static int indent = -1;
188 DEBUG_INVOKED static void
189 DEBUG_INVOKED show_required( pkgSpecs *req )
191 DEBUG_INVOKED const char *tarname = NULL;
192 DEBUG_INVOKED dmh_printf( "%*s require: %s\n", indent, "", req->GetTarName( tarname ) );
193 DEBUG_INVOKED free( (void *)(tarname) );
197 bool is_abi_compatible( pkgSpecs *refdata, const char *version )
199 /* Local helper, used by pkgXmlDocument::ResolveDependencies(),
200 * to confirm that the ABI identification number of a selected
201 * component package is an exact match to a requirement spec.
203 const char *ref_version;
204 if( (ref_version = refdata->GetComponentVersion()) == NULL )
206 * Here, confirm that both are unversioned...
208 return (version == NULL);
210 /* ...otherwise, fall through to check that both bear IDENTICALLY
211 * the same ABI version number.
213 return ((version != NULL) && (strcmp( version, ref_version ) == 0));
216 STATIC_INLINE unsigned long action_class
217 ( unsigned long requested, unsigned long viable )
219 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
220 * it classifies each requested action with respect to any previously
221 * installed version of each package, to ensure that reinstallation
222 * and recursive requests are dispatched appropriately.
224 return viable ? requested : ACTION_RECURSIVE_UPGRADE;
227 STATIC_INLINE unsigned long with_request_flags( unsigned long request )
229 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
230 * it isolates the request flags from the action code, to accommodate
231 * promotion of an alternative action with matching flags.
233 return request & ~(ACTION_MASK | ACTION_DOWNLOAD);
236 STATIC_INLINE unsigned long with_download( unsigned long action_code )
238 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
239 * it adds the download request flag,when promoting any action which
240 * may require a package archive to be downloaded.
242 return action_code | (ACTION_DOWNLOAD);
245 STATIC_INLINE unsigned long promote
246 ( unsigned long request, unsigned long action_code )
248 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
249 * it promotes an alternative action to that explicitly requested by
250 * the user, when this is necessary to satisfy a dependency.
252 return with_request_flags( request ) | with_download( action_code );
256 pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
258 /* For the specified "package", (nominally a "release"), identify its
259 * prerequisites, (as specified by "requires" tags), and schedule actions
260 * to process them; repeat recursively, to identify further dependencies
261 * of such prerequisites, and finally, extend the search to capture
262 * additional dependencies common to the containing package group.
264 pkgSpecs *refdata = NULL;
265 pkgXmlNode *refpkg = package;
267 DEBUG_INVOKED ++indent;
269 /* Capture the state of global option settings controlling the scope
270 * of recursive behaviour and reinstallation requests, so that we may
271 * implicitly extend the effect when processing virtual packages...
273 int request_mode = pkgOptions()->Test( OPTION_ALL_DEPS );
274 if( match_if_explicit( package->ArchiveName(), value_none ) )
276 * ...such that the effect of an upgrade or a reinstall implicitly
277 * applies, through a single level of recursion, to the first level
278 * of requisite dependencies.
280 request_mode |= OPTION_RECURSIVE;
281 request_mode |= request & ACTION_MASK;
283 while( package != NULL )
285 /* Collect any debugging messages, relating to this dependency,
286 * into a single message digest.
288 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
289 dmh_control( DMH_BEGIN_DIGEST )
292 /* We have a valid XML entity, which may identify dependencies;
293 * check if it includes any "requires" specification...
295 pkgXmlNode *dep = package->FindFirstAssociate( requires_key );
298 /* We found a dependency specification...
299 * Initially, assume this package is not installed.
301 pkgXmlNode *installed = NULL;
302 unsigned installed_is_viable = 0;
304 /* To facilitate resolution of "%" version matching wildcards
305 * in the requirements specification, we need to parse the version
306 * specification for the current dependent package...
308 if( refdata == NULL )
310 /* ...but we deferred that until we knew for sure that it would
311 * be needed; it is, so parse it now.
314 if( (refname = refpkg->GetPropVal( tarname_key, NULL )) != NULL )
316 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
317 dmh_printf( "%*s%s: resolve dependencies\n", indent, "", refname )
319 refdata = new pkgSpecs( refname );
323 /* Identify the prerequisite package, from its canonical name...
325 pkgActionItem wanted; pkgXmlNode *selected;
326 pkgSpecs req( wanted.SetRequirements( dep, refdata ) );
327 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
328 show_required( &req )
331 * (Both the package name, and subsystem if specified, must match)...
333 selected = FindPackageByName( req.GetPackageName(), req.GetSubSystemName() );
335 /* When we've identified the appropriate package...
337 if( selected != NULL )
339 /* ...and, more significantly, the appropriate component package,
340 * where applicable...
342 pkgXmlNode *component; const char *reqclass;
343 if( (reqclass = req.GetComponentClass()) == NULL )
344 reqclass = value_unknown;
346 if( (component = selected->FindFirstAssociate( component_key )) == NULL )
348 * ...but if no separate component package exists,
349 * consider the parent package itself, as sole component.
351 component = selected;
353 /* At this point, we have no more than a tentative package selection;
354 * it may not provide a component to fit the requirements specification.
355 * Thus, kill the selection, pending reaffirmation...
358 while( component != NULL )
360 /* ...by stepping through the "releases" of this component package...
362 pkgXmlNode *required = component->FindFirstAssociate( release_key );
363 while( required != NULL )
365 /* ...noting if we find one already marked as "installed"...
367 const char *tstclass;
368 DEBUG_INVOKED const char *already_installed = "";
369 pkgSpecs tst( tstclass = required->GetPropVal( tarname_key, NULL ) );
370 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
371 dmh_printf( "%*s considering: %s", indent, "", tstclass )
373 if( (tstclass = tst.GetComponentClass()) == NULL )
374 tstclass = value_unknown;
376 if( is_installed( required ) && (strcmp( tstclass, reqclass ) == 0)
378 * We found an installed version of the requisite component,
379 * but we ignore it unless it is ABI version compatible with
380 * the version we need; (the intent of ABI versioning is to
381 * accommodate multiple concurrent installations of shared
382 * objects supporting the differing ABI specifications).
384 && is_abi_compatible( &tst, req.GetComponentVersion() ) )
386 installed = required;
387 DEBUG_INVOKED already_installed = " (already installed)";
389 /* ...and identify the most suitable candidate "release"
390 * to satisfy the current dependency...
392 if( wanted.SelectIfMostRecentFit( required ) == required )
393 selected = component = required;
395 if( required == installed )
396 installed_is_viable = wanted.HasAttribute( ACTION_MAY_SELECT );
398 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
399 dmh_printf( "%s%s\n", wanted.HasAttribute( ACTION_MAY_SELECT )
400 ? ": viable candidate"
406 /* ...continuing, until all available "releases"
407 * have been evaluated accordingly.
409 required = required->FindNextAssociate( release_key );
412 /* Where multiple component packages do exist,
413 * continue until all have been inspected.
415 component = component->FindNextAssociate( component_key );
418 /* We have now identified the most suitable candidate package,
419 * to resolve the current dependency...
423 /* ...this package is already installed, so we may schedule
424 * a resolved dependency match, with no pending action...
426 unsigned long fallback = with_request_flags( request );
427 switch( action_class( request_mode, installed_is_viable ) )
431 case ACTION_RECURSIVE_REINSTALL:
433 * ...when the action is "install", with "--reinstall"
434 * and "--recursive" options in effect, we update the
435 * package selection to favour the already installed
436 * version over any available upgrade...
438 wanted.SelectPackage( selected = installed );
440 * ...falling through to...
443 case ACTION_RECURSIVE_REPLACE:
444 case ACTION_RECURSIVE_UPGRADE:
446 * ...schedule removal of the already installed version,
447 * for replacement by a fresh copy of the same version,
448 * or an upgraded version, as appropriate.
450 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
451 dmh_printf( "%*s%s: schedule replacement\n", indent + 2, "",
452 installed->GetPropVal( tarname_key, value_unknown )
455 wanted.SelectPackage( installed, to_remove );
456 fallback |= with_download( ACTION_UPGRADE );
460 /* In any other case, the currently installed version is
461 * to be left in place; we must ensure that its dependencies,
462 * (if any), will be resolved with respect to this already
465 wanted.SelectPackage( selected = installed );
468 /* Schedule the appropriate fallback action, (which may be none),
469 * for the already installed package.
471 rank = Schedule( fallback, wanted, rank );
475 /* FIXME: this change in logic may introduce a regression; I (KDM)
476 * may need to re-evaluate the conceptual effect of PRIMARY actions
477 * vs. SECONDARY actions in this context, but this pre-v0.5 logic
478 * breaks the handling of meta-packages in v0.5
480 else if( ((request & ACTION_MASK) == ACTION_INSTALL)
482 * The required package is not installed, so when
483 * we are performing an installation, ...
485 || ((request & (ACTION_PRIMARY | ACTION_INSTALL)) == ACTION_INSTALL) )
487 else if( (request & ACTION_INSTALL) == ACTION_INSTALL )
489 /* ...or when this is a new requirement of a package
490 * which is being upgraded, then we must schedule it
491 * for installation now; (we may simply ignore it, if
492 * we are performing a removal).
494 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
495 dmh_printf( "%*s%s: schedule installation (flags=0x%08x)\n",
496 indent + 2, "", selected->GetPropVal( tarname_key, value_unknown ),
497 promote( request, ACTION_INSTALL )
500 rank = Schedule( promote( request, ACTION_INSTALL ), wanted, rank );
503 /* Regardless of the action scheduled, we must recursively
504 * consider further dependencies of the resolved prerequisite;
505 * (but note that attempting to do this would be pointless if
506 * the prerequisite package could not be identified).
508 * FIXME: do we need to do this, when performing a removal?
509 * Right now, I (KDM) don't think so...
511 if( (selected != NULL) && ((request & ACTION_INSTALL) != 0) )
512 ResolveDependencies( selected, rank );
515 if( selected == NULL )
517 /* No package matching the selection criteria could be found;
518 * report a dependency resolution failure in respect of each
519 * specified criterion...
521 const char *ref, *key[] = { lt_key, le_key, eq_key, ge_key, gt_key };
522 const char *requestor = refpkg->GetPropVal( tarname_key, value_unknown );
524 dmh_control( DMH_BEGIN_DIGEST );
525 dmh_notify( DMH_ERROR, PKGMSG_SPECIFICATION_ERROR );
526 dmh_notify( DMH_ERROR, "%s: requires...\n", requestor );
527 for( int i = 0; i < sizeof( key ) / sizeof( char* ); i++ )
528 if( (ref = dep->GetPropVal( key[i], NULL )) != NULL )
530 dmh_notify( DMH_ERROR, "%s: unresolved dependency (type '%s')\n", ref, key[i] );
531 dmh_notify( DMH_ERROR, "%s: cannot identify any providing package\n" );
533 dmh_notify( DMH_ERROR, "please report this to the package maintainer\n" );
534 dmh_control( DMH_END_DIGEST );
537 /* Continue, until all prerequisites of the current package
538 * have been evaluated.
540 dep = dep->FindNextAssociate( requires_key );
543 /* Flush out any digest of debugging messages, which may
544 * have been collected, in respect of this dependency.
546 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
547 dmh_control( DMH_END_DIGEST )
550 /* Also consider any dependencies which may be common to
551 * all releases, or all components, of the current package;
552 * we do this by walking back through the XML hierarchy,
553 * searching for "requires" elements in all containing
554 * contexts, until we reach the root element.
556 package = (package == GetRoot()) ? NULL : package->GetParent();
558 DEBUG_INVOKED --indent;
562 STATIC_INLINE bool if_noref( const char *name )
564 /* Helper function, used exclusively by the following assert_unmatched()
565 * function; it is used to confirm that "name" represents nothing.
567 return (name == NULL) || (*name == '\0');
570 STATIC_INLINE bool if_match( const char *ref, const char *name )
572 /* Helper function, used exclusively by the following assert_unmatched()
573 * function; it is used to confirm that the package identified by "name"
574 * is an exact match for that represented by "ref".
576 return (name != NULL) && (strcmp( ref, name ) == 0);
579 STATIC_INLINE bool if_alias( const char *ref, const char *list )
581 /* Helper function, used exclusively by the following assert_unmatched()
582 * function; it is used to confirm that the "list" of package name aliases
583 * includes one which is an exact match for "ref".
585 return (list != NULL) && has_keyword( ref, list );
588 STATIC_INLINE bool assert_unmatched
589 ( const char *ref, const char *val, const char *name, const char *alias )
591 /* Helper for the following "assert_installed" function; it determines if
592 * the reference name specified by "ref" matches either the corresponding
593 * field value "val" from a tarname look-up, or in the case of a package
594 * name reference, the containing package "name" attribute or any of its
595 * specified "alias" names. The return value is false, in the case of a
596 * match, or true when unmatched.
600 ? /* When "ref" is NULL, then a match requires all specified candidates
601 * for matching to also be NULL, or to be pointers to empty strings.
603 !( if_noref( val ) && if_noref( name ) && if_noref( alias ))
605 : /* Otherwise, when "ref" is not NULL, then a match is identified when
606 * any one candidate is found to match.
608 !( if_match( ref, val ) || if_match( ref, name ) || if_alias( ref, alias ));
612 pkgXmlNode *assert_installed( pkgXmlNode *current, pkgXmlNode *installed )
614 /* Validation hook for pkgXmlDocument::Schedule(); it checks for
615 * possible prior installation of an obsolete version of a current
616 * package, (i.e. the package itself is listed in the distribution
617 * manifest, but the listing for the installed version has been
620 * Note that, by the time this helper is called, an installation
621 * may have been identified already, by a release reference which
622 * is still present in the distribution manifest; we perform this
623 * check, only if no such identification was possible.
625 if( current && (installed == NULL) )
627 /* This is the specific case where we have selected a current
628 * package for processing, but we HAVE NOT been able to identify
629 * a prior installation through a distribution manifest reference;
630 * thus, we must perform the further check for prior installation
631 * of an obsolete version.
633 * Starting from the sysroot record for the specified release...
635 pkgXmlNode *sysroot; const char *tarname;
636 pkgSpecs lookup( current->GetPropVal( tarname_key, NULL ) );
637 if( (sysroot = current->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
639 /* ...identify the first, if any, package installation record.
641 pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
644 /* When at least one installation record exists,
645 * establish references for the "tarname" fields which
646 * we must match, to identify a prior installation.
648 const char *refname = lookup.GetPackageName();
649 const char *cptname = lookup.GetComponentClass();
650 const char *version = lookup.GetComponentVersion();
652 /* Also identify the formal name for the containing package,
653 * and any aliases by which it may also be known, so that we
654 * may be able to identify a prior installation which may
655 * have borne a deprecated package name.
657 const char *pkgname = current->GetContainerAttribute( name_key );
658 const char *alias = current->GetContainerAttribute( alias_key );
660 /* For each candidate installation record found...
664 /* ...check if it matches the look-up criteria.
666 pkgSpecs chk( tarname = ref->GetPropVal( tarname_key, NULL ) );
667 if( assert_unmatched( chk.GetPackageName(), refname, pkgname, alias )
668 || assert_unmatched( chk.GetComponentClass(), cptname, NULL, NULL )
669 || assert_unmatched( chk.GetComponentVersion(), version, NULL, NULL ) )
671 * This candidate isn't a match; try the next, if any...
673 ref = ref->FindNextAssociate( installed_key );
676 { /* We found a prior installation of a deprecated version;
677 * back-build a corresponding reference within the associated
678 * package or component-package inventory, in the internal
679 * copy of the distribution manifest...
681 if( (installed = new pkgXmlNode( release_key )) != NULL )
683 installed->SetAttribute( tarname_key, tarname );
684 installed->SetAttribute( installed_key, value_yes );
685 if( (ref = current->GetParent()) != NULL )
686 installed = ref->AddChild( installed );
688 /* Having found a prior installation, there is no need to
689 * check any further installation records; force "ref" to
690 * NULL, to inhibit further searching.
698 /* However we get to here, we always return the pointer to the installed
699 * package entry identified by the dependency resolver, which may, or may
700 * not have been modified by this function.
705 void pkgActionItem::ConfirmInstallationStatus()
707 /* Set the "to_remove" selection in an action item to match the installed
708 * package entry, even when the release in question is no longer enumerated
709 * in the package catalogue; (used to identify any installed version when
710 * compiling a package reference listing).
713 = assert_installed( selection[to_install], selection[to_remove] );
717 const char *get_version_bounds( const char *name )
719 /* Helper to locate any version bounds specification which may
720 * have been appended to a package name command line argument.
722 if( (name != NULL) && *name )
724 * If the name is specified, and not zero-length, then
725 * the bounds specification begins at the first '<', '=',
726 * or '>' character, (if any)...
728 do { if( (*name == '<') || (*name == '=') || (*name == '>') )
730 * ...in which case, we return that location.
735 /* Otherwise we fall through, returning NULL to indicate
736 * that no bounds specification is present.
741 void pkgActionItem::ApplyBounds( pkgXmlNode *release, const char *bounds )
743 /* Method to interpret a user specified version requirement,
744 * and attach it to a primary action item, much as if it were
745 * an internally identified requirement, as identified by the
746 * dependency resolver.
749 pkgSpecs refspec( release );
751 while( (bounds != NULL) && *bounds )
753 /* Parse the user specified requirement, formulating a specification
754 * which may be interpreted by "pkginfo"; (use an arbitrary package
755 * name of "x", since we care only about the version number)...
757 const char *condition = NULL;
758 char spec_string[7 + strlen( bounds )]; strcpy( spec_string, "x-" );
759 char *p = spec_string + strlen( spec_string ) - 1;
762 /* ...identifying it as...
766 * ...an equality requirement...
773 if( *++bounds == '=' )
775 /* ...less than or equal
776 * (<=; inclusive upper bound)...
782 /* ...less than (exclusive upper bound)...
788 if( *++bounds == '=' )
790 /* ...greater than or equal
791 * (>=; inclusive lower bound)...
797 /* ...or greater than (exclusive lower bound).
802 do { /* Accumulate characters into the local copy of the specification,
803 * until we encounter the end of the user specified string, or the
804 * start of a second specification, (e.g. the second limit for a
805 * version number range specification).
807 *++p = ((*bounds == '<') || (*bounds == '=') || (*bounds == '>'))
812 /* Append an arbitrary component classification of "y", and an archive
813 * type of "z", because "pkginfo" requires them, then interpret as a
814 * "pkginfo" data structure...
817 pkgSpecs usrspec( spec_string );
819 /* ...then extract the version fields of interest, and insert them
820 * into the actual working reference specification...
822 refspec.SetPackageVersion( usrspec.GetPackageVersion() );
823 refspec.SetPackageBuild( usrspec.GetPackageBuild() );
824 if( (refname = usrspec.GetSubSystemVersion()) != NULL )
826 /* ...including the subsystem version, if any, which the user may
829 refspec.SetSubSystemVersion( refname );
830 refspec.SetSubSystemBuild( usrspec.GetSubSystemBuild() );
833 /* ...or allowing a wild-card match otherwise.
835 refspec.SetSubSystemVersion( "*" );
837 /* Convert the reference specification to "tarname" format...
839 if( (refname = refspec.GetTarName()) != NULL )
841 /* ...and construct a temporary "requires" specification from it.
843 pkgXmlNode requisite( requires_key );
844 requisite.SetAttribute( condition, refname );
846 /* Set the action item requirements to honour this...
848 SetRequirements( &requisite, &refspec );
850 /* ...then release the heap memory used to temporarily store the
851 * "tarname" attribute for this; (the remaining data associated
852 * with this wil be automatically discarded, when the temporary
853 * specification goes out of scope).
855 free( (void *)(refname) );
860 pkgActionItem *pkgActionItem::GetReference( pkgXmlNode *package )
862 /* Method to locate a scheduled action, if any, which relates to
863 * a specified package. Assume that the initial 'this' pointer is
864 * closer to the end, than to the beginning of the current list of
865 * scheduled actions, (which MUST NOT be empty), and...
867 pkgActionItem *item = this;
868 while( item->next != NULL )
870 * ...advance, to locate the very last entry in the list.
874 /* Now, working backward toward the beginning of the list...
876 while( item != NULL )
878 /* ...identify a "release" specification associated with
879 * each action item in turn...
881 pkgXmlNode *ref = item->Selection();
882 if( (ref != NULL) || ((ref = item->Selection( to_remove )) != NULL) )
884 /* ...convert this to an actual component package, or
885 * full package, reference...
887 while( ref->IsElementOfType( release_key ) )
888 ref = ref->GetParent();
890 /* ...and, if it matches the search target, return it.
892 if( ref == package ) return item;
894 /* ...or, when we haven't yet found a matching package,
895 * try the preceding scheduled action item, if any.
899 /* If we fall through to here, then we found no action to be
900 * performed on the specified package; report accordingly.
905 static void dmh_notify_no_match
906 ( const char *name, pkgXmlNode *package, const char *bounds )
908 /* Diagnostic helper, called when the user has requested a specific
909 * package version for which there is no exactly matching release.
911 dmh_control( DMH_BEGIN_DIGEST );
912 dmh_notify( DMH_ERROR, "there is no release matching %s%s\n",
913 name, (bounds == NULL) ? "" : bounds
915 if( (package = package->FindFirstAssociate( release_key )) != NULL )
917 /* To assist the user in formalising a more appropriate
918 * version specification...
920 dmh_notify( DMH_ERROR, "available candidate releases are...\n" );
923 /* ...display a list of available release tarballs...
925 const char *tarname = package->GetPropVal( tarname_key, NULL );
926 if( tarname != NULL )
927 dmh_notify( DMH_ERROR, " %s\n", tarname );
929 /* ...cycling, until all have been identified.
931 package = package->FindNextAssociate( release_key );
934 dmh_control( DMH_END_DIGEST );
937 pkgActionItem* pkgXmlDocument::Schedule( unsigned long action, const char* name )
939 /* Task scheduler interface; schedules actions to process all
940 * dependencies for the package specified by "name", honouring
941 * any appended version bounds specified for the parent.
944 /* We may call this method without any assigned package name,
945 * in which case, we interpret it as a request to return the
946 * list of previously scheduled actions...
950 /* ...but when a package name is specified, then we
951 * proceed to schedule the specified action on it.
953 char scratch_pad[strlen( name )];
954 const char *bounds_specification = get_version_bounds( name );
955 if( bounds_specification != NULL )
957 /* Separate any version bounds specification from
958 * the original package name specification.
960 size_t scratch_pad_len = bounds_specification - name;
961 name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
962 scratch_pad[scratch_pad_len] = '\0';
966 if( (release = FindPackageByName( name )) != NULL )
968 /* We found the specification for the named package...
970 pkgXmlNode *component = release->FindFirstAssociate( component_key );
971 if( component != NULL )
973 * When it is subdivided into component-packages,
974 * we need to consider each as a possible candidate
975 * for task scheduling.
979 while( release != NULL )
981 /* Within each candidate package or component-package...
983 pkgXmlNode *package = release;
984 if( (release = release->FindFirstAssociate( release_key )) != NULL )
986 /* ...initially assume it is not installed, and that
987 * no installable upgrade is available.
989 pkgActionItem latest;
990 pkgXmlNode *installed = NULL, *upgrade = NULL;
992 /* Establish the action for which dependency resolution is
993 * to be performed; note that this may be promoted to a more
994 * inclusive class, during resolution, so we need to reset
995 * it for each new dependency which may be encountered.
999 /* Any action request processed here is, by definition,
1000 * a request for a primary action; mark it as such.
1002 action |= ACTION_PRIMARY;
1004 /* When the user has given a version bounds specification,
1005 * then we must assign appropriate action item requirements.
1007 if( bounds_specification != NULL )
1008 latest.ApplyBounds( release, bounds_specification );
1010 /* For each candidate release in turn...
1012 while( release != NULL )
1014 /* ...inspect it to identify any which is already installed,
1015 * and also the latest available...
1017 if( is_installed( release ) )
1019 * ...i.e. here we have identified a release
1020 * which is currently installed...
1022 latest.SelectPackage( installed = release, to_remove );
1024 if( latest.SelectIfMostRecentFit( release ) == release )
1026 * ...while this is the most recent we have
1027 * encountered so far.
1031 /* Continue with the next specified release, if any.
1033 release = release->FindNextAssociate( release_key );
1036 if( (installed = assert_installed( upgrade, installed )) == NULL )
1038 /* There is no installed version...
1039 * therefore, there is nothing to do for any action
1040 * other than ACTION_INSTALL...
1042 if( (action & ACTION_MASK) == ACTION_INSTALL )
1045 * ...in which case, we must recursively resolve
1046 * any dependencies for the scheduled "upgrade".
1048 if( latest.Selection() == NULL )
1049 dmh_notify_no_match( name, package, bounds_specification );
1051 ResolveDependencies(
1052 upgrade, Schedule( with_download( action ), latest )
1056 { /* attempting ACTION_UPGRADE or ACTION_REMOVE
1057 * is an error; diagnose it.
1059 if( component == NULL )
1061 * In this case, the user explicitly specified a single
1062 * package component, so it's a simple error...
1064 dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
1065 action_name( action & ACTION_MASK ), name
1069 /* ...but here, the user specified only the package name,
1070 * which implicitly applies to all associated components;
1071 * since some may be installed, prefer to issue a warning
1072 * in respect of any which aren't.
1074 const char *extname = component->GetPropVal( class_key, "" );
1075 char full_package_name[2 + strlen( name ) + strlen( extname )];
1076 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1078 dmh_control( DMH_BEGIN_DIGEST );
1079 dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
1080 extname = action_name( action & ACTION_MASK ), full_package_name
1082 dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
1085 dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
1088 dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
1091 dmh_control( DMH_END_DIGEST );
1095 else if( upgrade && (upgrade != installed) )
1097 /* There is an installed version, but an upgrade to a newer
1098 * version is available; when performing ACTION_UPGRADE...
1100 if( (action & ACTION_MASK) == ACTION_UPGRADE )
1102 * ...we must recursively resolve any dependencies...
1104 ResolveDependencies( upgrade,
1105 Schedule( with_download( action ), latest )
1108 else if( (action & ACTION_MASK) == ACTION_REMOVE )
1110 /* ...while for ACTION_REMOVE, we have little to do,
1111 * beyond scheduling the removal; (we don't extend the
1112 * scope of a remove request to prerequisite packages,
1113 * so there is no need to resolve dependencies)...
1115 latest.SelectPackage( installed );
1116 Schedule( action, latest );
1119 { /* ...but, we decline to proceed with ACTION_INSTALL
1120 * unless the --reinstall option is enabled...
1122 if( pkgOptions()->Test( OPTION_REINSTALL ) )
1124 /* ...in which case, we resolve dependencies for,
1125 * and reschedule a reinstallation of the currently
1126 * installed version...
1128 latest.SelectPackage( installed );
1129 ResolveDependencies( installed,
1130 Schedule( with_download( action | ACTION_REMOVE ), latest )
1134 { /* ...otherwise, we reformulate the appropriate
1135 * fully qualified package name...
1137 const char *extname = ( component != NULL )
1138 ? component->GetPropVal( class_key, "" )
1140 char full_package_name[2 + strlen( name ) + strlen( extname )];
1141 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1143 * ...which we then incorporate into an advisory
1144 * diagnostic message, which serves both to inform
1145 * the user of this error condition, and also to
1146 * suggest appropriate corrective action.
1148 dmh_control( DMH_BEGIN_DIGEST );
1149 dmh_notify( DMH_ERROR, "%s: package is already installed\n",
1152 dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
1155 dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
1158 dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n"
1160 dmh_control( DMH_END_DIGEST );
1165 { /* In this case, the package is already installed,
1166 * and no more recent release is available; we still
1167 * recursively resolve its dependencies, to capture
1168 * any potential upgrades for them.
1170 if( latest.Selection() == NULL )
1171 dmh_notify_no_match( name, package, bounds_specification );
1173 ResolveDependencies( upgrade, Schedule( action, latest ));
1176 if( (component != NULL)
1177 && ((component = component->FindNextAssociate( component_key )) != NULL) )
1179 * When evaluating a component-package, we extend our
1180 * evaluation, to consider for any further components of
1181 * the current package.
1183 release = component;
1188 /* We found no information on the requested package;
1189 * diagnose as a non-fatal error.
1191 dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
1193 /* Finally, we return a pointer to the currently scheduled
1194 * actions list, if any.
1199 void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )
1201 /* Wrapper function to retrieve the list of all installed packages,
1202 * passing each entry in turn to the standard task scheduler. We
1203 * begin by locating the first sysroot entry in the XML database...
1205 pkgXmlNode *sysroot = GetRoot()->FindFirstAssociate( sysroot_key );
1207 /* ...then, while we have sysroots to examine...
1209 while( sysroot != NULL )
1211 /* ...we retrieve the first package installation record within
1212 * the current sysroot data set.
1214 pkgXmlNode *package = sysroot->FindFirstAssociate( installed_key );
1216 /* Within each sysroot, until we've retrieved all embedded
1217 * installation records...
1219 while( package != NULL )
1221 /* ...we read the canonical tarname for the package,
1222 * and when it is appropriately specified...
1224 const char *tarname = package->GetPropVal( tarname_key, NULL );
1225 if( tarname != NULL )
1227 /* ...we decode it, to determine the package name,
1228 * subsystem name and component class.
1230 pkgSpecs decode( tarname );
1231 const char *pkgname = decode.GetPackageName();
1232 const char *sysname = decode.GetSubSystemName();
1233 const char *cptname = decode.GetComponentClass();
1235 /* From these three, we need to reconstruct an effective
1236 * package name for the scheduler look-up; this reconstruction
1237 * is performed using the following formatted buffer.
1239 const char *fmt = "%s-%s";
1240 char refname[3 + strlen( sysname ) + strlen( pkgname ) + strlen( cptname )];
1241 if( FindPackageByName( pkgname, sysname ) == NULL )
1243 /* The package name alone is insufficient for a successful
1244 * look-up; assume that the effective package name has been
1245 * defined by prefixing the sysroot name.
1247 sprintf( refname, fmt, sysname, pkgname );
1250 if( cptname != NULL )
1252 /* A fully qualified logical package name should include
1253 * the component class name, abstracted from the canonical
1254 * tarname, and appended to the package name.
1256 sprintf( refname, fmt, pkgname, cptname );
1260 /* Having constructed the effective logical package name,
1261 * we schedule the requested action on the package...
1263 Schedule( action, pkgname );
1265 /* ...then move on to the next installed package, if any,
1266 * within the current sysroot data set...
1268 package = package->FindNextAssociate( installed_key );
1270 /* ...and ultimately, to the next sysroot, if any, in the
1273 sysroot = sysroot->FindNextAssociate( sysroot_key );
1277 /* $RCSfile$: end of file */