OSDN Git Service

Support options set by preferences assigned within profile.xml
[mingw/mingw-get.git] / src / pkgdeps.cpp
1 /*
2  * pkgdeps.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, 2012, MinGW Project
8  *
9  *
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.
14  *
15  *
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.
21  *
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.
28  *
29  */
30 #include <string.h>
31
32 #include "dmh.h"
33 #include "debug.h"
34
35 #include "pkginfo.h"
36 #include "pkgbase.h"
37 #include "pkgkeys.h"
38 #include "pkgtask.h"
39 #include "pkgopts.h"
40
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.
46  */
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)
50
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...
54  */
55 EXTERN_C const char *pkgMsgUnknownPackage( void );
56
57 const char *pkgMsgUnknownPackage( void )
58 {
59   /* FIXME: (see note above); return English language only, for now.
60    */
61   return "%s: unknown package\n";
62 }
63
64 /* Provide a convenience macro for declaring static inline functions
65  * which we want to always expand inline.
66  */
67 #define STATIC_INLINE   static inline __attribute__((__always_inline__))
68
69 static bool is_installed( pkgXmlNode *release )
70 {
71   /* Helper to check installation status of a specified package release.
72    */
73   const char *status;
74   /*
75    * First, check for any 'installed' attribute which may have been
76    * explicitly specified for the 'release' record...
77    */
78   if( (status = release->GetPropVal( installed_key, NULL )) != NULL )
79     /*
80      * ...immediately returning the status deduced from it,
81      * when present.
82      */
83     return (strcmp( status, yes_value ) == 0);
84
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...
88    */
89   const char *pkgname;
90   if( ((pkgname = release->GetPropVal( tarname_key, NULL )) != NULL)
91   &&   (release->GetInstallationRecord( pkgname ) != NULL)  )
92   {
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...
96      */
97     release->SetAttribute( installed_key, yes_value );
98     return true;
99   }
100
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.
105    */
106   release->SetAttribute( installed_key, no_value );
107   return false;
108 }
109
110 pkgXmlNode *pkgXmlNode::GetInstallationRecord( const char *pkgname )
111 {
112   /* Retrieve the installation record, if any, for the package
113    * specified by fully qualified canonical 'pkgname'.
114    *
115    * First, break down the specified package name, and retrieve
116    * the sysroot database entry for its associated subsystem.
117    */
118   pkgXmlNode *sysroot;
119   pkgSpecs lookup( pkgname );
120   if( (sysroot = GetSysRoot( lookup.GetSubSystemName() )) != NULL )
121   {
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.
125      */
126     pkgXmlNode *pkg = sysroot->FindFirstAssociate( installed_key );
127     while( pkg != NULL )
128     {
129       /* We found an installed package entry; check if it has
130        * the correct canonical name...
131        */
132       const char *installed = pkg->GetPropVal( tarname_key, NULL );
133       if( (installed != NULL) && (strcmp( installed, pkgname ) == 0) )
134         /*
135          * ...returning this entry if so...
136          */
137         return pkg;
138
139       /* ...otherwise, move on to the next entry, if any.
140        */
141       pkg = pkg->FindNextAssociate( installed_key );
142     }
143   }
144
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.
147    */
148   return NULL;
149 }
150
151 const char *pkgXmlNode::GetContainerAttribute( const char *key, const char *sub )
152 {
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".
158    */
159   pkgXmlNode *pkg = this;
160   pkgXmlNode *root = pkg->GetDocumentRoot();
161   while( pkg != NULL )
162   {
163     /* We haven't yet tried to search beyond the document root;
164      * try matching "key" to an attribute of the current element...
165      */
166     const char *retval = pkg->GetPropVal( key, NULL );
167     if( retval != NULL )
168       /*
169        * ...returning its value, if such an attribute is found...
170        */
171       return retval;
172
173     /* ...otherwise,
174      * take a further step back towards the document root.
175      */
176     pkg = (pkg == root) ? NULL : pkg->GetParent();
177   }
178
179   /* If we get to here, then no element with the required "key"
180    * attribute could be found; substitute the specified default.
181    */
182   return sub;
183 }
184
185 DEBUG_INVOKED static int indent = -1;
186
187 DEBUG_INVOKED static void
188 DEBUG_INVOKED show_required( pkgSpecs *req )
189 DEBUG_INVOKED {
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) );
193 DEBUG_INVOKED }
194
195 static inline
196 bool is_abi_compatible( pkgSpecs *refdata, const char *version )
197 {
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.
201    */
202   const char *ref_version;
203   if( (ref_version = refdata->GetComponentVersion()) == NULL )
204     /*
205      * Here, confirm that both are unversioned...
206      */
207     return (version == NULL);
208
209   /* ...otherwise, fall through to check that both bear IDENTICALLY
210    * the same ABI version number.
211    */
212   return ((version != NULL) && (strcmp( version, ref_version ) == 0));
213 }
214
215 STATIC_INLINE unsigned long action_class
216 ( unsigned long requested, unsigned long viable )
217 {
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.
222    */
223   return viable ? requested : ACTION_RECURSIVE_UPGRADE;
224 }
225
226 STATIC_INLINE unsigned long with_request_flags( unsigned long request )
227 {
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.
231    */
232   return request & ~(ACTION_MASK | ACTION_DOWNLOAD);
233 }
234
235 STATIC_INLINE unsigned long with_download( unsigned long action_code )
236 {
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.
240    */
241   return  action_code | (ACTION_DOWNLOAD);
242 }
243
244 STATIC_INLINE unsigned long promote
245 ( unsigned long request, unsigned long action_code )
246 {
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.
250    */
251   return with_request_flags( request ) | with_download( action_code );
252 }
253
254 void
255 pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
256 {
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.
262    */
263   pkgSpecs *refdata = NULL;
264   pkgXmlNode *refpkg = package;
265
266   DEBUG_INVOKED ++indent;
267
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...
271    */
272   int request_mode = pkgOptions()->Test( OPTION_ALL_DEPS );
273   if( match_if_explicit( package->ArchiveName(), value_none ) )
274     /*
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.
278      */
279     request_mode |= OPTION_RECURSIVE;
280   request_mode |= request & ACTION_MASK;
281
282   while( package != NULL )
283   {
284     /* We have a valid XML entity, which may identify dependencies;
285      * check if it includes any "requires" specification...
286      */
287     pkgXmlNode *dep = package->FindFirstAssociate( requires_key );
288     while( dep != NULL )
289     {
290       /* We found a dependency specification...
291        * Initially, assume this package is not installed.
292        */
293       pkgXmlNode *installed = NULL;
294       unsigned installed_is_viable = 0;
295
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...
299        */
300       if( refdata == NULL )
301       {
302         /* ...but we deferred that until we knew for sure that it would
303          * be needed; it is, so parse it now.
304          */
305         const char *refname;
306         if( (refname = refpkg->GetPropVal( tarname_key, NULL )) != NULL )
307         {
308           DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
309               dmh_printf( "%*s%s: resolve dependencies\n", indent, "", refname )
310             );
311           refdata = new pkgSpecs( refname );
312         }
313       }
314
315       /* Identify the prerequisite package, from its canonical name...
316        */
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 )
321         );
322       /*
323        * (Both the package name, and subsystem if specified, must match)...
324        */
325       selected = FindPackageByName( req.GetPackageName(), req.GetSubSystemName() );
326
327       /* When we've identified the appropriate package...
328        */
329       if( selected != NULL )
330       {
331         /* ...and, more significantly, the appropriate component package,
332          * where applicable...
333          */
334         pkgXmlNode *component; const char *reqclass;
335         if( (reqclass = req.GetComponentClass()) == NULL )
336           reqclass = value_unknown;
337
338         if( (component = selected->FindFirstAssociate( component_key )) == NULL )
339           /*
340            * ...but if no separate component package exists,
341            * consider the parent package itself, as sole component.
342            */
343           component = selected;
344
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...
348          */
349         selected = NULL;
350         while( component != NULL )
351         {
352           /* ...by stepping through the "releases" of this component package...
353           */
354           pkgXmlNode *required = component->FindFirstAssociate( release_key );
355           while( required != NULL )
356           {
357             /* ...noting if we find one already marked as "installed"...
358             */
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 )
364               );
365             if( (tstclass = tst.GetComponentClass()) == NULL )
366               tstclass = value_unknown;
367
368             if( is_installed( required ) && (strcmp( tstclass, reqclass ) == 0)
369             /*
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).
375              */
376             &&  is_abi_compatible( &tst, req.GetComponentVersion() )  )
377             {
378               installed = required;
379               DEBUG_INVOKED already_installed = " (already installed)";
380             }
381             /* ...and identify the most suitable candidate "release"
382              * to satisfy the current dependency...
383              */
384             if( wanted.SelectIfMostRecentFit( required ) == required )
385               selected = component = required;
386
387             if( required == installed )
388               installed_is_viable = wanted.HasAttribute( ACTION_MAY_SELECT );
389
390             DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
391                 dmh_printf( "%s%s\n", wanted.HasAttribute( ACTION_MAY_SELECT )
392                   ? ": viable candidate"
393                   : "",
394                   already_installed
395                 )
396               );
397
398             /* ...continuing, until all available "releases"
399              * have been evaluated accordingly.
400              */
401             required = required->FindNextAssociate( release_key );
402           }
403
404           /* Where multiple component packages do exist,
405            * continue until all have been inspected.
406            */
407           component = component->FindNextAssociate( component_key );
408         }
409
410         /* We have now identified the most suitable candidate package,
411          * to resolve the current dependency...
412          */
413         if( installed )
414         {
415           /* ...this package is already installed, so we may schedule
416            * a resolved dependency match, with no pending action...
417            */
418           unsigned long fallback = with_request_flags( request );
419           switch( action_class( request_mode, installed_is_viable ) )
420           {
421             /* ...except that...
422              */
423             case ACTION_RECURSIVE_REINSTALL:
424               /*
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...
429                */
430               wanted.SelectPackage( selected = installed );
431               /*
432                * ...falling through to...
433                */
434
435             case ACTION_RECURSIVE_REPLACE:
436             case ACTION_RECURSIVE_UPGRADE:
437               /*
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.
441                */
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 )
445                     )
446                 );
447               wanted.SelectPackage( installed, to_remove );
448               fallback |= with_download( ACTION_UPGRADE );
449               break;
450
451             default:
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
455                * installed version.
456                */
457               wanted.SelectPackage( selected = installed );
458           }
459
460           /* Schedule the appropriate fallback action, (which may be none),
461            * for the already installed package.
462            */
463           rank = Schedule( fallback, wanted, rank );
464         }
465
466 #       if 0
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
471          */
472         else if( ((request & ACTION_MASK) == ACTION_INSTALL)
473           /*
474            * The required package is not installed, so when
475            * we are performing an installation, ...
476            */
477         || ((request & (ACTION_PRIMARY | ACTION_INSTALL)) == ACTION_INSTALL) )
478 #       endif
479         else if( (request & ACTION_INSTALL) == ACTION_INSTALL )
480         {
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).
485            */
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 )
490                 )
491             );
492           rank = Schedule( promote( request, ACTION_INSTALL ), wanted, rank );
493         }
494
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...
499          */
500         if( (request & ACTION_INSTALL) != 0 )
501           ResolveDependencies( selected, rank );
502       }
503
504       if( selected == NULL )
505       {
506         /* No package matching the selection criteria could be found;
507          * report a dependency resolution failure in respect of each
508          * specified criterion...
509          */
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 );
512
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 )
517           {
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" );
520           }
521         dmh_notify( DMH_ERROR, "please report this to the package maintainer\n" );
522         dmh_control( DMH_END_DIGEST );
523       }
524
525       /* Continue, until all prerequisites of the current package
526        * have been evaluated.
527        */
528       dep = dep->FindNextAssociate( requires_key );
529     }
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.
535      */
536     package = (package == GetRoot()) ? NULL : package->GetParent();
537   }
538   DEBUG_INVOKED --indent;
539   delete refdata;
540 }
541
542 STATIC_INLINE bool if_noref( const char *name )
543 {
544   /* Helper function, used exclusively by the following assert_unmatched()
545    * function; it is used to confirm that "name" represents nothing.
546    */
547   return (name == NULL) || (*name == '\0');
548 }
549
550 STATIC_INLINE bool if_match( const char *ref, const char *name )
551 {
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".
555    */
556   return (name != NULL) && (strcmp( ref, name ) == 0);
557 }
558
559 STATIC_INLINE bool if_alias( const char *ref, const char *list )
560 {
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".
564    */
565   return (list != NULL) && has_keyword( ref, list );
566 }
567
568 STATIC_INLINE bool assert_unmatched
569 ( const char *ref, const char *val, const char *name, const char *alias )
570 {
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.
577    */
578   return (ref == NULL)
579
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.
582        */
583       !( if_noref( val ) && if_noref( name ) && if_noref( alias ))
584
585     : /* Otherwise, when "ref" is not NULL, then a match is identified when
586        * any one candidate is found to match.
587        */
588       !( if_match( ref, val ) || if_match( ref, name ) || if_alias( ref, alias ));
589 }
590
591 static
592 pkgXmlNode *assert_installed( pkgXmlNode *current, pkgXmlNode *installed )
593 {
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
598    * removed).
599    *
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.
604    */
605   if( current && (installed == NULL) )
606   {
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.
612      *
613      * Starting from the sysroot record for the specified release...
614      */
615     pkgXmlNode *sysroot; const char *tarname;
616     pkgSpecs lookup( current->GetPropVal( tarname_key, NULL ) );
617     if( (sysroot = current->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
618     {
619       /* ...identify the first, if any, package installation record.
620        */
621       pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
622       if( ref != NULL )
623       {
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.
627          */
628         const char *refname = lookup.GetPackageName();
629         const char *cptname = lookup.GetComponentClass();
630         const char *version = lookup.GetComponentVersion();
631
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.
636          */
637         const char *pkgname = current->GetContainerAttribute( name_key );
638         const char *alias = current->GetContainerAttribute( alias_key );
639
640         /* For each candidate installation record found...
641          */
642         while( ref != NULL )
643         {
644           /* ...check if it matches the look-up criteria.
645            */
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 )  )
650             /*
651              * This candidate isn't a match; try the next, if any...
652              */
653             ref = ref->FindNextAssociate( installed_key );
654
655           else
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...
660              */
661             if( (installed = new pkgXmlNode( release_key )) != NULL )
662             {
663               installed->SetAttribute( tarname_key, tarname );
664               installed->SetAttribute( installed_key, value_yes );
665               if( (ref = current->GetParent()) != NULL )
666                 installed = ref->AddChild( installed );
667             }
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.
671              */
672             ref = NULL;
673           }
674         }
675       }
676     }
677   }
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.
681    */
682   return installed;
683 }
684
685 void pkgActionItem::ConfirmInstallationStatus()
686 {
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).
691    */
692   selection[to_remove]
693     = assert_installed( selection[to_install], selection[to_remove] );
694 }
695
696 static inline
697 const char *get_version_bounds( const char *name )
698 {
699   /* Helper to locate any version bounds specification which may
700    * have been appended to a package name command line argument.
701    */
702   if( (name != NULL) && *name )
703     /*
704      * If the name is specified, and not zero-length, then
705      * the bounds specification begins at the first '<', '=',
706      * or '>' character, (if any)...
707      */
708     do { if( (*name == '<') || (*name == '=') || (*name == '>') )
709            /*
710             * ...in which case, we return that location.
711             */
712            return name;
713        } while( *++name );
714
715   /* Otherwise we fall through, returning NULL to indicate
716    * that no bounds specification is present.
717    */
718   return NULL;
719 }
720
721 void pkgActionItem::ApplyBounds( pkgXmlNode *release, const char *bounds )
722 {
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.
727    */
728   const char *refname;
729   pkgSpecs refspec( release );
730
731   while( (bounds != NULL) && *bounds )
732   {
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)...
736      */
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;
740     switch( *bounds )
741     {
742       /* ...identifying it as...
743        */
744       case '=':
745         /*
746          * ...an equality requirement...
747          */
748         condition = eq_key;
749         ++bounds;
750         break;
751
752       case '<':
753         if( *++bounds == '=' )
754         {
755           /* ...less than or equal
756            * (<=; inclusive upper bound)...
757            */
758           condition = le_key;
759           ++bounds;
760         }
761         else
762           /* ...less than (exclusive upper bound)...
763            */
764           condition = lt_key;
765         break;
766
767       case '>':
768         if( *++bounds == '=' )
769         {
770           /* ...greater than or equal
771            * (>=; inclusive lower bound)...
772            */
773           condition = ge_key;
774           ++bounds;
775         }
776         else
777           /* ...or greater than (exclusive lower bound).
778            */
779           condition = gt_key;
780         break;
781     }
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).
786           */
787          *++p = ((*bounds == '<') || (*bounds == '=') || (*bounds == '>'))
788            ? '\0' : *bounds;
789          if( *p ) ++bounds;
790        } while( *p );
791
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...
795      */
796     strcpy( p, "-y.z" );
797     pkgSpecs usrspec( spec_string );
798
799     /* ...then extract the version fields of interest, and insert them
800      * into the actual working reference specification...
801      */
802     refspec.SetPackageVersion( usrspec.GetPackageVersion() );
803     refspec.SetPackageBuild( usrspec.GetPackageBuild() );
804     if( (refname = usrspec.GetSubSystemVersion()) != NULL )
805     {
806       /* ...including the subsystem version, if any, which the user may
807        * have specified...
808        */
809       refspec.SetSubSystemVersion( refname );
810       refspec.SetSubSystemBuild( usrspec.GetSubSystemBuild() );
811     }
812     else
813       /* ...or allowing a wild-card match otherwise.
814        */
815       refspec.SetSubSystemVersion( "*" );
816
817     /* Convert the reference specification to "tarname" format...
818      */
819     if( (refname = refspec.GetTarName()) != NULL )
820     {
821       /* ...and construct a temporary "requires" specification from it.
822        */
823       pkgXmlNode requisite( requires_key );
824       requisite.SetAttribute( condition, refname );
825
826       /* Set the action item requirements to honour this...
827        */
828       SetRequirements( &requisite, &refspec );
829
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).
834        */
835       free( (void *)(refname) );
836     }
837   }
838 }
839
840 static void dmh_notify_no_match
841 ( const char *name, pkgXmlNode *package, const char *bounds )
842 {
843   /* Diagnostic helper, called when the user has requested a specific
844    * package version for which there is no exactly matching release.
845    */
846   dmh_control( DMH_BEGIN_DIGEST );
847   dmh_notify( DMH_ERROR, "there is no release matching %s%s\n",
848       name, (bounds == NULL) ? "" : bounds
849     );
850   if( (package = package->FindFirstAssociate( release_key )) != NULL )
851   {
852     /* To assist the user in formalising a more appropriate
853      * version specification...
854      */
855     dmh_notify( DMH_ERROR, "available candidate releases are...\n" );
856     while( package )
857     {
858       /* ...display a list of available release tarballs...
859        */
860       const char *tarname = package->GetPropVal( tarname_key, NULL );
861       if( tarname != NULL )
862         dmh_notify( DMH_ERROR, " %s\n", tarname );
863
864       /* ...cycling, until all have been identified.
865        */
866       package = package->FindNextAssociate( release_key );
867     }
868   }
869   dmh_control( DMH_END_DIGEST );
870 }
871
872 void pkgXmlDocument::Schedule( unsigned long action, const char* name )
873 {
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.
877    */
878   char scratch_pad[strlen( name )];
879   const char *bounds_specification = get_version_bounds( name );
880   if( bounds_specification != NULL )
881   {
882     /* Separate any version bounds specification from
883      * the original package name specification.
884      */
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';
888   }
889
890   pkgXmlNode *release;
891   if( (release = FindPackageByName( name )) != NULL )
892   {
893     /* We found the specification for the named package...
894      */
895     pkgXmlNode *component = release->FindFirstAssociate( component_key );
896     if( component != NULL )
897       /*
898        * When it is subdivided into component-packages,
899        * we need to consider each as a possible candidate
900        * for task scheduling.
901        */
902       release = component;
903
904     while( release != NULL )
905     {
906       /* Within each candidate package or component-package...
907        */
908       pkgXmlNode *package = release;
909       if( (release = release->FindFirstAssociate( release_key )) != NULL )
910       {
911         /* ...initially assume it is not installed, and that
912          * no installable upgrade is available.
913          */
914         pkgActionItem latest;
915         pkgXmlNode *installed = NULL, *upgrade = NULL;
916
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.
921          */
922         request = action;
923
924         /* Any action request processed here is, by definition,
925          * a request for a primary action; mark it as such.
926          */
927         action |= ACTION_PRIMARY;
928
929         /* When the user has given a version bounds specification,
930          * then we must assign appropriate action item requirements.
931          */
932         if( bounds_specification != NULL )
933           latest.ApplyBounds( release, bounds_specification );
934
935         /* For each candidate release in turn...
936          */
937         while( release != NULL )
938         {
939           /* ...inspect it to identify any which is already installed,
940            * and also the latest available...
941            */
942           if( is_installed( release ) )
943             /*
944              * ...i.e. here we have identified a release
945              * which is currently installed...
946              */
947             latest.SelectPackage( installed = release, to_remove );
948
949           if( latest.SelectIfMostRecentFit( release ) == release )
950             /*
951              * ...while this is the most recent we have
952              * encountered so far.
953              */
954             upgrade = release;
955
956           /* Continue with the next specified release, if any.
957            */
958           release = release->FindNextAssociate( release_key );
959         }
960
961         if( (installed = assert_installed( upgrade, installed )) == NULL )
962         {
963           /* There is no installed version...
964            * therefore, there is nothing to do for any action
965            * other than ACTION_INSTALL...
966            */
967           if( (action & ACTION_MASK) == ACTION_INSTALL )
968           {
969             /*
970              * ...in which case, we must recursively resolve
971              * any dependencies for the scheduled "upgrade".
972              */
973             if( latest.Selection() == NULL )
974               dmh_notify_no_match( name, package, bounds_specification );
975             else
976               ResolveDependencies(
977                   upgrade, Schedule( with_download( action ), latest )
978                 );
979           }
980           else
981           { /* attempting ACTION_UPGRADE or ACTION_REMOVE
982              * is an error; diagnose it.
983              */
984             if( component == NULL )
985               /*
986                * In this case, the user explicitly specified a single
987                * package component, so it's a simple error...
988                */
989               dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
990                   action_name( action & ACTION_MASK ), name
991                 );
992             else
993             {
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.
998                */
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 );
1002
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
1006                 );
1007               dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
1008                   full_package_name
1009                 );
1010               dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
1011                   full_package_name
1012                 );
1013               dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
1014                   full_package_name
1015                 );
1016               dmh_control( DMH_END_DIGEST );
1017             }
1018           }
1019         }
1020         else if( upgrade && (upgrade != installed) )
1021         {
1022           /* There is an installed version, but an upgrade to a newer
1023            * version is available; when performing ACTION_UPGRADE...
1024            */
1025           if( (action & ACTION_MASK) == ACTION_UPGRADE )
1026             /*
1027              * ...we must recursively resolve any dependencies...
1028              */
1029             ResolveDependencies( upgrade,
1030                 Schedule( with_download( action ), latest )
1031               );
1032
1033           else if( (action & ACTION_MASK) == ACTION_REMOVE )
1034           {
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)...
1039              */
1040             latest.SelectPackage( installed );
1041             Schedule( action, latest );
1042           }
1043           else
1044           { /* ...but, we decline to proceed with ACTION_INSTALL
1045              * unless the --reinstall option is enabled...
1046              */
1047             if( pkgOptions()->Test( OPTION_REINSTALL ) )
1048             {
1049               /* ...in which case, we resolve dependencies for,
1050                * and reschedule a reinstallation of the currently
1051                * installed version...
1052                */
1053               latest.SelectPackage( installed );
1054               ResolveDependencies( installed,
1055                   Schedule( with_download( action | ACTION_REMOVE ), latest )
1056                 );
1057             }
1058             else
1059             { /* ...otherwise, we reformulate the appropriate
1060                * fully qualified package name...
1061                */
1062               const char *extname = ( component != NULL )
1063                 ? component->GetPropVal( class_key, "" )
1064                 : "";
1065               char full_package_name[2 + strlen( name ) + strlen( extname )];
1066               sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1067               /*
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.
1072                */
1073               dmh_control( DMH_BEGIN_DIGEST );
1074               dmh_notify( DMH_ERROR, "%s: package is already installed\n",
1075                   full_package_name
1076                 );
1077               dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
1078                   full_package_name
1079                 );
1080               dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
1081                   full_package_name
1082                 );
1083               dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n" 
1084                 );
1085               dmh_control( DMH_END_DIGEST );
1086             }
1087           }
1088         }
1089         else
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.
1094            */
1095           if( latest.Selection() == NULL )
1096             dmh_notify_no_match( name, package, bounds_specification );
1097           else
1098             ResolveDependencies( upgrade, Schedule( action, latest ));
1099         }
1100       }
1101
1102       if( (component = component->FindNextAssociate( component_key )) != NULL )
1103         /*
1104          * When evaluating a component-package, we extend our
1105          * evaluation, to consider for any further components of
1106          * the current package.
1107          */
1108         release = component;
1109     }
1110   }
1111
1112   else
1113     /* We found no information on the requested package;
1114      * diagnose as a non-fatal error.
1115      */
1116     dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
1117 }
1118
1119 void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )
1120 {
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...
1124    */
1125   pkgXmlNode *sysroot = GetRoot()->FindFirstAssociate( sysroot_key );
1126
1127   /* ...then, while we have sysroots to examine...
1128    */
1129   while( sysroot != NULL )
1130   {
1131     /* ...we retrieve the first package installation record within
1132      * the current sysroot data set.
1133      */
1134     pkgXmlNode *package = sysroot->FindFirstAssociate( installed_key );
1135
1136     /* Within each sysroot, until we've retrieved all embedded
1137      * installation records...
1138      */
1139     while( package != NULL )
1140     {
1141       /* ...we read the canonical tarname for the package,
1142        * and when it is appropriately specified...
1143        */
1144       const char *tarname = package->GetPropVal( tarname_key, NULL );
1145       if( tarname != NULL )
1146       {
1147         /* ...we decode it, to determine the package name,
1148          * subsystem name and component class.
1149          */
1150         pkgSpecs decode( tarname );
1151         const char *pkgname = decode.GetPackageName();
1152         const char *sysname = decode.GetSubSystemName();
1153         const char *cptname = decode.GetComponentClass();
1154
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.
1158          */
1159         const char *fmt = "%s-%s";
1160         char refname[3 + strlen( sysname ) + strlen( pkgname ) + strlen( cptname )];
1161         if( FindPackageByName( pkgname, sysname ) == NULL )
1162         {
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.
1166            */
1167           sprintf( refname, fmt, sysname, pkgname );
1168           pkgname = refname;
1169         }
1170         if( cptname != NULL )
1171         {
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.
1175            */
1176           sprintf( refname, fmt, pkgname, cptname );
1177           pkgname = refname;
1178         }
1179
1180         /* Having constructed the effective logical package name,
1181          * we schedule the requested action on the package...
1182          */
1183         Schedule( action, pkgname );
1184       }
1185       /* ...then move on to the next installed package, if any,
1186        * within the current sysroot data set...
1187        */
1188       package = package->FindNextAssociate( installed_key );
1189     }
1190     /* ...and ultimately, to the next sysroot, if any, in the
1191      * XML database.
1192      */
1193     sysroot = sysroot->FindNextAssociate( sysroot_key );
1194   }
1195 }
1196
1197 /* $RCSfile$: end of file */