diff --git a/check/check-print-options b/check/check-print-options index b521d4d..4f77e65 100755 --- a/check/check-print-options +++ b/check/check-print-options @@ -31,7 +31,8 @@ RESULT="public-dep >= 1" run_test --print-requires requires-test # --print-requires-private -RESULT="private-dep >= 1" +RESULT="internal-dep >= 1 +private-dep >= 1" run_test --print-requires-private requires-test # --list-all, limit to a subdirectory @@ -51,6 +52,7 @@ run_test --modversion --version simple # --print-requires/--print-requires-private allowed together RESULT="public-dep >= 1 +internal-dep >= 1 private-dep >= 1" run_test --print-requires --print-requires-private requires-test run_test --print-requires-private --print-requires requires-test diff --git a/check/check-requires-private b/check/check-requires-private index dc69baf..586b33e 100755 --- a/check/check-requires-private +++ b/check/check-requires-private @@ -4,20 +4,23 @@ set -e . ${srcdir}/common -# expect cflags from requires-test and public-dep +# expect cflags from requires-test, public-dep and private-dep but not internal-dep RESULT="-I/requires-test/include -I/private-dep/include -I/public-dep/include" run_test --cflags requires-test +run_test --cflags requires-notfound-test run_test --static --cflags requires-test +run_test --static --cflags requires-notfound-test # expect libs for just requires-test and public-dep RESULT="-L/requires-test/lib -L/public-dep/lib -lrequires-test -lpublic-dep" if [ "$list_indirect_deps" = no ]; then run_test --libs requires-test + run_test --libs requires-notfound-test fi -# expect libs for requires-test, public-dep and private-dep in static case -RESULT="-L/requires-test/lib -L/private-dep/lib -L/public-dep/lib \ --lrequires-test -lprivate-dep -lpublic-dep" +# expect libs for requires-test, public-dep, private-dep and internal-dep in static case +RESULT="-L/requires-test/lib -L/internal-dep/lib -L/private-dep/lib -L/public-dep/lib \ +-lrequires-test -linternal-dep -lprivate-dep -lpublic-dep" if [ "$list_indirect_deps" = yes ]; then run_test --libs requires-test fi diff --git a/check/internal-dep.pc b/check/internal-dep.pc new file mode 100644 index 0000000..87a051b --- /dev/null +++ b/check/internal-dep.pc @@ -0,0 +1,6 @@ +Name: Requires test package +Description: Dummy pkgconfig test package for testing Requires/Requires.private +Version: 1.0.0 +Libs: -L/internal-dep/lib -linternal-dep +Cflags: -I/internal-dep/include + diff --git a/check/requires-notfound-test.pc b/check/requires-notfound-test.pc new file mode 100644 index 0000000..dfe46b6 --- /dev/null +++ b/check/requires-notfound-test.pc @@ -0,0 +1,9 @@ +Name: Requires test package +Description: Dummy pkgconfig test package for testing Requires/Requires.private +Version: 1.0.0 +Requires: public-dep >= 1 +Requires.private: private-dep >= 1 +Requires.internal: notfound-dep >= 1 +Libs: -L/requires-test/lib -lrequires-test +Cflags: -I/requires-test/include + diff --git a/check/requires-test.pc b/check/requires-test.pc index e483db2..5339f4e 100644 --- a/check/requires-test.pc +++ b/check/requires-test.pc @@ -3,6 +3,7 @@ Description: Dummy pkgconfig test package for testing Requires/Requires.private Version: 1.0.0 Requires: public-dep >= 1 Requires.private: private-dep >= 1 +Requires.internal: internal-dep >= 1 Libs: -L/requires-test/lib -lrequires-test Cflags: -I/requires-test/include diff --git a/glib/m4macros/glib-gettext.m4 b/glib/m4macros/glib-gettext.m4 index 5217fd8..155b1d8 100644 --- a/glib/m4macros/glib-gettext.m4 +++ b/glib/m4macros/glib-gettext.m4 @@ -313,7 +313,7 @@ msgstr "" # on various variables needed by the Makefile.in.in installed by # glib-gettextize. dnl -glib_DEFUN([GLIB_GNU_GETTEXT], +AU_DEFUN([GLIB_GNU_GETTEXT], [AC_REQUIRE([AC_PROG_CC])dnl GLIB_LC_MESSAGES @@ -383,7 +383,8 @@ glib_DEFUN([GLIB_GNU_GETTEXT], rm -f po/POTFILES sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ < $srcdir/po/POTFILES.in > po/POTFILES - ]) + ], + [[$0: This macro is deprecated. You should use upstream gettext instead.]]) # AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) # ------------------------------- diff --git a/main.c b/main.c index cfebdba..02b6a14 100644 --- a/main.c +++ b/main.c @@ -483,6 +483,36 @@ static const GOptionEntry options_table[] = { { NULL, 0, 0, 0, NULL, NULL, NULL } }; +static void +print_requires (GList *packages, RequireType type) +{ + GList *pkgtmp; + for (pkgtmp = packages; pkgtmp != NULL; pkgtmp = g_list_next (pkgtmp)) + { + Package *pkg = pkgtmp->data; + GList *reqtmp; + + if (type == REQUIRE) + reqtmp = pkg->requires; + else if (type == REQUIRE_PRIVATE) + reqtmp = pkg->requires_private; + else + reqtmp = pkg->requires_internal; + + for (; reqtmp != NULL; reqtmp = g_list_next (reqtmp)) + { + RequiredVersion *req = reqtmp->data; + Package *deppkg = req->package; + if (req->comparison == ALWAYS_MATCH) + printf ("%s\n", deppkg->key); + else + printf ("%s %s %s\n", deppkg->key, + comparison_to_str(req->comparison), + req->version); + } + } +} + int main (int argc, char **argv) { @@ -626,6 +656,10 @@ main (int argc, char **argv) (want_static_lib_list && (pkg_flags & LIBS_ANY))) enable_requires_private(); + if (want_requires_private || want_exists || + (want_static_lib_list && (pkg_flags & LIBS_ANY))) + enable_requires_internal(); + /* ignore Requires if no Cflags or Libs are requested */ if (pkg_flags == 0 && !want_requires && !want_exists) @@ -769,53 +803,16 @@ main (int argc, char **argv) if (want_requires) { - GList *pkgtmp; - for (pkgtmp = packages; pkgtmp != NULL; pkgtmp = g_list_next (pkgtmp)) - { - Package *pkg = pkgtmp->data; - GList *reqtmp; - - /* process Requires: */ - for (reqtmp = pkg->requires; reqtmp != NULL; reqtmp = g_list_next (reqtmp)) - { - Package *deppkg = reqtmp->data; - RequiredVersion *req; - req = g_hash_table_lookup(pkg->required_versions, deppkg->key); - if ((req == NULL) || (req->comparison == ALWAYS_MATCH)) - printf ("%s\n", deppkg->key); - else - printf ("%s %s %s\n", deppkg->key, - comparison_to_str(req->comparison), - req->version); - } - } + /* process Requires: */ + print_requires (packages, REQUIRE); } if (want_requires_private) { - GList *pkgtmp; - for (pkgtmp = packages; pkgtmp != NULL; pkgtmp = g_list_next (pkgtmp)) - { - Package *pkg = pkgtmp->data; - GList *reqtmp; - /* process Requires.private: */ - for (reqtmp = pkg->requires_private; reqtmp != NULL; reqtmp = g_list_next (reqtmp)) - { + /* process Requires.internal: */ + print_requires (packages, REQUIRE_INTERNAL); - Package *deppkg = reqtmp->data; - RequiredVersion *req; - - if (g_list_find (pkg->requires, reqtmp->data)) - continue; - - req = g_hash_table_lookup(pkg->required_versions, deppkg->key); - if ((req == NULL) || (req->comparison == ALWAYS_MATCH)) - printf ("%s\n", deppkg->key); - else - printf ("%s %s %s\n", deppkg->key, - comparison_to_str(req->comparison), - req->version); - } - } + /* process Requires.private: */ + print_requires (packages, REQUIRE_PRIVATE); } /* Print all flags; then print a newline at the end. */ diff --git a/parse.c b/parse.c index 6e9907c..6d6b171 100644 --- a/parse.c +++ b/parse.c @@ -546,7 +546,7 @@ parse_requires (Package *pkg, const char *str, const char *path) } trimmed = trim_and_sub (pkg, str, path); - pkg->requires_entries = parse_module_list (pkg, trimmed, path); + pkg->requires = parse_module_list (pkg, trimmed, path); g_free (trimmed); } @@ -565,7 +565,26 @@ parse_requires_private (Package *pkg, const char *str, const char *path) } trimmed = trim_and_sub (pkg, str, path); - pkg->requires_private_entries = parse_module_list (pkg, trimmed, path); + pkg->requires_private = parse_module_list (pkg, trimmed, path); + g_free (trimmed); +} + +static void +parse_requires_internal (Package *pkg, const char *str, const char *path) +{ + char *trimmed; + + if (pkg->requires_internal) + { + verbose_error ("Requires.internal field occurs twice in '%s'\n", path); + if (parse_strict) + exit (1); + else + return; + } + + trimmed = trim_and_sub (pkg, str, path); + pkg->requires_internal = parse_module_list (pkg, trimmed, path); g_free (trimmed); } @@ -907,7 +926,8 @@ parse_url (Package *pkg, const char *str, const char *path) static void parse_line (Package *pkg, const char *untrimmed, const char *path, gboolean ignore_requires, gboolean ignore_private_libs, - gboolean ignore_requires_private) + gboolean ignore_requires_private, + gboolean ignore_requires_internal) { char *str; char *p; @@ -955,6 +975,11 @@ parse_line (Package *pkg, const char *untrimmed, const char *path, if (!ignore_requires_private) parse_requires_private (pkg, p, path); } + else if (strcmp (tag, "Requires.internal") == 0) + { + if (!ignore_requires_internal) + parse_requires_internal (pkg, p, path); + } else if (strcmp (tag, "Requires") == 0) { if (ignore_requires == FALSE) @@ -1089,7 +1114,8 @@ Package* parse_package_file (const char *key, const char *path, gboolean ignore_requires, gboolean ignore_private_libs, - gboolean ignore_requires_private) + gboolean ignore_requires_private, + gboolean ignore_requires_internal) { FILE *f; Package *pkg; @@ -1134,7 +1160,7 @@ parse_package_file (const char *key, const char *path, one_line = TRUE; parse_line (pkg, str->str, path, ignore_requires, ignore_private_libs, - ignore_requires_private); + ignore_requires_private, ignore_requires_internal); g_string_truncate (str, 0); } diff --git a/parse.h b/parse.h index db1bf86..6736f58 100644 --- a/parse.h +++ b/parse.h @@ -25,7 +25,8 @@ Package *parse_package_file (const char *key, const char *path, gboolean ignore_requires, gboolean ignore_private_libs, - gboolean ignore_requires_private); + gboolean ignore_requires_private, + gboolean ignore_requires_internal); GList *parse_module_list (Package *pkg, const char *str, const char *path); diff --git a/pkg-config-guide.html b/pkg-config-guide.html index c666fe5..9ebce3f 100644 --- a/pkg-config-guide.html +++ b/pkg-config-guide.html @@ -121,13 +121,23 @@ Libs: -L${libdir} -lfoo
The most important pkg-config metadata fields are - Requires, Requires.private, Cflags, Libs - and Libs.private. They will define the metadata used by external - projects to compile and link with the library.
+ Requires, Requires.private, Requires.internal, + Cflags, Libs and Libs.private. They will define + the metadata used by external projects to compile and link with the library. -Requires and Requires.private define other modules - needed by the library. It is usually preferred to use the private variant of - Requires to avoid exposing unnecessary libraries to the program - that is linking with your library. If the program will not be using the - symbols of the required library, it should not be linking directly to that - library. See the discussion of +
Requires, Requires.private and Requires.internal + define other modules needed by the library. It is usually preferred to use the + private variant of Requires to avoid exposing unnecessary libraries + to the program that is linking with your library. If the program will not be + using the symbols of the required library, it should not be linking directly + to that library. See the discussion of overlinking for a more thorough explanation.
Since pkg-config always exposes the link flags of the Requires libraries, these modules will become direct dependencies of the program. On the other hand, libraries from Requires.private - will only be included when static linking. For this reason, it is usually - only appropriate to add modules from the same package in Requires. -
+ and Requires.internal will only be included when static linking. For + this reason, it is usually only appropriate to add modules from the same + package in Requires.The Libs field contains the link flags necessary to use that library. In addition, Libs and Libs.private contain link @@ -404,10 +414,10 @@ myapp_LDADD = $(X_LIBS) expose libx data types in its public API. What do I put in my z.pc file? -
Again, add the module to Requires.private if it supports - pkg-config. In this case, the compiler flags will be emitted - unnecessarily, but it ensures that the linker flags will be present when - linking statically. If libx does not support pkg-config, +
Add the module to Requires.internal if it supports + pkg-config. Unlike Requires.private the compiler flags + will not be emitted, but it ensures that the linker flags will be present + when linking statically. If libx does not support pkg-config, add the necessary linker flags to Libs.private.
diff --git a/pkg.c b/pkg.c index 4c1523a..22c40c9 100644 --- a/pkg.c +++ b/pkg.c @@ -49,6 +49,7 @@ static GList *search_dirs = NULL; gboolean disable_uninstalled = FALSE; gboolean ignore_requires = FALSE; gboolean ignore_requires_private = TRUE; +gboolean ignore_requires_internal = TRUE; gboolean ignore_private_libs = TRUE; void @@ -219,6 +220,24 @@ package_init (gboolean want_list) add_virtual_pkgconfig_package (); } +static void +internal_get_package_foreach (gpointer data, gpointer user_data) +{ + gboolean warn = GPOINTER_TO_INT (user_data); + RequiredVersion *ver = data; + Package *pkg = ver->owner; + + debug_spew ("Searching for '%s' requirement '%s'\n", + pkg->key, ver->name); + ver->package = internal_get_package (ver->name, warn); + if (ver->package == NULL) + { + verbose_error ("Package '%s', required by '%s', not found\n", + ver->name, pkg->key); + exit (1); + } +} + static Package * internal_get_package (const char *name, gboolean warn) { @@ -226,7 +245,6 @@ internal_get_package (const char *name, gboolean warn) char *key = NULL; char *location = NULL; unsigned int path_position = 0; - GList *iter; GList *dir_iter; pkg = g_hash_table_lookup (packages, name); @@ -300,7 +318,8 @@ internal_get_package (const char *name, gboolean warn) debug_spew ("Reading '%s' from file '%s'\n", name, location); pkg = parse_package_file (key, location, ignore_requires, - ignore_private_libs, ignore_requires_private); + ignore_private_libs, ignore_requires_private, + ignore_requires_internal); g_free (key); if (pkg != NULL && strstr (location, "uninstalled.pc")) @@ -323,58 +342,15 @@ internal_get_package (const char *name, gboolean warn) g_hash_table_insert (packages, pkg->key, pkg); /* pull in Requires packages */ - for (iter = pkg->requires_entries; iter != NULL; iter = g_list_next (iter)) - { - Package *req; - RequiredVersion *ver = iter->data; - - debug_spew ("Searching for '%s' requirement '%s'\n", - pkg->key, ver->name); - req = internal_get_package (ver->name, warn); - if (req == NULL) - { - verbose_error ("Package '%s', required by '%s', not found\n", - ver->name, pkg->key); - exit (1); - } - - if (pkg->required_versions == NULL) - pkg->required_versions = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_insert (pkg->required_versions, ver->name, ver); - pkg->requires = g_list_prepend (pkg->requires, req); - } - - /* pull in Requires.private packages */ - for (iter = pkg->requires_private_entries; iter != NULL; - iter = g_list_next (iter)) - { - Package *req; - RequiredVersion *ver = iter->data; - - debug_spew ("Searching for '%s' private requirement '%s'\n", - pkg->key, ver->name); - req = internal_get_package (ver->name, warn); - if (req == NULL) - { - verbose_error ("Package '%s', required by '%s', not found\n", - ver->name, pkg->key); - exit (1); - } - - if (pkg->required_versions == NULL) - pkg->required_versions = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_insert (pkg->required_versions, ver->name, ver); - pkg->requires_private = g_list_prepend (pkg->requires_private, req); - } - - /* make requires_private include a copy of the public requires too */ - pkg->requires_private = g_list_concat (g_list_copy (pkg->requires), - pkg->requires_private); - - pkg->requires = g_list_reverse (pkg->requires); - pkg->requires_private = g_list_reverse (pkg->requires_private); + g_list_foreach (pkg->requires, + internal_get_package_foreach, + GINT_TO_POINTER (warn)); + g_list_foreach (pkg->requires_private, + internal_get_package_foreach, + GINT_TO_POINTER (warn)); + g_list_foreach (pkg->requires_internal, + internal_get_package_foreach, + GINT_TO_POINTER (warn)); verify_package (pkg); @@ -510,10 +486,15 @@ packages_sort_by_path_position (GList *list) * any package that it depends on. */ static void -recursive_fill_list (Package *pkg, gboolean include_private, +recursive_fill_list (Package *pkg, gboolean include_private, FlagType flags, GHashTable *visited, GList **listp) { - GList *tmp; + GList *lists[N_REQUIRE_TYPES] = { + pkg->requires, + pkg->requires_private, + pkg->requires_internal, + }; + gint type; /* * If the package has already been visited, then it is already in 'listp' and @@ -532,11 +513,44 @@ recursive_fill_list (Package *pkg, gboolean include_private, g_hash_table_replace (visited, pkg->key, pkg->key); } - /* Start from the end of the required package list to maintain order since - * the recursive list is built by prepending. */ - tmp = include_private ? pkg->requires_private : pkg->requires; - for (tmp = g_list_last (tmp); tmp != NULL; tmp = g_list_previous (tmp)) - recursive_fill_list (tmp->data, include_private, visited, listp); + for (type = 0; type < N_REQUIRE_TYPES; type++) + { + GList *iter; + FlagType new_flags; + + switch (type) + { + case REQUIRE: + new_flags = flags; + break; + + case REQUIRE_PRIVATE: + /* When we don't include private libs we still want to pull Cflags + * from Require.private. + * See https://bugs.freedesktop.org/show_bug.cgi?id=105572 */ + new_flags = include_private ? flags : flags & CFLAGS_ANY; + break; + + case REQUIRE_INTERNAL: + /* Only pull Libs from Require.internal if we include private libs. + * Don't pull anything otherwise. + * See https://bugs.freedesktop.org/show_bug.cgi?id=105572 */ + new_flags = include_private ? flags & LIBS_ANY : 0; + break; + } + + if (new_flags == 0) + continue; + + /* Start from the end of the required package list to maintain order since + * the recursive list is built by prepending. */ + for (iter = g_list_last (lists[type]); iter != NULL; iter = g_list_previous (iter)) + { + RequiredVersion *ver = iter->data; + if (ver->package != NULL) + recursive_fill_list (ver->package, include_private, new_flags, visited, listp); + } + } *listp = g_list_prepend (*listp, pkg); } @@ -588,7 +602,7 @@ fill_list (GList *packages, FlagType type, * the recursive list is built by prepending. */ visited = g_hash_table_new (g_str_hash, g_str_equal); for (tmp = g_list_last (packages); tmp != NULL; tmp = g_list_previous (tmp)) - recursive_fill_list (tmp->data, include_private, visited, &expanded); + recursive_fill_list (tmp->data, include_private, type, visited, &expanded); g_hash_table_destroy (visited); spew_package_list ("post-recurse", expanded); @@ -640,6 +654,29 @@ static const gchar *msvc_include_envvars[] = { }; #endif +static void +verify_version_foreach (gpointer data, gpointer user_data) +{ + RequiredVersion *ver = data; + Package *req = ver->package; + Package *pkg = ver->owner; + + if (!version_test (ver->comparison, req->version, ver->version)) + { + verbose_error ("Package '%s' requires '%s %s %s' but version of %s is %s\n", + pkg->key, req->key, + comparison_to_str (ver->comparison), + ver->version, + req->key, + req->version); + if (req->url) + verbose_error ("You may find new versions of %s at %s\n", + req->name, req->url); + + exit (1); + } +} + static void verify_package (Package *pkg) { @@ -687,44 +724,15 @@ verify_package (Package *pkg) } /* Make sure we have the right version for all requirements */ - - iter = pkg->requires_private; - - while (iter != NULL) - { - Package *req = iter->data; - RequiredVersion *ver = NULL; - - if (pkg->required_versions) - ver = g_hash_table_lookup (pkg->required_versions, - req->key); - - if (ver) - { - if (!version_test (ver->comparison, req->version, ver->version)) - { - verbose_error ("Package '%s' requires '%s %s %s' but version of %s is %s\n", - pkg->key, req->key, - comparison_to_str (ver->comparison), - ver->version, - req->key, - req->version); - if (req->url) - verbose_error ("You may find new versions of %s at %s\n", - req->name, req->url); - - exit (1); - } - } - - iter = g_list_next (iter); - } + g_list_foreach (pkg->requires, verify_version_foreach, NULL); + g_list_foreach (pkg->requires_private, verify_version_foreach, NULL); + g_list_foreach (pkg->requires_internal, verify_version_foreach, NULL); /* Make sure we didn't drag in any conflicts via Requires * (inefficient algorithm, who cares) */ visited = g_hash_table_new (g_str_hash, g_str_equal); - recursive_fill_list (pkg, TRUE, visited, &requires); + recursive_fill_list (pkg, TRUE, FLAGS_ANY, visited, &requires); g_hash_table_destroy (visited); conflicts = pkg->conflicts; @@ -936,14 +944,14 @@ packages_get_flags (GList *pkgs, FlagType flags) /* sort packages in path order for -L/-I, dependency order otherwise */ if (flags & CFLAGS_OTHER) { - cur = get_multi_merged (pkgs, CFLAGS_OTHER, FALSE, TRUE); + cur = get_multi_merged (pkgs, CFLAGS_OTHER, FALSE, !ignore_private_libs); debug_spew ("adding CFLAGS_OTHER string \"%s\"\n", cur); g_string_append (str, cur); g_free (cur); } if (flags & CFLAGS_I) { - cur = get_multi_merged (pkgs, CFLAGS_I, TRUE, TRUE); + cur = get_multi_merged (pkgs, CFLAGS_I, TRUE, !ignore_private_libs); debug_spew ("adding CFLAGS_I string \"%s\"\n", cur); g_string_append (str, cur); g_free (cur); @@ -1181,6 +1189,7 @@ print_package_list (void) ignore_requires = TRUE; ignore_requires_private = TRUE; + ignore_requires_internal = TRUE; /* Add the packages to a pointer array and sort by pkg->key first, to give * deterministic output. While doing that, work out the maximum key length @@ -1246,3 +1255,15 @@ disable_requires_private(void) { ignore_requires_private = TRUE; } + +void +enable_requires_internal(void) +{ + ignore_requires_internal = FALSE; +} + +void +disable_requires_internal(void) +{ + ignore_requires_internal = TRUE; +} diff --git a/pkg.h b/pkg.h index c6732bd..18bde7e 100644 --- a/pkg.h +++ b/pkg.h @@ -55,12 +55,21 @@ struct Flag_ char *arg; }; +typedef enum +{ + REQUIRE, + REQUIRE_PRIVATE, + REQUIRE_INTERNAL, + N_REQUIRE_TYPES +} RequireType; + struct RequiredVersion_ { char *name; ComparisonType comparison; char *version; Package *owner; + Package *package; }; struct Package_ @@ -71,14 +80,12 @@ struct Package_ char *description; char *url; char *pcfiledir; /* directory it was loaded from */ - GList *requires_entries; - GList *requires; - GList *requires_private_entries; - GList *requires_private; + GList *requires; /* list of RequiredVersion */ + GList *requires_private; /* list of RequiredVersion */ + GList *requires_internal; /* list of RequiredVersion */ GList *libs; GList *cflags; GHashTable *vars; - GHashTable *required_versions; /* hash from name to RequiredVersion */ GList *conflicts; /* list of RequiredVersion */ gboolean uninstalled; /* used the -uninstalled file */ int path_position; /* used to order packages by position in path of their .pc file, lower number means earlier in path */ @@ -122,6 +129,8 @@ void enable_requires(void); void disable_requires(void); void enable_requires_private(void); void disable_requires_private(void); +void enable_requires_internal(void); +void disable_requires_internal(void); /* If TRUE, do not automatically prefer uninstalled versions */ extern gboolean disable_uninstalled;