Compare commits

...

29 Commits

Author SHA1 Message Date
Petr Písař 2d26db3a98 doc: Requires.private is used for header inclusion
ci/woodpecker/push/woodpecker Pipeline failed Details
Document that "pkgconf --cflags" inherits paths for including headers
from dependencies listed in Requires.private.

https://github.com/pkgconf/pkgconf/issues/300
https://github.com/pkgconf/pkgconf/issues/352
2024-04-25 23:50:21 -07:00
Pierce a8a492e2e4 Fix typo in README.md
ci/woodpecker/push/woodpecker Pipeline failed Details
2024-04-25 23:49:39 -07:00
Ismael Luceno c583d49a37 pkg.m4: Abort by default if no pkgconf
ci/woodpecker/push/woodpecker Pipeline failed Details
Provide a second optional argument to PKG_PROG_PKG_CONFIG with an action to
take if no pkgconf is found, and default to aborting.

Too many configure scripts in the wild don't handle an empty PKG_CONFIG,
resulting in weird error messages through the run, which often confuse
users.

Authors wishing to fallback to other methods can either return to the old
behavior by specifying [:] as an action, or use [PKG_CONFIG=false] and call
pkg-config macros normally, handling the fallback as any other failure.

Users can override PKG_CONFIG in the command line so this should imply no
regression.

Closes: https://github.com/pkgconf/pkgconf/issues/350
Ref: https://gitlab.freedesktop.org/pkg-config/pkg-config/-/issues/69
Signed-off-by: Ismael Luceno <ismael@iodev.co.uk>
2024-04-25 23:48:18 -07:00
Ariadne Conill 06120a8769 pkgconf 2.2.0 2024-03-27 18:39:44 +00:00
Ariadne Conill 7a8532f849 queue: remove no longer used root package reference when walking dependency graphs 2024-03-27 18:39:43 +00:00
Ariadne Conill a3d7ecb31b Use libpkgconf.so.5 due to ABI changes 2024-03-27 11:31:48 -07:00
Kai Pastor e2a8080cbf Reuse static solution for general solution 2024-03-27 11:15:04 -07:00
Kai Pastor 883b57df47 Cache packages from explicit file requests 2024-03-27 11:15:04 -07:00
Kai Pastor 194dde096b Update expected test results 2024-03-27 11:15:04 -07:00
Kai Pastor 78d53ea012 Revise serials, traversal, flattening
Remove the 'traverse_serial' fields which were added in 2.1.1.
Use the 'serial' field to track the current traversal.
Stop using 'identifier' to sort packages in the flattened solution.
Directly construct the flattened solution by a specific walk which
also preserves the relative order in Requires and Requires.private.
The topological sort is a single list, so don't fill requires_private.
Purely private dependencies are marked in dependency flags.
The ancestor flag is a pkg property, not a client property.
2024-03-27 11:15:04 -07:00
Kai Pastor 4090ba3e0d Clarify tracing messages 2024-03-27 11:15:04 -07:00
Kai Pastor 5eb79f6651 Add tests for lib order 2024-03-27 11:15:04 -07:00
Kai Pastor e7c9bdb317 Don't print 'required by <VIRTUAL-PACKAGE>' 2024-03-27 11:09:16 -07:00
Kai Pastor 4e01010d6f Mark user input in error message 2024-03-27 11:09:16 -07:00
Kai Pastor 6219af9a0a Color private-reachable nodes gray 2024-03-27 11:06:30 -07:00
Kai Pastor eaf1a2d804 Resolve user request for digraph 2024-03-27 11:06:30 -07:00
Kai Pastor d6753888b8 Update test expected results 2024-03-27 11:06:30 -07:00
Kai Pastor f95e8d4428 Print digraph for user request perspective 2024-03-27 11:06:30 -07:00
Kai Pastor db63c8ee0b Show edges for flattened order 2024-03-27 11:06:30 -07:00
Kai Pastor b6e04e2d47 pkgconf 2.1.1
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details
2024-02-04 03:26:15 -08:00
Ariadne Conill 2edf954f3a Update NEWS for pkgconf 2.1.1 2024-02-04 03:25:54 -08:00
Ariadne Conill d8924aced5 Update gitignore to catch all of the autoconf-generated files
ci/woodpecker/push/woodpecker Pipeline was successful Details
2024-02-04 03:16:14 -08:00
Ariadne Conill 80b5011e69 Use traverse_serial to short circuit graph evaluation for already visited nodes.
In our previous attempt to optimize this problem, we did not track the type of the
visit to the node, e.g. whether it came from evaluating Requires or Requires.private,
which resulted in some solutions being correctly incalculated due to greedy optimization
of the dependency graph.

We reintroduce this optimization by adding a second traversal serial as well as
re-introducing the PROPF_VISITED node property as well as a new PROPF_VISITED_PRIVATE
node property flag.  This allows a node to be revisted at maximum two times per
traversal level.

Co-authored-by: Yi Chou <yich@google.com>
2024-02-04 03:04:52 -08:00
Kai Pastor 4a3adf3457 Test digraph completeness for private-libs-duplication
ci/woodpecker/push/woodpecker Pipeline was successful Details
Complements 45073b7.
2024-02-04 02:37:14 -08:00
Kai Pastor 5825e2c6d6 Fix crash on two-word expressions
ci/woodpecker/push/woodpecker Pipeline was successful Details
2023-12-05 10:50:18 -07:00
Kai Pastor b2f8386c32 Fix --modversion output 2023-12-05 10:50:18 -07:00
Kai Pastor 125af82dbe Test --modversion with constraint 2023-12-05 10:50:18 -07:00
Sam James 08f4a9da15 NEWS: fix typo
ci/woodpecker/push/woodpecker Pipeline was successful Details
Signed-off-by: Sam James <sam@gentoo.org>
2023-11-29 12:03:11 -08:00
Stefan Weil a4ecd42bf3 Fix it's -> its
Signed-off-by: Stefan Weil <sw@weilnetz.de>
2023-11-29 12:02:44 -08:00
18 changed files with 382 additions and 177 deletions

49
.gitignore vendored
View File

@ -1,13 +1,19 @@
Makefile
Makefile.in
.dirstamp
stamp-h*
*.h.in~
*.o
*.og
*.op
*.lo
*.gcno
*.gcda
*.gcov
core
.gdb_history
.libs
.deps
*.exe
@ -16,8 +22,8 @@ core
*.orig
*.patch
*.rej
/tests/run.sh
# autoconf stuff
/aclocal.m4
/autom4te.cache/
/buildsys.mk
@ -26,8 +32,47 @@ core
/config.log
/config.status
/configure
/compile
/config.guess
/config.sub
/depcomp
/install-sh
/missing
# programs
/bomtool
/pkgconf
/pkgconf-gcov
/pkgconf-profile
Makefile
# libtool
/libtool
/ltmain.sh
/libpkgconf.la
/m4/libtool.m4
/m4/lt~obsolete.m4
/m4/ltoptions.m4
/m4/ltsugar.m4
/m4/ltversion.m4
# pkgconfig file
/libpkgconf.pc
# autoheader
/libpkgconf/config.h
/libpkgconf/config.h.in
# tests
/Kyuafile
/tests/Kyuafile
/tests/basic
/tests/builtins
/tests/conflicts
/tests/framework
/tests/parser
/tests/provides
/tests/regress
/tests/requires
/tests/sysroot
/tests/test_env.sh
/tests/version

View File

@ -159,7 +159,7 @@ libpkgconf_la_SOURCES = \
libpkgconf/path.c \
libpkgconf/personality.c \
libpkgconf/parser.c
libpkgconf_la_LDFLAGS = -no-undefined -version-info 4:0:0 -export-symbols-regex '^pkgconf_'
libpkgconf_la_LDFLAGS = -no-undefined -version-info 5:0:0 -export-symbols-regex '^pkgconf_'
dist_man_MANS = \
man/pkgconf.1 \

View File

@ -39,7 +39,7 @@ all: pkgconf-lite
libpkgconf/config.h:
@echo '#define PACKAGE_NAME "pkgconf-lite"' >> $@
@echo '#define PACKAGE_BUGREPORT "https://git.dereferenced.org/pkgconf/pkgconf/issues"' >> $@
@echo '#define PACKAGE_VERSION "2.1.0"' >> $@
@echo '#define PACKAGE_VERSION "2.2.0"' >> $@
@echo '#define PACKAGE PACKAGE_NAME " " PACKAGE_VERSION' >> $@
@echo '#define HAVE_STRLCPY' >> $@
@echo '#define HAVE_STRLCAT' >> $@

36
NEWS
View File

@ -1,6 +1,40 @@
Changes from previous version of pkgconf
========================================
Changes from 2.1.1 to 2.2.0:
----------------------------
* libpkgconf SOVERSION is now 5.
* Significant solver rework to flatten both requires and requires.private
dependencies in a single pass. Improves performance slightly and ensures
proper dependency order.
Patches by Kai Pastor.
* Improve `--digraph` output to reflect more of the solver's state in the
rendered dependency graph.
Patches by Kai Pastor.
* Do not reference the graph root by name when presenting error messages about
directly requested dependency nodes.
Patch by Kai Pastor.
Changes from 2.1.0 to 2.1.1:
----------------------------
* Documentation fixes from Sam James and Stefan Weil.
* Fix --modversion with constraints.
Patch by Kai Pastor.
* Reintroduce an optimization to the dependency graph walker which avoids
revisiting already visited nodes.
Patch by Yi Chou with some modifications.
* Add a regression test to check that the dependency flattener is working
as expected.
Patch by Kai Pastor.
Changes from 2.0.3 to 2.1.0:
----------------------------
@ -9,7 +43,7 @@ Changes from 2.0.3 to 2.1.0:
Patches by Kai Pastor.
* Fix warnings with GCC 14 -Walloc-size.
Patch by Sam Jones.
Patch by Sam James.
* Add --solution to the pkgconf CLI to dump the solver state.

View File

@ -50,7 +50,7 @@ As previously mentioned, pkgconf makes optimizations to the linker flags in both
case of static and shared linking in order to avoid overlinking binaries and also
simplifies the `CFLAGS` and `LIBS` output of the pkgconf tool for improved readability.
This functionality depends on the pkg-config module properly declaring it's dependency
This functionality depends on the pkg-config module properly declaring its dependency
tree instead of using `Libs` and `Cflags` fields to directly link against other modules
which have pkg-config metadata files installed.
@ -116,7 +116,7 @@ flags like so:
pkgconf is compiled using [Meson](https://mesonbuild.com) on Windows. In theory, you could also use
Meson to build on UNIX, but this is not recommended at this time as it pkgconf is typically built
Meson to build on UNIX, but this is not recommended at this time as pkgconf is typically built
much earlier than Meson.
$ meson setup build -Dtests=disabled

View File

@ -254,39 +254,71 @@ apply_provides(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int
#ifndef PKGCONF_LITE
static void
print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused)
print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
{
pkgconf_node_t *node;
(void) client;
(void) unused;
pkgconf_pkg_t **last_seen = data;
printf("\"%s\" [fontname=Sans fontsize=8]\n", pkg->id);
if(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
return;
if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE)
printf("\"%s\" [fontname=Sans fontsize=8 fontcolor=gray color=gray]\n", pkg->id);
else
printf("\"%s\" [fontname=Sans fontsize=8]\n", pkg->id);
if (last_seen != NULL)
{
if (*last_seen != NULL)
printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=red]\n", (*last_seen)->id, pkg->id);
*last_seen = pkg;
}
PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node)
{
pkgconf_dependency_t *dep = node->data;
const char *dep_id = (dep->match != NULL) ? dep->match->id : dep->package;
printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg->id, dep->package);
if ((dep->flags & PKGCONF_PKG_DEPF_PRIVATE) == 0)
printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg->id, dep_id);
else
printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep_id);
}
PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node)
{
pkgconf_dependency_t *dep = node->data;
const char *dep_id = (dep->match != NULL) ? dep->match->id : dep->package;
printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep->package);
printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep_id);
}
}
static bool
apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
{
int eflag;
pkgconf_list_t *list = data;
pkgconf_pkg_t *last_seen = NULL;
pkgconf_node_t *iter;
printf("digraph deptree {\n");
printf("edge [color=blue len=7.5 fontname=Sans fontsize=8]\n");
printf("node [fontname=Sans fontsize=8]\n");
printf("\"user:request\" [fontname=Sans fontsize=8]\n");
eflag = pkgconf_pkg_traverse(client, world, print_digraph_node, unused, maxdepth, 0);
PKGCONF_FOREACH_LIST_ENTRY(list->head, iter)
{
pkgconf_queue_t *pkgq = iter->data;
pkgconf_pkg_t *pkg = pkgconf_pkg_find(client, pkgq->package);
printf("\"user:request\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg == NULL ? pkgq->package : pkg->id);
if (pkg != NULL)
pkgconf_pkg_unref(client, pkg);
}
eflag = pkgconf_pkg_traverse(client, world, print_digraph_node, &last_seen, maxdepth, 0);
if (eflag != PKGCONF_PKG_ERRF_OK)
return false;
@ -333,7 +365,12 @@ apply_modversion(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int
pkgconf_dependency_t *dep = world_iter->data;
pkgconf_pkg_t *pkg = dep->match;
if (strcmp(pkg->why, queue_node->package))
const size_t name_len = strlen(pkg->why);
if (name_len > strlen(queue_node->package) ||
strncmp(pkg->why, queue_node->package, name_len) ||
(queue_node->package[name_len] != 0 &&
!isspace(queue_node->package[name_len]) &&
!PKGCONF_IS_OPERATOR_CHAR(queue_node->package[name_len])))
continue;
if (pkg->version != NULL) {
@ -1400,6 +1437,15 @@ cleanup3:
pkgconf_queue_push(&pkgq, package);
pkg_optind++;
}
else if (argv[pkg_optind + 2] == NULL)
{
char packagebuf[PKGCONF_BUFSIZE];
snprintf(packagebuf, sizeof packagebuf, "%s %s", package, argv[pkg_optind + 1]);
pkg_optind += 2;
pkgconf_queue_push(&pkgq, packagebuf);
}
else
{
char packagebuf[PKGCONF_BUFSIZE];
@ -1468,7 +1514,7 @@ cleanup3:
if ((want_flags & PKG_DIGRAPH) == PKG_DIGRAPH)
{
want_flags &= ~(PKG_CFLAGS|PKG_LIBS);
apply_digraph(&pkg_client, &world, NULL, 2);
apply_digraph(&pkg_client, &world, &pkgq, 2);
}
if ((want_flags & PKG_SOLUTION) == PKG_SOLUTION)
@ -1530,20 +1576,8 @@ cleanup3:
printf(" ");
if (!(want_flags & PKG_STATIC))
{
pkgconf_client_set_flags(&pkg_client, pkg_client.flags & ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE);
/* redo the solution for the library set: free the solution itself, and any cached graph nodes */
pkgconf_solution_free(&pkg_client, &world);
pkgconf_cache_free(&pkg_client);
if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth))
{
ret = EXIT_FAILURE;
goto out;
}
}
apply_libs(&pkg_client, &world, NULL, 2);
}

View File

@ -12,7 +12,7 @@ dnl implied. In no event shall the authors be liable for any damages arising
dnl from the use of this software.
AC_PREREQ([2.71])
AC_INIT([pkgconf],[2.1.0],[https://github.com/pkgconf/pkgconf/issues/new])
AC_INIT([pkgconf],[2.2.0],[https://github.com/pkgconf/pkgconf/issues/new])
AC_CONFIG_SRCDIR([cli/main.c])
AC_CONFIG_MACRO_DIR([m4])
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])

View File

@ -51,7 +51,7 @@ The `dependency` module provides support for building `dependency lists` (the ba
.. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list)
Release a dependency list and it's child dependency nodes.
Release a dependency list and its child dependency nodes.
:param pkgconf_list_t* list: The dependency list to release.
:return: nothing

View File

@ -262,7 +262,7 @@ pkgconf_dependency_unref(pkgconf_client_t *client, pkgconf_dependency_t *dep)
*
* .. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list)
*
* Release a dependency list and it's child dependency nodes.
* Release a dependency list and its child dependency nodes.
*
* :param pkgconf_list_t* list: The dependency list to release.
* :return: nothing

View File

@ -81,8 +81,8 @@ typedef struct pkgconf_queue_ pkgconf_queue_t;
#define PKGCONF_FOREACH_LIST_ENTRY_REVERSE(tail, value) \
for ((value) = (tail); (value) != NULL; (value) = (value)->prev)
#define LIBPKGCONF_VERSION 20003
#define LIBPKGCONF_VERSION_STR "2.0.3"
#define LIBPKGCONF_VERSION 20200
#define LIBPKGCONF_VERSION_STR "2.2.0"
struct pkgconf_queue_ {
pkgconf_node_t iter;
@ -137,6 +137,8 @@ struct pkgconf_path_ {
#define PKGCONF_PKG_PROPF_CACHED 0x02
#define PKGCONF_PKG_PROPF_UNINSTALLED 0x08
#define PKGCONF_PKG_PROPF_VIRTUAL 0x10
#define PKGCONF_PKG_PROPF_ANCESTOR 0x20
#define PKGCONF_PKG_PROPF_VISITED_PRIVATE 0x40
struct pkgconf_pkg_ {
int refcount;
@ -278,7 +280,6 @@ PKGCONF_API void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *p
#define PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS 0x4000
#define PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES 0x8000
#define PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES 0x10000
#define PKGCONF_PKG_PKGF_ANCESTOR 0x20000
#define PKGCONF_PKG_DEPF_INTERNAL 0x1
#define PKGCONF_PKG_DEPF_PRIVATE 0x2

View File

@ -775,15 +775,13 @@ pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
{
if ((f = fopen(name, "r")) != NULL)
{
pkgconf_pkg_t *pkg;
PKGCONF_TRACE(client, "%s is a file", name);
pkg = pkgconf_pkg_new_from_file(client, name, f, 0);
if (pkg != NULL)
{
pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
return pkg;
goto out;
}
}
}
@ -1456,7 +1454,11 @@ pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent,
client->already_sent_notice = true;
}
pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id);
if (parent->flags & PKGCONF_PKG_PROPF_VIRTUAL)
pkgconf_error(client, "Package '%s' not found\n", node->package);
else
pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id);
pkgconf_audit_log(client, "%s NOT-FOUND\n", node->package);
}
else if (eflags & PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH)
@ -1487,7 +1489,7 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client,
unsigned int eflags = PKGCONF_PKG_ERRF_OK;
pkgconf_node_t *node, *next;
parent->flags |= PKGCONF_PKG_PKGF_ANCESTOR;
parent->flags |= PKGCONF_PKG_PROPF_ANCESTOR;
PKGCONF_FOREACH_LIST_ENTRY_SAFE(deplist->head, next, node)
{
@ -1509,10 +1511,8 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client,
if (pkgdep == NULL)
continue;
if((pkgdep->flags & PKGCONF_PKG_PKGF_ANCESTOR) != 0)
if((pkgdep->flags & PKGCONF_PKG_PROPF_ANCESTOR) != 0)
{
pkgdep->identifier = ++client->identifier;
/* In this case we have a circular reference.
* We break that by deleteing the circular node from the
* the list, so that we dont create a situation where
@ -1540,14 +1540,12 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client,
pkgconf_audit_log_dependency(client, pkgdep, depnode);
pkgdep->identifier = ++client->identifier;
pkgdep->serial = client->serial;
eflags |= pkgconf_pkg_traverse_main(client, pkgdep, func, data, depth - 1, skip_flags);
next:
pkgconf_pkg_unref(client, pkgdep);
}
parent->flags &= ~PKGCONF_PKG_PKGF_ANCESTOR;
parent->flags &= ~PKGCONF_PKG_PROPF_ANCESTOR;
return eflags;
}
@ -1628,6 +1626,16 @@ pkgconf_pkg_traverse_main(pkgconf_client_t *client,
if (maxdepth == 0)
return eflags;
/* Short-circuit if we have already visited this node.
*/
if (root->serial == client->serial)
return eflags;
root->serial = client->serial;
if (root->identifier == 0)
root->identifier = ++client->identifier;
PKGCONF_TRACE(client, "%s: level %d, serial %"PRIu64, root->id, maxdepth, client->serial);
if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) != PKGCONF_PKG_PROPF_VIRTUAL || (client->flags & PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) != PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL)
@ -1636,21 +1644,23 @@ pkgconf_pkg_traverse_main(pkgconf_client_t *client,
func(client, root, data);
}
if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS))
if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS) && root->conflicts.head != NULL)
{
PKGCONF_TRACE(client, "%s: walking 'Conflicts' list", root->id);
eflags = pkgconf_pkg_walk_conflicts_list(client, root, &root->conflicts);
if (eflags != PKGCONF_PKG_ERRF_OK)
return eflags;
}
PKGCONF_TRACE(client, "%s: walking requires list", root->id);
PKGCONF_TRACE(client, "%s: walking 'Requires' list", root->id);
eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags);
if (eflags != PKGCONF_PKG_ERRF_OK)
return eflags;
if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE)
{
PKGCONF_TRACE(client, "%s: walking requires.private list", root->id);
PKGCONF_TRACE(client, "%s: walking 'Requires.private' list", root->id);
/* XXX: ugly */
client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
@ -1675,6 +1685,9 @@ pkgconf_pkg_traverse(pkgconf_client_t *client,
if (root->flags & PKGCONF_PKG_PROPF_VIRTUAL)
client->serial++;
if ((client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) == 0)
skip_flags |= PKGCONF_PKG_DEPF_PRIVATE;
return pkgconf_pkg_traverse_main(client, root, func, data, maxdepth, skip_flags);
}

View File

@ -103,134 +103,133 @@ pkgconf_queue_free(pkgconf_list_t *list)
}
static void
pkgconf_queue_collect_dependents(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
pkgconf_queue_mark_public(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
{
if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE)
{
pkgconf_list_t *list = data;
pkgconf_node_t *node;
PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
{
pkgconf_dependency_t *dep = node->data;
if (dep->match == pkg)
dep->flags &= ~PKGCONF_PKG_DEPF_PRIVATE;
}
pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE;
PKGCONF_TRACE(client, "%s: updated, public", pkg->id);
}
}
static unsigned int
pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client,
pkgconf_pkg_t *root,
void *data,
int maxdepth);
static inline unsigned int
pkgconf_queue_collect_dependencies_walk(pkgconf_client_t *client,
pkgconf_list_t *deplist,
void *data,
int depth)
{
unsigned int eflags = PKGCONF_PKG_ERRF_OK;
pkgconf_node_t *node;
pkgconf_pkg_t *world = data;
if (pkg == world)
return;
PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node)
{
pkgconf_dependency_t *parent_dep = node->data;
pkgconf_dependency_t *flattened_dep;
flattened_dep = pkgconf_dependency_copy(client, parent_dep);
if ((client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE)
pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->required);
else
pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->requires_private);
}
if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE)
{
PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node)
{
pkgconf_dependency_t *parent_dep = node->data;
pkgconf_dependency_t *flattened_dep;
flattened_dep = pkgconf_dependency_copy(client, parent_dep);
pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->requires_private);
}
}
}
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;
return depB->match->identifier - depA->match->identifier;
}
static inline void
flatten_dependency_set(pkgconf_client_t *client, pkgconf_list_t *list)
{
pkgconf_node_t *node, *next;
pkgconf_dependency_t **deps = NULL;
size_t dep_count = 0, i;
PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
PKGCONF_FOREACH_LIST_ENTRY_REVERSE(deplist->tail, node)
{
pkgconf_dependency_t *dep = node->data;
pkgconf_pkg_t *pkg = pkgconf_pkg_verify_dependency(client, dep, NULL);
pkgconf_dependency_t *flattened_dep;
pkgconf_pkg_t *pkg = dep->match;
if (pkg == NULL)
if (*dep->package == '\0')
continue;
if (pkg->serial == client->serial)
{
pkgconf_node_delete(node, list);
pkgconf_dependency_unref(client, dep);
goto next;
}
if (dep->match == NULL)
if (pkg == 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))
{
PKGCONF_TRACE(client, "skipping, "SIZE_FMT_SPECIFIER" deps", dep_count);
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);
next:
pkgconf_pkg_unref(client, pkg);
}
if (deps == NULL)
return;
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];
if (dep->match == NULL)
if (pkg->serial == client->serial)
continue;
memset(&dep->iter, '\0', sizeof (dep->iter));
pkgconf_node_insert(&dep->iter, dep, list);
if (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE)
pkg->flags |= PKGCONF_PKG_PROPF_VISITED_PRIVATE;
else
pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE;
PKGCONF_TRACE(client, "slot "SIZE_FMT_SPECIFIER": dep %s matched to %p<%s> id %"PRIu64, i, dep->package, dep->match, dep->match->id, dep->match->identifier);
eflags |= pkgconf_queue_collect_dependencies_main(client, pkg, data, depth - 1);
flattened_dep = pkgconf_dependency_copy(client, dep);
pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->required);
}
free(deps);
return eflags;
}
static unsigned int
pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client,
pkgconf_pkg_t *root,
void *data,
int maxdepth)
{
unsigned int eflags = PKGCONF_PKG_ERRF_OK;
if (maxdepth == 0)
return eflags;
/* Short-circuit if we have already visited this node.
*/
if (root->serial == client->serial)
return eflags;
root->serial = client->serial;
if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE)
{
PKGCONF_TRACE(client, "%s: collecting private dependencies, level %d", root->id, maxdepth);
/* XXX: ugly */
const unsigned int saved_flags = client->flags;
client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
eflags = pkgconf_queue_collect_dependencies_walk(client, &root->requires_private, data, maxdepth);
client->flags = saved_flags;
if (eflags != PKGCONF_PKG_ERRF_OK)
return eflags;
}
PKGCONF_TRACE(client, "%s: collecting public dependencies, level %d", root->id, maxdepth);
eflags = pkgconf_queue_collect_dependencies_walk(client, &root->required, data, maxdepth);
if (eflags != PKGCONF_PKG_ERRF_OK)
return eflags;
PKGCONF_TRACE(client, "%s: finished, %s", root->id, (root->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) ? "private" : "public");
return eflags;
}
static inline unsigned int
pkgconf_queue_collect_dependencies(pkgconf_client_t *client,
pkgconf_pkg_t *root,
void *data,
int maxdepth)
{
++client->serial;
return pkgconf_queue_collect_dependencies_main(client, root, data, maxdepth);
}
static inline unsigned int
pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth)
{
unsigned int result;
const unsigned int saved_flags = client->flags;
pkgconf_pkg_t initial_world = {
.id = "virtual:world",
.id = "user:request",
.realname = "virtual world package",
.flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
};
@ -241,31 +240,39 @@ pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_lis
return PKGCONF_PKG_ERRF_DEPGRAPH_BREAK;
}
/* collect all the dependencies */
result = pkgconf_pkg_traverse(client, &initial_world, pkgconf_queue_collect_dependents, world, maxdepth, 0);
PKGCONF_TRACE(client, "solving");
result = pkgconf_pkg_traverse(client, &initial_world, NULL, NULL, maxdepth, 0);
if (result != PKGCONF_PKG_ERRF_OK)
{
pkgconf_solution_free(client, &initial_world);
return result;
}
PKGCONF_TRACE(client, "flattening");
result = pkgconf_queue_collect_dependencies(client, &initial_world, world, maxdepth);
if (result != PKGCONF_PKG_ERRF_OK)
{
pkgconf_solution_free(client, &initial_world);
return result;
}
if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE)
{
PKGCONF_TRACE(client, "marking public deps");
client->flags &= ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE;
client->flags |= PKGCONF_PKG_PKGF_SKIP_CONFLICTS;
result = pkgconf_pkg_traverse(client, &initial_world, pkgconf_queue_mark_public, &world->required, maxdepth, 0);
client->flags = saved_flags;
if (result != PKGCONF_PKG_ERRF_OK)
{
pkgconf_solution_free(client, &initial_world);
return result;
}
}
/* free the initial solution */
pkgconf_solution_free(client, &initial_world);
/* 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);
++client->serial;
PKGCONF_TRACE(client, "flattening requires.private deps");
flatten_dependency_set(client, &world->requires_private);
return PKGCONF_PKG_ERRF_OK;
}

View File

@ -126,9 +126,10 @@ All dependencies must be satisfied or the pkg-config implementation must not use
the package.
(optional; dependency list)
.It Requires.private
Required dependencies that must be met for the package to be usable for static linking.
Required dependencies that must be met for the package to be usable for header
inclusion and static linking.
All dependencies must be satisfied or the pkg-config implementation must not use
the package for static linking.
the package for header inclusion and static linking.
(optional; dependency list)
.It Conflicts
Dependencies that must not be met for the package to be usable.

View File

@ -1,5 +1,5 @@
project('pkgconf', 'c',
version : '2.1.0',
version : '2.2.0',
license : 'ISC',
meson_version : '>=0.49',
default_options : ['c_std=c99'],
@ -96,8 +96,8 @@ libpkgconf = library('pkgconf',
'libpkgconf/tuple.c',
c_args: ['-DLIBPKGCONF_EXPORT', build_static],
install : true,
version : '4.0.0',
soversion : '4',
version : '5.0.0',
soversion : '5',
)
# For other projects using libpkgconfig as a subproject

13
pkg.m4
View File

@ -46,8 +46,8 @@ m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
dnl ----------------------------------
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND])
dnl ---------------------------------------------------------
dnl Since: 0.16
dnl
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
@ -55,6 +55,12 @@ dnl first found in the path. Checks that the version of pkg-config found
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
dnl used since that's the first version where most current features of
dnl pkg-config existed.
dnl
dnl If pkg-config is not found or older than specified, it will result
dnl in an empty PKG_CONFIG variable. To avoid widespread issues with
dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting.
dnl You can specify [PKG_CONFIG=false] as an action instead, which would
dnl result in pkg-config tests failing, but no bogus error messages.
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
@ -75,6 +81,9 @@ if test -n "$PKG_CONFIG"; then
AC_MSG_RESULT([no])
PKG_CONFIG=""
fi
fi
if test -z "$PKG_CONFIG"; then
m4_default([$2], [AC_MSG_ERROR([pkg-config not found])])
fi[]dnl
])dnl PKG_PROG_PKG_CONFIG

View File

@ -231,7 +231,7 @@ libs_circular1_body()
libs_circular_directpc_body()
{
atf_check \
-o inline:"-lcircular-2 -lcircular-3 -lcircular-1\n" \
-o inline:"-lcircular-3 -lcircular-1 -lcircular-2\n" \
pkgconf --libs ${selfdir}/lib1/circular-3.pc
}

View File

@ -27,6 +27,9 @@ tests_init \
modversion_fullpath \
modversion_provides \
modversion_uninstalled \
modversion_one_word_expression \
modversion_two_word_expression \
modversion_three_word_expression \
pcpath \
virtual_variable \
fragment_collision \
@ -301,3 +304,21 @@ modversion_uninstalled_body()
atf_check -o inline:"1.2.3\n" \
pkgconf --with-path="${selfdir}/lib1" --modversion omg
}
modversion_one_word_expression_body()
{
atf_check -o inline:"1.2.3\n" \
pkgconf --with-path="${selfdir}/lib1" --modversion "foo > 1.0"
}
modversion_two_word_expression_body()
{
atf_check -o inline:"1.2.3\n" \
pkgconf --with-path="${selfdir}/lib1" --modversion foo "> 1.0"
}
modversion_three_word_expression_body()
{
atf_check -o inline:"1.2.3\n" \
pkgconf --with-path="${selfdir}/lib1" --modversion foo ">" 1.0
}

View File

@ -10,6 +10,10 @@ tests_init \
argv_parse2 \
static_cflags \
private_duplication \
private_duplication_digraph \
foo_bar \
bar_foo \
foo_metapackage_3 \
libs_static2 \
missing \
requires_internal \
@ -37,7 +41,7 @@ libs_static_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o inline:"-L/test/lib -lbaz -L/test/lib -lzee -L/test/lib -lfoo\n" \
-o inline:"-L/test/lib -lbaz -L/test/lib -lzee -lfoo\n" \
pkgconf --static --libs baz
}
@ -45,7 +49,7 @@ libs_static_pure_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o inline:"-L/test/lib -lbaz -L/test/lib -lfoo\n" \
-o inline:"-L/test/lib -lbaz -lfoo\n" \
pkgconf --static --pure --libs baz
}
@ -69,10 +73,46 @@ private_duplication_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o inline:"-lprivate -lbaz -lzee -lbar -lfoo -lfoo\n" \
-o inline:"-lprivate -lbaz -lzee -lbar -lfoo\n" \
pkgconf --static --libs-only-l private-libs-duplication
}
private_duplication_digraph_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o 'match:"user:request" -> "private-libs-duplication"' \
-o 'match:"private-libs-duplication" -> "bar"' \
-o 'match:"private-libs-duplication" -> "baz"' \
-o 'match:"bar" -> "foo"' \
-o 'match:"baz" -> "foo"' \
pkgconf --static --libs-only-l private-libs-duplication --digraph
}
bar_foo_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o inline:"-lbar -lfoo\n" \
pkgconf --static --libs-only-l bar foo
}
foo_bar_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o inline:"-lbar -lfoo\n" \
pkgconf --static --libs-only-l foo bar
}
foo_metapackage_3_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"
atf_check \
-o inline:"-lbar -lfoo\n" \
pkgconf --static --libs-only-l foo metapackage-3
}
libs_static2_body()
{
export PKG_CONFIG_PATH="${selfdir}/lib1"