2012-07-29 08:49:56 +00:00
/*
* queue . c
* compilation of a list of packages into a world dependency set
*
* Copyright ( c ) 2012 pkgconf authors ( see AUTHORS ) .
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* This software is provided ' as is ' and without any warranty , express or
* implied . In no event shall the authors be liable for any damages arising
* from the use of this software .
*/
2017-09-18 04:38:25 +00:00
# include <libpkgconf/stdinc.h>
2015-09-06 14:35:08 +00:00
# include <libpkgconf/libpkgconf.h>
2012-07-29 08:49:56 +00:00
2016-12-11 05:27:21 +00:00
/*
* ! doc
*
* libpkgconf ` queue ` module
* = = = = = = = = = = = = = = = = = = = = = = = = =
*
* The ` queue ` module provides an interface that allows easily building a dependency graph from an
* arbitrary set of dependencies . It also provides support for doing " preflight " checks on the entire
* dependency graph prior to working with it .
*
* Using the ` queue ` module functions is the recommended way of working with dependency graphs .
*/
2013-03-01 16:24:57 +00:00
typedef struct {
2015-09-06 15:31:21 +00:00
pkgconf_node_t iter ;
2013-03-01 16:24:57 +00:00
char * package ;
2015-09-06 15:51:34 +00:00
} pkgconf_queue_t ;
2013-03-01 16:24:57 +00:00
2016-12-11 05:27:21 +00:00
/*
* ! doc
*
* . . c : function : : void pkgconf_queue_push ( pkgconf_list_t * list , const char * package )
*
* Pushes a requested dependency onto the dependency resolver ' s queue .
*
* : param pkgconf_list_t * list : the dependency resolution queue to add the package request to .
* : param char * package : the dependency atom requested
* : return : nothing
*/
2013-03-01 16:24:57 +00:00
void
2015-09-06 15:51:34 +00:00
pkgconf_queue_push ( pkgconf_list_t * list , const char * package )
2012-07-29 08:49:56 +00:00
{
2015-09-06 15:51:34 +00:00
pkgconf_queue_t * pkgq = calloc ( sizeof ( pkgconf_queue_t ) , 1 ) ;
2012-07-29 08:49:56 +00:00
pkgq - > package = strdup ( package ) ;
2023-08-04 08:23:00 +00:00
pkgconf_node_insert_tail ( & pkgq - > iter , pkgq , list ) ;
2012-07-29 08:49:56 +00:00
}
2012-07-29 08:56:20 +00:00
2016-12-11 05:27:21 +00:00
/*
* ! doc
*
* . . c : function : : bool pkgconf_queue_compile ( pkgconf_client_t * client , pkgconf_pkg_t * world , pkgconf_list_t * list )
*
* Compile a dependency resolution queue into a dependency resolution problem if possible , otherwise report an error .
*
* : param pkgconf_client_t * client : The pkgconf client object to use for dependency resolution .
* : param pkgconf_pkg_t * world : The designated root of the dependency graph .
* : param pkgconf_list_t * list : The list of dependency requests to consider .
* : return : true if the built dependency resolution problem is consistent , else false
* : rtype : bool
*/
2012-07-29 08:56:20 +00:00
bool
2016-12-02 06:29:33 +00:00
pkgconf_queue_compile ( pkgconf_client_t * client , pkgconf_pkg_t * world , pkgconf_list_t * list )
2012-07-29 08:56:20 +00:00
{
2015-09-06 15:31:21 +00:00
pkgconf_node_t * iter ;
2012-07-29 08:56:20 +00:00
2015-09-06 15:31:21 +00:00
PKGCONF_FOREACH_LIST_ENTRY ( list - > head , iter )
2012-07-29 08:56:20 +00:00
{
2015-09-06 15:51:34 +00:00
pkgconf_queue_t * pkgq ;
2012-07-29 08:56:20 +00:00
2013-03-01 16:24:57 +00:00
pkgq = iter - > data ;
2018-03-18 23:01:59 +00:00
pkgconf_dependency_parse ( client , world , & world - > required , pkgq - > package , 0 ) ;
2012-07-29 08:56:20 +00:00
}
2017-12-12 06:21:21 +00:00
return ( world - > required . head ! = NULL ) ;
2012-07-29 08:56:20 +00:00
}
2016-12-11 05:27:21 +00:00
/*
* ! doc
*
* . . c : function : : void pkgconf_queue_free ( pkgconf_list_t * list )
*
* Release any memory related to a dependency resolution queue .
*
* : param pkgconf_list_t * list : The dependency resolution queue to release .
* : return : nothing
*/
2012-07-29 08:56:20 +00:00
void
2015-09-06 15:51:34 +00:00
pkgconf_queue_free ( pkgconf_list_t * list )
2012-07-29 08:56:20 +00:00
{
2015-09-06 15:31:21 +00:00
pkgconf_node_t * node , * tnode ;
2012-07-29 08:56:20 +00:00
2015-09-06 15:31:21 +00:00
PKGCONF_FOREACH_LIST_ENTRY_SAFE ( list - > head , tnode , node )
2012-07-29 08:56:20 +00:00
{
2015-09-06 15:51:34 +00:00
pkgconf_queue_t * pkgq = node - > data ;
2013-03-01 16:24:57 +00:00
2012-07-29 08:56:20 +00:00
free ( pkgq - > package ) ;
free ( pkgq ) ;
}
}
2012-07-29 09:45:21 +00:00
2022-06-26 15:02:37 +00:00
static void
pkgconf_queue_collect_dependents ( pkgconf_client_t * client , pkgconf_pkg_t * pkg , void * data )
{
pkgconf_node_t * node ;
pkgconf_pkg_t * world = data ;
2022-06-26 18:04:42 +00:00
if ( pkg = = world )
return ;
2022-08-08 00:58:39 +00:00
PKGCONF_FOREACH_LIST_ENTRY ( pkg - > required . head , node )
2022-06-26 15:02:37 +00:00
{
2022-08-08 00:58:39 +00:00
pkgconf_dependency_t * flattened_dep ;
2022-06-26 15:02:37 +00:00
2022-08-08 00:58:39 +00:00
flattened_dep = pkgconf_dependency_copy ( client , node - > data ) ;
2022-06-26 15:02:37 +00:00
2022-08-08 00:58:39 +00:00
pkgconf_node_insert ( & flattened_dep - > iter , flattened_dep , & world - > required ) ;
2022-06-26 15:02:37 +00:00
}
2022-08-08 00:58:39 +00:00
if ( client - > flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE )
2022-06-26 15:02:37 +00:00
{
2022-08-03 23:40:04 +00:00
PKGCONF_FOREACH_LIST_ENTRY ( pkg - > requires_private . head , node )
{
pkgconf_dependency_t * flattened_dep ;
2022-06-26 15:02:37 +00:00
2022-08-03 23:40:04 +00:00
flattened_dep = pkgconf_dependency_copy ( client , node - > data ) ;
2022-06-26 15:02:37 +00:00
2022-08-03 23:40:04 +00:00
pkgconf_node_insert ( & flattened_dep - > iter , flattened_dep , & world - > requires_private ) ;
}
2022-06-26 15:02:37 +00:00
}
}
static int
dep_sort_cmp ( const void * a , const void * b )
{
const pkgconf_dependency_t * depA = * ( void * * ) a ;
const pkgconf_dependency_t * depB = * ( void * * ) b ;
2023-08-04 08:19:42 +00:00
return depB - > match - > identifier - depA - > match - > identifier ;
2022-06-26 15:02:37 +00:00
}
static inline void
flatten_dependency_set ( pkgconf_client_t * client , pkgconf_list_t * list )
{
2022-08-04 19:22:49 +00:00
pkgconf_node_t * node , * next ;
2022-06-26 15:02:37 +00:00
pkgconf_dependency_t * * deps = NULL ;
size_t dep_count = 0 , i ;
2022-08-04 19:22:49 +00:00
PKGCONF_FOREACH_LIST_ENTRY_SAFE ( list - > head , next , node )
2022-06-26 15:02:37 +00:00
{
pkgconf_dependency_t * dep = node - > data ;
pkgconf_pkg_t * pkg = pkgconf_pkg_verify_dependency ( client , dep , NULL ) ;
2022-06-26 18:09:22 +00:00
if ( pkg = = NULL )
continue ;
2022-06-26 15:02:37 +00:00
if ( pkg - > serial = = client - > serial )
2022-08-04 19:22:49 +00:00
{
pkgconf_node_delete ( node , list ) ;
pkgconf_dependency_unref ( client , dep ) ;
2022-08-03 23:08:00 +00:00
goto next ;
2022-08-04 19:22:49 +00:00
}
2022-06-26 15:02:37 +00:00
if ( dep - > match = = NULL )
{
PKGCONF_TRACE ( client , " WTF: unmatched dependency %p <%s> " , dep , dep - > package ) ;
abort ( ) ;
}
/* for virtuals, we need to check to see if there are dupes */
for ( i = 0 ; i < dep_count ; i + + )
{
pkgconf_dependency_t * other_dep = deps [ i ] ;
PKGCONF_TRACE ( client , " dedup %s = %s? " , dep - > package , other_dep - > package ) ;
if ( ! strcmp ( dep - > package , other_dep - > package ) )
{
2023-01-21 20:45:29 +00:00
PKGCONF_TRACE ( client , " skipping, " SIZE_FMT_SPECIFIER " deps " , dep_count ) ;
2022-06-26 15:02:37 +00:00
goto next ;
}
}
pkg - > serial = client - > serial ;
/* copy to the deps table */
dep_count + + ;
deps = pkgconf_reallocarray ( deps , dep_count , sizeof ( void * ) ) ;
deps [ dep_count - 1 ] = dep ;
PKGCONF_TRACE ( client , " added %s to dep table " , dep - > package ) ;
2022-08-03 23:08:00 +00:00
next :
pkgconf_pkg_unref ( client , pkg ) ;
2022-06-26 15:02:37 +00:00
}
2022-08-03 23:22:14 +00:00
if ( deps = = NULL )
return ;
2022-06-26 15:02:37 +00:00
qsort ( deps , dep_count , sizeof ( void * ) , dep_sort_cmp ) ;
/* zero the list and start readding */
pkgconf_list_zero ( list ) ;
for ( i = 0 ; i < dep_count ; i + + )
{
pkgconf_dependency_t * dep = deps [ i ] ;
2022-08-16 18:38:46 +00:00
if ( dep - > match = = NULL )
continue ;
2022-06-26 15:02:37 +00:00
memset ( & dep - > iter , ' \0 ' , sizeof ( dep - > iter ) ) ;
pkgconf_node_insert ( & dep - > iter , dep , list ) ;
2023-08-04 08:19:42 +00:00
PKGCONF_TRACE ( client , " slot " SIZE_FMT_SPECIFIER " : dep %s matched to %p<%s> id " SIZE_FMT_SPECIFIER , i , dep - > package , dep - > match , dep - > match - > id , dep - > match - > identifier ) ;
2022-06-26 15:02:37 +00:00
}
free ( deps ) ;
}
2012-07-29 10:36:21 +00:00
static inline unsigned int
2017-01-19 23:32:38 +00:00
pkgconf_queue_verify ( pkgconf_client_t * client , pkgconf_pkg_t * world , pkgconf_list_t * list , int maxdepth )
2012-07-29 10:36:21 +00:00
{
2022-06-26 15:02:37 +00:00
unsigned int result ;
2016-12-01 21:05:03 +00:00
if ( ! pkgconf_queue_compile ( client , world , list ) )
2015-09-06 16:45:00 +00:00
return PKGCONF_PKG_ERRF_DEPGRAPH_BREAK ;
2012-07-29 10:36:21 +00:00
2022-06-26 15:02:37 +00:00
/* collect all the dependencies */
result = pkgconf_pkg_traverse ( client , world , pkgconf_queue_collect_dependents , world , maxdepth , 0 ) ;
if ( result ! = PKGCONF_PKG_ERRF_OK )
return result ;
/* flatten the dependency set using serials.
* we copy the dependencies to a vector , and then erase the list .
* then we copy them back to the list .
*/
+ + client - > serial ;
PKGCONF_TRACE ( client , " flattening requires deps " ) ;
flatten_dependency_set ( client , & world - > required ) ;
2022-06-26 15:16:36 +00:00
+ + client - > serial ;
2022-06-26 15:02:37 +00:00
PKGCONF_TRACE ( client , " flattening requires.private deps " ) ;
flatten_dependency_set ( client , & world - > requires_private ) ;
return PKGCONF_PKG_ERRF_OK ;
2012-07-29 10:36:21 +00:00
}
2022-08-16 19:27:35 +00:00
/*
* ! doc
*
* . . c : function : : void pkgconf_solution_free ( pkgconf_client_t * client , pkgconf_pkg_t * world , int maxdepth )
*
* Removes references to package nodes contained in a solution .
*
* : param pkgconf_client_t * client : The pkgconf client object to use for dependency resolution .
* : param pkgconf_pkg_t * world : The root for the generated dependency graph . Should have PKGCONF_PKG_PROPF_VIRTUAL flag .
* : returns : nothing
*/
void
pkgconf_solution_free ( pkgconf_client_t * client , pkgconf_pkg_t * world )
{
( void ) client ;
if ( world - > flags & PKGCONF_PKG_PROPF_VIRTUAL )
{
pkgconf_dependency_free ( & world - > required ) ;
pkgconf_dependency_free ( & world - > requires_private ) ;
}
}
2022-08-08 09:08:27 +00:00
/*
* ! doc
*
* . . c : function : : bool pkgconf_queue_solve ( pkgconf_client_t * client , pkgconf_list_t * list , pkgconf_pkg_t * world , int maxdepth )
*
* Solves and flattens the dependency graph for the supplied dependency list .
*
* : param pkgconf_client_t * client : The pkgconf client object to use for dependency resolution .
* : param pkgconf_list_t * list : The list of dependency requests to consider .
2022-08-16 19:27:35 +00:00
* : param pkgconf_pkg_t * world : The root for the generated dependency graph , provided by the caller . Should have PKGCONF_PKG_PROPF_VIRTUAL flag .
2022-08-08 09:08:27 +00:00
* : param int maxdepth : The maximum allowed depth for the dependency resolver . A depth of - 1 means unlimited .
* : returns : true if the dependency resolver found a solution , otherwise false .
* : rtype : bool
*/
bool
pkgconf_queue_solve ( pkgconf_client_t * client , pkgconf_list_t * list , pkgconf_pkg_t * world , int maxdepth )
{
/* if maxdepth is one, then we will not traverse deeper than our virtual package. */
if ( ! maxdepth )
maxdepth = - 1 ;
return pkgconf_queue_verify ( client , world , list , maxdepth ) = = PKGCONF_PKG_ERRF_OK ;
}
2016-12-11 05:27:21 +00:00
/*
* ! doc
*
2017-01-19 23:32:38 +00:00
* . . c : function : : void pkgconf_queue_apply ( pkgconf_client_t * client , pkgconf_list_t * list , pkgconf_queue_apply_func_t func , int maxdepth , void * data )
2016-12-11 05:27:21 +00:00
*
* Attempt to compile a dependency resolution queue into a dependency resolution problem , then attempt to solve the problem and
* feed the solution to a callback function if a complete dependency graph is found .
*
2022-08-08 09:08:27 +00:00
* This function should not be used in new code . Use pkgconf_queue_solve instead .
*
2016-12-11 05:27:21 +00:00
* : param pkgconf_client_t * client : The pkgconf client object to use for dependency resolution .
* : param pkgconf_list_t * list : The list of dependency requests to consider .
* : param pkgconf_queue_apply_func_t func : The callback function to call if a solution is found by the dependency resolver .
* : param int maxdepth : The maximum allowed depth for the dependency resolver . A depth of - 1 means unlimited .
* : param void * data : An opaque pointer which is passed to the callback function .
* : returns : true if the dependency resolver found a solution , otherwise false .
* : rtype : bool
*/
2012-07-29 09:45:21 +00:00
bool
2017-01-19 23:32:38 +00:00
pkgconf_queue_apply ( pkgconf_client_t * client , pkgconf_list_t * list , pkgconf_queue_apply_func_t func , int maxdepth , void * data )
2012-07-29 09:45:21 +00:00
{
2022-08-03 22:56:54 +00:00
bool ret = false ;
2015-09-06 16:20:48 +00:00
pkgconf_pkg_t world = {
2016-05-20 07:06:46 +00:00
. id = " virtual:world " ,
2012-07-29 09:45:21 +00:00
. realname = " virtual world package " ,
2017-01-23 05:28:51 +00:00
. flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL ,
2012-07-29 09:45:21 +00:00
} ;
/* if maxdepth is one, then we will not traverse deeper than our virtual package. */
if ( ! maxdepth )
maxdepth = - 1 ;
2022-08-08 09:08:27 +00:00
if ( ! pkgconf_queue_solve ( client , list , & world , maxdepth ) )
2022-08-03 22:56:54 +00:00
goto cleanup ;
2012-07-29 09:45:21 +00:00
2022-06-26 15:02:37 +00:00
/* the world dependency set is flattened after it is returned from pkgconf_queue_verify */
2022-06-26 15:17:08 +00:00
if ( ! func ( client , & world , data , maxdepth ) )
2022-08-03 22:56:54 +00:00
goto cleanup ;
2012-07-29 09:45:21 +00:00
2022-08-03 22:56:54 +00:00
ret = true ;
2012-07-29 09:45:21 +00:00
2022-08-03 22:56:54 +00:00
cleanup :
pkgconf_pkg_free ( client , & world ) ;
return ret ;
2012-07-29 09:45:21 +00:00
}
2012-07-29 10:36:21 +00:00
2016-12-11 05:27:21 +00:00
/*
* ! doc
*
2017-01-19 23:32:38 +00:00
* . . c : function : : void pkgconf_queue_validate ( pkgconf_client_t * client , pkgconf_list_t * list , pkgconf_queue_apply_func_t func , int maxdepth , void * data )
2016-12-11 05:27:21 +00:00
*
* Attempt to compile a dependency resolution queue into a dependency resolution problem , then attempt to solve the problem .
*
* : param pkgconf_client_t * client : The pkgconf client object to use for dependency resolution .
* : param pkgconf_list_t * list : The list of dependency requests to consider .
* : param int maxdepth : The maximum allowed depth for the dependency resolver . A depth of - 1 means unlimited .
* : returns : true if the dependency resolver found a solution , otherwise false .
* : rtype : bool
*/
2012-07-29 10:36:21 +00:00
bool
2017-01-19 23:32:38 +00:00
pkgconf_queue_validate ( pkgconf_client_t * client , pkgconf_list_t * list , int maxdepth )
2012-07-29 10:36:21 +00:00
{
bool retval = true ;
2015-09-06 16:20:48 +00:00
pkgconf_pkg_t world = {
2016-05-20 07:06:46 +00:00
. id = " virtual:world " ,
2012-07-29 10:36:21 +00:00
. realname = " virtual world package " ,
2017-01-23 05:28:51 +00:00
. flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL ,
2012-07-29 10:36:21 +00:00
} ;
/* if maxdepth is one, then we will not traverse deeper than our virtual package. */
if ( ! maxdepth )
maxdepth = - 1 ;
2017-01-19 23:32:38 +00:00
if ( pkgconf_queue_verify ( client , & world , list , maxdepth ) ! = PKGCONF_PKG_ERRF_OK )
2012-07-29 10:36:21 +00:00
retval = false ;
2016-12-02 06:29:33 +00:00
pkgconf_pkg_free ( client , & world ) ;
2012-07-29 10:36:21 +00:00
return retval ;
}