6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2010, 2011, 2012, MinGW 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.
41 /* Define supplementary action codes, which may be used exclusively
42 * by pkgXmlDocument::ResolveDependencies(), to ensure that recursive
43 * actions are appropriately scheduled; these apply to user specified
44 * actions "upgrade --recursive", "install --recursive --reinstall",
45 * and "upgrade --recursive --reinstall" respectively.
47 #define ACTION_RECURSIVE_UPGRADE (ACTION_UPGRADE | OPTION_RECURSIVE)
48 #define ACTION_RECURSIVE_REINSTALL (ACTION_INSTALL | OPTION_ALL_DEPS)
49 #define ACTION_RECURSIVE_REPLACE (ACTION_UPGRADE | OPTION_ALL_DEPS)
51 /* FIXME: the following declaration belongs in a future "pkgmsgs.h"
52 * header file, with the function implementation in a separate C or C++
53 * messages source file, with appropriate internationalisation...
55 EXTERN_C const char *pkgMsgUnknownPackage( void );
57 const char *pkgMsgUnknownPackage( void )
59 /* FIXME: (see note above); return English language only, for now.
61 return "%s: unknown package\n";
64 /* Provide a convenience macro for declaring static inline functions
65 * which we want to always expand inline.
67 #define STATIC_INLINE static inline __attribute__((__always_inline__))
69 static bool is_installed( pkgXmlNode *release )
71 /* Helper to check installation status of a specified package release.
75 * First, check for any 'installed' attribute which may have been
76 * explicitly specified for the 'release' record...
78 if( (status = release->GetPropVal( installed_key, NULL )) != NULL )
80 * ...immediately returning the status deduced from it,
83 return (strcmp( status, yes_value ) == 0);
85 /* When the package definition itself doesn't bear an explicit
86 * 'installed' attribute, then we must check the system map for
87 * an associated installation record...
90 if( ((pkgname = release->GetPropVal( tarname_key, NULL )) != NULL)
91 && (release->GetInstallationRecord( pkgname ) != NULL) )
93 /* ...and, when one is found, we can mark this package with
94 * an explicit status attribute for future reference, before
95 * we return confirmation of the installed status...
97 release->SetAttribute( installed_key, yes_value );
101 /* If we get to here, we may deduce that the package is not
102 * installed; once again, we set the explicit attribute value
103 * to convey this for future reference, before returning the
104 * appropriate status flag.
106 release->SetAttribute( installed_key, no_value );
110 pkgXmlNode *pkgXmlNode::GetInstallationRecord( const char *pkgname )
112 /* Retrieve the installation record, if any, for the package
113 * specified by fully qualified canonical 'pkgname'.
115 * First, break down the specified package name, and retrieve
116 * the sysroot database entry for its associated subsystem.
119 pkgSpecs lookup( pkgname );
120 if( (sysroot = GetSysRoot( lookup.GetSubSystemName() )) != NULL )
122 /* We successfully retrieved a sysroot entry; now we must
123 * search the associated list of installed packages, for one
124 * with the appropriate canonical package name.
126 pkgXmlNode *pkg = sysroot->FindFirstAssociate( installed_key );
129 /* We found an installed package entry; check if it has
130 * the correct canonical name...
132 const char *installed = pkg->GetPropVal( tarname_key, NULL );
133 if( (installed != NULL) && (strcmp( installed, pkgname ) == 0) )
135 * ...returning this entry if so...
139 /* ...otherwise, move on to the next entry, if any.
141 pkg = pkg->FindNextAssociate( installed_key );
145 /* If we get to here, we didn't find an entry for the required
146 * package; return NULL, indicating that it is not installed.
151 const char *pkgXmlNode::GetContainerAttribute( const char *key, const char *sub )
153 /* Walk the XML path from current element, back towards the document root,
154 * until we find the innermost element which has an attribute matching "key";
155 * if such an element is found, return the value of the attribute; if we have
156 * traversed the entire path, all the way to the document root, and we have
157 * not found any element with the "key" attribute, return "sub".
159 pkgXmlNode *pkg = this;
160 pkgXmlNode *root = pkg->GetDocumentRoot();
163 /* We haven't yet tried to search beyond the document root;
164 * try matching "key" to an attribute of the current element...
166 const char *retval = pkg->GetPropVal( key, NULL );
169 * ...returning its value, if such an attribute is found...
174 * take a further step back towards the document root.
176 pkg = (pkg == root) ? NULL : pkg->GetParent();
179 /* If we get to here, then no element with the required "key"
180 * attribute could be found; substitute the specified default.
185 DEBUG_INVOKED static int indent = -1;
187 DEBUG_INVOKED static void
188 DEBUG_INVOKED show_required( pkgSpecs *req )
190 DEBUG_INVOKED const char *tarname = NULL;
191 DEBUG_INVOKED dmh_printf( "%*s require: %s\n", indent, "", req->GetTarName( tarname ) );
192 DEBUG_INVOKED free( (void *)(tarname) );
196 bool is_abi_compatible( pkgSpecs *refdata, const char *version )
198 /* Local helper, used by pkgXmlDocument::ResolveDependencies(),
199 * to confirm that the ABI identification number of a selected
200 * component package is an exact match to a requirement spec.
202 const char *ref_version;
203 if( (ref_version = refdata->GetComponentVersion()) == NULL )
205 * Here, confirm that both are unversioned...
207 return (version == NULL);
209 /* ...otherwise, fall through to check that both bear IDENTICALLY
210 * the same ABI version number.
212 return ((version != NULL) && (strcmp( version, ref_version ) == 0));
215 STATIC_INLINE unsigned long action_class
216 ( unsigned long requested, unsigned long viable )
218 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
219 * it classifies each requested action with respect to any previously
220 * installed version of each package, to ensure that reinstallation
221 * and recursive requests are dispatched appropriately.
223 return viable ? requested : ACTION_RECURSIVE_UPGRADE;
226 STATIC_INLINE unsigned long with_request_flags( unsigned long request )
228 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
229 * it isolates the request flags from the action code, to accommodate
230 * promotion of an alternative action with matching flags.
232 return request & ~(ACTION_MASK | ACTION_DOWNLOAD);
235 STATIC_INLINE unsigned long with_download( unsigned long action_code )
237 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
238 * it adds the download request flag,when promoting any action which
239 * may require a package archive to be downloaded.
241 return action_code | (ACTION_DOWNLOAD);
244 STATIC_INLINE unsigned long promote
245 ( unsigned long request, unsigned long action_code )
247 /* Helper function for use by pkgXmlDocument::ResolveDependencies();
248 * it promotes an alternative action to that explicitly requested by
249 * the user, when this is necessary to satisfy a dependency.
251 return with_request_flags( request ) | with_download( action_code );
255 pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
257 /* For the specified "package", (nominally a "release"), identify its
258 * prerequisites, (as specified by "requires" tags), and schedule actions
259 * to process them; repeat recursively, to identify further dependencies
260 * of such prerequisites, and finally, extend the search to capture
261 * additional dependencies common to the containing package group.
263 pkgSpecs *refdata = NULL;
264 pkgXmlNode *refpkg = package;
266 DEBUG_INVOKED ++indent;
268 /* Capture the state of global option settings controlling the scope
269 * of recursive behaviour and reinstallation requests, so that we may
270 * implicitly extend the effect when processing virtual packages...
272 int request_mode = pkgOptions()->Test( OPTION_ALL_DEPS );
273 if( match_if_explicit( package->ArchiveName(), value_none ) )
275 * ...such that the effect of an upgrade or a reinstall implicitly
276 * applies, through a single level of recursion, to the first level
277 * of requisite dependencies.
279 request_mode |= OPTION_RECURSIVE;
280 request_mode |= request & ACTION_MASK;
282 while( package != NULL )
284 /* We have a valid XML entity, which may identify dependencies;
285 * check if it includes any "requires" specification...
287 pkgXmlNode *dep = package->FindFirstAssociate( requires_key );
290 /* We found a dependency specification...
291 * Initially, assume this package is not installed.
293 pkgXmlNode *installed = NULL;
294 unsigned installed_is_viable = 0;
296 /* To facilitate resolution of "%" version matching wildcards
297 * in the requirements specification, we need to parse the version
298 * specification for the current dependent package...
300 if( refdata == NULL )
302 /* ...but we deferred that until we knew for sure that it would
303 * be needed; it is, so parse it now.
306 if( (refname = refpkg->GetPropVal( tarname_key, NULL )) != NULL )
308 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
309 dmh_printf( "%*s%s: resolve dependencies\n", indent, "", refname )
311 refdata = new pkgSpecs( refname );
315 /* Identify the prerequisite package, from its canonical name...
317 pkgActionItem wanted; pkgXmlNode *selected;
318 pkgSpecs req( wanted.SetRequirements( dep, refdata ) );
319 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
320 show_required( &req )
323 * (Both the package name, and subsystem if specified, must match)...
325 selected = FindPackageByName( req.GetPackageName(), req.GetSubSystemName() );
327 /* When we've identified the appropriate package...
329 if( selected != NULL )
331 /* ...and, more significantly, the appropriate component package,
332 * where applicable...
334 pkgXmlNode *component; const char *reqclass;
335 if( (reqclass = req.GetComponentClass()) == NULL )
336 reqclass = value_unknown;
338 if( (component = selected->FindFirstAssociate( component_key )) == NULL )
340 * ...but if no separate component package exists,
341 * consider the parent package itself, as sole component.
343 component = selected;
345 /* At this point, we have no more than a tentative package selection;
346 * it may not provide a component to fit the requirements specification.
347 * Thus, kill the selection, pending reaffirmation...
350 while( component != NULL )
352 /* ...by stepping through the "releases" of this component package...
354 pkgXmlNode *required = component->FindFirstAssociate( release_key );
355 while( required != NULL )
357 /* ...noting if we find one already marked as "installed"...
359 const char *tstclass;
360 DEBUG_INVOKED const char *already_installed = "";
361 pkgSpecs tst( tstclass = required->GetPropVal( tarname_key, NULL ) );
362 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
363 dmh_printf( "%*s considering: %s", indent, "", tstclass )
365 if( (tstclass = tst.GetComponentClass()) == NULL )
366 tstclass = value_unknown;
368 if( is_installed( required ) && (strcmp( tstclass, reqclass ) == 0)
370 * We found an installed version of the requisite component,
371 * but we ignore it unless it is ABI version compatible with
372 * the version we need; (the intent of ABI versioning is to
373 * accommodate multiple concurrent installations of shared
374 * objects supporting the differing ABI specifications).
376 && is_abi_compatible( &tst, req.GetComponentVersion() ) )
378 installed = required;
379 DEBUG_INVOKED already_installed = " (already installed)";
381 /* ...and identify the most suitable candidate "release"
382 * to satisfy the current dependency...
384 if( wanted.SelectIfMostRecentFit( required ) == required )
385 selected = component = required;
387 if( required == installed )
388 installed_is_viable = wanted.HasAttribute( ACTION_MAY_SELECT );
390 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
391 dmh_printf( "%s%s\n", wanted.HasAttribute( ACTION_MAY_SELECT )
392 ? ": viable candidate"
398 /* ...continuing, until all available "releases"
399 * have been evaluated accordingly.
401 required = required->FindNextAssociate( release_key );
404 /* Where multiple component packages do exist,
405 * continue until all have been inspected.
407 component = component->FindNextAssociate( component_key );
410 /* We have now identified the most suitable candidate package,
411 * to resolve the current dependency...
415 /* ...this package is already installed, so we may schedule
416 * a resolved dependency match, with no pending action...
418 unsigned long fallback = with_request_flags( request );
419 switch( action_class( request_mode, installed_is_viable ) )
423 case ACTION_RECURSIVE_REINSTALL:
425 * ...when the action is "install", with "--reinstall"
426 * and "--recursive" options in effect, we update the
427 * package selection to favour the already installed
428 * version over any available upgrade...
430 wanted.SelectPackage( selected = installed );
432 * ...falling through to...
435 case ACTION_RECURSIVE_REPLACE:
436 case ACTION_RECURSIVE_UPGRADE:
438 * ...schedule removal of the already installed version,
439 * for replacement by a fresh copy of the same version,
440 * or an upgraded version, as appropriate.
442 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
443 dmh_printf( "%*s%s: schedule replacement\n", indent + 2, "",
444 installed->GetPropVal( tarname_key, value_unknown )
447 wanted.SelectPackage( installed, to_remove );
448 fallback |= with_download( ACTION_UPGRADE );
452 /* In any other case, the currently installed version is
453 * to be left in place; we must ensure that its dependencies,
454 * (if any), will be resolved with respect to this already
457 wanted.SelectPackage( selected = installed );
460 /* Schedule the appropriate fallback action, (which may be none),
461 * for the already installed package.
463 rank = Schedule( fallback, wanted, rank );
467 /* FIXME: this change in logic may introduce a regression; I (KDM)
468 * may need to re-evaluate the conceptual effect of PRIMARY actions
469 * vs. SECONDARY actions in this context, but this pre-v0.5 logic
470 * breaks the handling of meta-packages in v0.5
472 else if( ((request & ACTION_MASK) == ACTION_INSTALL)
474 * The required package is not installed, so when
475 * we are performing an installation, ...
477 || ((request & (ACTION_PRIMARY | ACTION_INSTALL)) == ACTION_INSTALL) )
479 else if( (request & ACTION_INSTALL) == ACTION_INSTALL )
481 /* ...or when this is a new requirement of a package
482 * which is being upgraded, then we must schedule it
483 * for installation now; (we may simply ignore it, if
484 * we are performing a removal).
486 DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
487 dmh_printf( "%*s%s: schedule installation (flags=0x%08x)\n",
488 indent + 2, "", selected->GetPropVal( tarname_key, value_unknown ),
489 promote( request, ACTION_INSTALL )
492 rank = Schedule( promote( request, ACTION_INSTALL ), wanted, rank );
495 /* Regardless of the action scheduled, we must recursively
496 * consider further dependencies of the resolved prerequisite;
497 * FIXME: do we need to do this, when performing a removal?
498 * Right now, I (KDM) don't think so...
500 if( (request & ACTION_INSTALL) != 0 )
501 ResolveDependencies( selected, rank );
504 if( selected == NULL )
506 /* No package matching the selection criteria could be found;
507 * report a dependency resolution failure in respect of each
508 * specified criterion...
510 const char *ref, *key[] = { lt_key, le_key, eq_key, ge_key, gt_key };
511 const char *requestor = refpkg->GetPropVal( tarname_key, value_unknown );
513 dmh_control( DMH_BEGIN_DIGEST );
514 dmh_notify( DMH_ERROR, "%s: requires...\n", requestor );
515 for( int i = 0; i < sizeof( key ) / sizeof( char* ); i++ )
516 if( (ref = dep->GetPropVal( key[i], NULL )) != NULL )
518 dmh_notify( DMH_ERROR, "%s: unresolved dependency (type '%s')\n", ref, key[i] );
519 dmh_notify( DMH_ERROR, "%s: cannot identify any providing package\n" );
521 dmh_notify( DMH_ERROR, "please report this to the package maintainer\n" );
522 dmh_control( DMH_END_DIGEST );
525 /* Continue, until all prerequisites of the current package
526 * have been evaluated.
528 dep = dep->FindNextAssociate( requires_key );
530 /* Also consider any dependencies which may be common to
531 * all releases, or all components, of the current package;
532 * we do this by walking back through the XML hierarchy,
533 * searching for "requires" elements in all containing
534 * contexts, until we reach the root element.
536 package = (package == GetRoot()) ? NULL : package->GetParent();
538 DEBUG_INVOKED --indent;
542 STATIC_INLINE bool if_noref( const char *name )
544 /* Helper function, used exclusively by the following assert_unmatched()
545 * function; it is used to confirm that "name" represents nothing.
547 return (name == NULL) || (*name == '\0');
550 STATIC_INLINE bool if_match( const char *ref, const char *name )
552 /* Helper function, used exclusively by the following assert_unmatched()
553 * function; it is used to confirm that the package identified by "name"
554 * is an exact match for that represented by "ref".
556 return (name != NULL) && (strcmp( ref, name ) == 0);
559 STATIC_INLINE bool if_alias( const char *ref, const char *list )
561 /* Helper function, used exclusively by the following assert_unmatched()
562 * function; it is used to confirm that the "list" of package name aliases
563 * includes one which is an exact match for "ref".
565 return (list != NULL) && has_keyword( ref, list );
568 STATIC_INLINE bool assert_unmatched
569 ( const char *ref, const char *val, const char *name, const char *alias )
571 /* Helper for the following "assert_installed" function; it determines if
572 * the reference name specified by "ref" matches either the corresponding
573 * field value "val" from a tarname look-up, or in the case of a package
574 * name reference, the containing package "name" attribute or any of its
575 * specified "alias" names. The return value is false, in the case of a
576 * match, or true when unmatched.
580 ? /* When "ref" is NULL, then a match requires all specified candidates
581 * for matching to also be NULL, or to be pointers to empty strings.
583 !( if_noref( val ) && if_noref( name ) && if_noref( alias ))
585 : /* Otherwise, when "ref" is not NULL, then a match is identified when
586 * any one candidate is found to match.
588 !( if_match( ref, val ) || if_match( ref, name ) || if_alias( ref, alias ));
592 pkgXmlNode *assert_installed( pkgXmlNode *current, pkgXmlNode *installed )
594 /* Validation hook for pkgXmlDocument::Schedule(); it checks for
595 * possible prior installation of an obsolete version of a current
596 * package, (i.e. the package itself is listed in the distribution
597 * manifest, but the listing for the installed version has been
600 * Note that, by the time this helper is called, an installation
601 * may have been identified already, by a release reference which
602 * is still present in the distribution manifest; we perform this
603 * check, only if no such identification was possible.
605 if( current && (installed == NULL) )
607 /* This is the specific case where we have selected a current
608 * package for processing, but we HAVE NOT been able to identify
609 * a prior installation through a distribution manifest reference;
610 * thus, we must perform the further check for prior installation
611 * of an obsolete version.
613 * Starting from the sysroot record for the specified release...
615 pkgXmlNode *sysroot; const char *tarname;
616 pkgSpecs lookup( current->GetPropVal( tarname_key, NULL ) );
617 if( (sysroot = current->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
619 /* ...identify the first, if any, package installation record.
621 pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
624 /* When at least one installation record exists,
625 * establish references for the "tarname" fields which
626 * we must match, to identify a prior installation.
628 const char *refname = lookup.GetPackageName();
629 const char *cptname = lookup.GetComponentClass();
630 const char *version = lookup.GetComponentVersion();
632 /* Also identify the formal name for the containing package,
633 * and any aliases by which it may also be known, so that we
634 * may be able to identify a prior installation which may
635 * have borne a deprecated package name.
637 const char *pkgname = current->GetContainerAttribute( name_key );
638 const char *alias = current->GetContainerAttribute( alias_key );
640 /* For each candidate installation record found...
644 /* ...check if it matches the look-up criteria.
646 pkgSpecs chk( tarname = ref->GetPropVal( tarname_key, NULL ) );
647 if( assert_unmatched( chk.GetPackageName(), refname, pkgname, alias )
648 || assert_unmatched( chk.GetComponentClass(), cptname, NULL, NULL )
649 || assert_unmatched( chk.GetComponentVersion(), version, NULL, NULL ) )
651 * This candidate isn't a match; try the next, if any...
653 ref = ref->FindNextAssociate( installed_key );
656 { /* We found a prior installation of a deprecated version;
657 * back-build a corresponding reference within the associated
658 * package or component-package inventory, in the internal
659 * copy of the distribution manifest...
661 if( (installed = new pkgXmlNode( release_key )) != NULL )
663 installed->SetAttribute( tarname_key, tarname );
664 installed->SetAttribute( installed_key, value_yes );
665 if( (ref = current->GetParent()) != NULL )
666 installed = ref->AddChild( installed );
668 /* Having found a prior installation, there is no need to
669 * check any further installation records; force "ref" to
670 * NULL, to inhibit further searching.
678 /* However we get to here, we always return the pointer to the installed
679 * package entry identified by the dependency resolver, which may, or may
680 * not have been modified by this function.
685 void pkgActionItem::ConfirmInstallationStatus()
687 /* Set the "to_remove" selection in an action item to match the installed
688 * package entry, even when the release in question is no longer enumerated
689 * in the package catalogue; (used to identify any installed version when
690 * compiling a package reference listing).
693 = assert_installed( selection[to_install], selection[to_remove] );
697 const char *get_version_bounds( const char *name )
699 /* Helper to locate any version bounds specification which may
700 * have been appended to a package name command line argument.
702 if( (name != NULL) && *name )
704 * If the name is specified, and not zero-length, then
705 * the bounds specification begins at the first '<', '=',
706 * or '>' character, (if any)...
708 do { if( (*name == '<') || (*name == '=') || (*name == '>') )
710 * ...in which case, we return that location.
715 /* Otherwise we fall through, returning NULL to indicate
716 * that no bounds specification is present.
721 void pkgActionItem::ApplyBounds( pkgXmlNode *release, const char *bounds )
723 /* Method to interpret a user specified version requirement,
724 * and attach it to a primary action item, much as if it were
725 * an internally identified requirement, as identified by the
726 * dependency resolver.
729 pkgSpecs refspec( release );
731 while( (bounds != NULL) && *bounds )
733 /* Parse the user specified requirement, formulating a specification
734 * which may be interpreted by "pkginfo"; (use an arbitrary package
735 * name of "x", since we care only about the version number)...
737 const char *condition = NULL;
738 char spec_string[7 + strlen( bounds )]; strcpy( spec_string, "x-" );
739 char *p = spec_string + strlen( spec_string ) - 1;
742 /* ...identifying it as...
746 * ...an equality requirement...
753 if( *++bounds == '=' )
755 /* ...less than or equal
756 * (<=; inclusive upper bound)...
762 /* ...less than (exclusive upper bound)...
768 if( *++bounds == '=' )
770 /* ...greater than or equal
771 * (>=; inclusive lower bound)...
777 /* ...or greater than (exclusive lower bound).
782 do { /* Accumulate characters into the local copy of the specification,
783 * until we encounter the end of the user specified string, or the
784 * start of a second specification, (e.g. the second limit for a
785 * version number range specification).
787 *++p = ((*bounds == '<') || (*bounds == '=') || (*bounds == '>'))
792 /* Append an arbitrary component classification of "y", and an archive
793 * type of "z", because "pkginfo" requires them, then interpret as a
794 * "pkginfo" data structure...
797 pkgSpecs usrspec( spec_string );
799 /* ...then extract the version fields of interest, and insert them
800 * into the actual working reference specification...
802 refspec.SetPackageVersion( usrspec.GetPackageVersion() );
803 refspec.SetPackageBuild( usrspec.GetPackageBuild() );
804 if( (refname = usrspec.GetSubSystemVersion()) != NULL )
806 /* ...including the subsystem version, if any, which the user may
809 refspec.SetSubSystemVersion( refname );
810 refspec.SetSubSystemBuild( usrspec.GetSubSystemBuild() );
813 /* ...or allowing a wild-card match otherwise.
815 refspec.SetSubSystemVersion( "*" );
817 /* Convert the reference specification to "tarname" format...
819 if( (refname = refspec.GetTarName()) != NULL )
821 /* ...and construct a temporary "requires" specification from it.
823 pkgXmlNode requisite( requires_key );
824 requisite.SetAttribute( condition, refname );
826 /* Set the action item requirements to honour this...
828 SetRequirements( &requisite, &refspec );
830 /* ...then release the heap memory used to temporarily store the
831 * "tarname" attribute for this; (the remaining data associated
832 * with this wil be automatically discarded, when the temporary
833 * specification goes out of scope).
835 free( (void *)(refname) );
840 static void dmh_notify_no_match
841 ( const char *name, pkgXmlNode *package, const char *bounds )
843 /* Diagnostic helper, called when the user has requested a specific
844 * package version for which there is no exactly matching release.
846 dmh_control( DMH_BEGIN_DIGEST );
847 dmh_notify( DMH_ERROR, "there is no release matching %s%s\n",
848 name, (bounds == NULL) ? "" : bounds
850 if( (package = package->FindFirstAssociate( release_key )) != NULL )
852 /* To assist the user in formalising a more appropriate
853 * version specification...
855 dmh_notify( DMH_ERROR, "available candidate releases are...\n" );
858 /* ...display a list of available release tarballs...
860 const char *tarname = package->GetPropVal( tarname_key, NULL );
861 if( tarname != NULL )
862 dmh_notify( DMH_ERROR, " %s\n", tarname );
864 /* ...cycling, until all have been identified.
866 package = package->FindNextAssociate( release_key );
869 dmh_control( DMH_END_DIGEST );
872 void pkgXmlDocument::Schedule( unsigned long action, const char* name )
874 /* Task scheduler interface; schedules actions to process all
875 * dependencies for the package specified by "name", honouring
876 * any appended version bounds specified for the parent.
878 char scratch_pad[strlen( name )];
879 const char *bounds_specification = get_version_bounds( name );
880 if( bounds_specification != NULL )
882 /* Separate any version bounds specification from
883 * the original package name specification.
885 size_t scratch_pad_len = bounds_specification - name;
886 name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
887 scratch_pad[scratch_pad_len] = '\0';
891 if( (release = FindPackageByName( name )) != NULL )
893 /* We found the specification for the named package...
895 pkgXmlNode *component = release->FindFirstAssociate( component_key );
896 if( component != NULL )
898 * When it is subdivided into component-packages,
899 * we need to consider each as a possible candidate
900 * for task scheduling.
904 while( release != NULL )
906 /* Within each candidate package or component-package...
908 pkgXmlNode *package = release;
909 if( (release = release->FindFirstAssociate( release_key )) != NULL )
911 /* ...initially assume it is not installed, and that
912 * no installable upgrade is available.
914 pkgActionItem latest;
915 pkgXmlNode *installed = NULL, *upgrade = NULL;
917 /* Establish the action for which dependency resolution is
918 * to be performed; note that this may be promoted to a more
919 * inclusive class, during resolution, so we need to reset
920 * it for each new dependency which may be encountered.
924 /* Any action request processed here is, by definition,
925 * a request for a primary action; mark it as such.
927 action |= ACTION_PRIMARY;
929 /* When the user has given a version bounds specification,
930 * then we must assign appropriate action item requirements.
932 if( bounds_specification != NULL )
933 latest.ApplyBounds( release, bounds_specification );
935 /* For each candidate release in turn...
937 while( release != NULL )
939 /* ...inspect it to identify any which is already installed,
940 * and also the latest available...
942 if( is_installed( release ) )
944 * ...i.e. here we have identified a release
945 * which is currently installed...
947 latest.SelectPackage( installed = release, to_remove );
949 if( latest.SelectIfMostRecentFit( release ) == release )
951 * ...while this is the most recent we have
952 * encountered so far.
956 /* Continue with the next specified release, if any.
958 release = release->FindNextAssociate( release_key );
961 if( (installed = assert_installed( upgrade, installed )) == NULL )
963 /* There is no installed version...
964 * therefore, there is nothing to do for any action
965 * other than ACTION_INSTALL...
967 if( (action & ACTION_MASK) == ACTION_INSTALL )
970 * ...in which case, we must recursively resolve
971 * any dependencies for the scheduled "upgrade".
973 if( latest.Selection() == NULL )
974 dmh_notify_no_match( name, package, bounds_specification );
977 upgrade, Schedule( with_download( action ), latest )
981 { /* attempting ACTION_UPGRADE or ACTION_REMOVE
982 * is an error; diagnose it.
984 if( component == NULL )
986 * In this case, the user explicitly specified a single
987 * package component, so it's a simple error...
989 dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
990 action_name( action & ACTION_MASK ), name
994 /* ...but here, the user specified only the package name,
995 * which implicitly applies to all associated components;
996 * since some may be installed, prefer to issue a warning
997 * in respect of any which aren't.
999 const char *extname = component->GetPropVal( class_key, "" );
1000 char full_package_name[2 + strlen( name ) + strlen( extname )];
1001 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1003 dmh_control( DMH_BEGIN_DIGEST );
1004 dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
1005 extname = action_name( action & ACTION_MASK ), full_package_name
1007 dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
1010 dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
1013 dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
1016 dmh_control( DMH_END_DIGEST );
1020 else if( upgrade && (upgrade != installed) )
1022 /* There is an installed version, but an upgrade to a newer
1023 * version is available; when performing ACTION_UPGRADE...
1025 if( (action & ACTION_MASK) == ACTION_UPGRADE )
1027 * ...we must recursively resolve any dependencies...
1029 ResolveDependencies( upgrade,
1030 Schedule( with_download( action ), latest )
1033 else if( (action & ACTION_MASK) == ACTION_REMOVE )
1035 /* ...while for ACTION_REMOVE, we have little to do,
1036 * beyond scheduling the removal; (we don't extend the
1037 * scope of a remove request to prerequisite packages,
1038 * so there is no need to resolve dependencies)...
1040 latest.SelectPackage( installed );
1041 Schedule( action, latest );
1044 { /* ...but, we decline to proceed with ACTION_INSTALL
1045 * unless the --reinstall option is enabled...
1047 if( pkgOptions()->Test( OPTION_REINSTALL ) )
1049 /* ...in which case, we resolve dependencies for,
1050 * and reschedule a reinstallation of the currently
1051 * installed version...
1053 latest.SelectPackage( installed );
1054 ResolveDependencies( installed,
1055 Schedule( with_download( action | ACTION_REMOVE ), latest )
1059 { /* ...otherwise, we reformulate the appropriate
1060 * fully qualified package name...
1062 const char *extname = ( component != NULL )
1063 ? component->GetPropVal( class_key, "" )
1065 char full_package_name[2 + strlen( name ) + strlen( extname )];
1066 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1068 * ...which we then incorporate into an advisory
1069 * diagnostic message, which serves both to inform
1070 * the user of this error condition, and also to
1071 * suggest appropriate corrective action.
1073 dmh_control( DMH_BEGIN_DIGEST );
1074 dmh_notify( DMH_ERROR, "%s: package is already installed\n",
1077 dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
1080 dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
1083 dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n"
1085 dmh_control( DMH_END_DIGEST );
1090 { /* In this case, the package is already installed,
1091 * and no more recent release is available; we still
1092 * recursively resolve its dependencies, to capture
1093 * any potential upgrades for them.
1095 if( latest.Selection() == NULL )
1096 dmh_notify_no_match( name, package, bounds_specification );
1098 ResolveDependencies( upgrade, Schedule( action, latest ));
1102 if( (component = component->FindNextAssociate( component_key )) != NULL )
1104 * When evaluating a component-package, we extend our
1105 * evaluation, to consider for any further components of
1106 * the current package.
1108 release = component;
1113 /* We found no information on the requested package;
1114 * diagnose as a non-fatal error.
1116 dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
1119 void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )
1121 /* Wrapper function to retrieve the list of all installed packages,
1122 * passing each entry in turn to the standard task scheduler. We
1123 * begin by locating the first sysroot entry in the XML database...
1125 pkgXmlNode *sysroot = GetRoot()->FindFirstAssociate( sysroot_key );
1127 /* ...then, while we have sysroots to examine...
1129 while( sysroot != NULL )
1131 /* ...we retrieve the first package installation record within
1132 * the current sysroot data set.
1134 pkgXmlNode *package = sysroot->FindFirstAssociate( installed_key );
1136 /* Within each sysroot, until we've retrieved all embedded
1137 * installation records...
1139 while( package != NULL )
1141 /* ...we read the canonical tarname for the package,
1142 * and when it is appropriately specified...
1144 const char *tarname = package->GetPropVal( tarname_key, NULL );
1145 if( tarname != NULL )
1147 /* ...we decode it, to determine the package name,
1148 * subsystem name and component class.
1150 pkgSpecs decode( tarname );
1151 const char *pkgname = decode.GetPackageName();
1152 const char *sysname = decode.GetSubSystemName();
1153 const char *cptname = decode.GetComponentClass();
1155 /* From these three, we need to reconstruct an effective
1156 * package name for the scheduler look-up; this reconstruction
1157 * is performed using the following formatted buffer.
1159 const char *fmt = "%s-%s";
1160 char refname[3 + strlen( sysname ) + strlen( pkgname ) + strlen( cptname )];
1161 if( FindPackageByName( pkgname, sysname ) == NULL )
1163 /* The package name alone is insufficient for a successful
1164 * look-up; assume that the effective package name has been
1165 * defined by prefixing the sysroot name.
1167 sprintf( refname, fmt, sysname, pkgname );
1170 if( cptname != NULL )
1172 /* A fully qualified logical package name should include
1173 * the component class name, abstracted from the canonical
1174 * tarname, and appended to the package name.
1176 sprintf( refname, fmt, pkgname, cptname );
1180 /* Having constructed the effective logical package name,
1181 * we schedule the requested action on the package...
1183 Schedule( action, pkgname );
1185 /* ...then move on to the next installed package, if any,
1186 * within the current sysroot data set...
1188 package = package->FindNextAssociate( installed_key );
1190 /* ...and ultimately, to the next sysroot, if any, in the
1193 sysroot = sysroot->FindNextAssociate( sysroot_key );
1197 /* $RCSfile$: end of file */