Add Cflags.private field

Those Cflags will be added if linking statically against a library;
this is necessary if the public headers need to be mutated depending on
linkage mode. Eg on Microsoft Windows varaibles whose definition resides
in a shared library need to be declared with a special attribute;
if linked to statically this attribute must not be used.

With Cflags.private their headers can eg check if 'LIBRARYNAME_STATIC'
is not defined to know that the special attribute is needed; without it
everyone linking against the library will need to manually research what
the expected macro is and set it depending on linkage mode.

This field is also supported by pkgconf since version 0.9.3 and
already used by (some) affected libraries targeting Microsoft Windows.

Note that _do_parse_cflags always adds the flags to pkg->cflags
and there is no pkg->cflags_private; instead the call to
parse_cflags_private is conditional. This matches the
existing implementation of Libs.private.

Closes: https://gitlab.freedesktop.org/pkg-config/pkg-config/-/issues/38
This commit is contained in:
Oneric 2021-09-21 19:45:25 +02:00
parent 481ef5b743
commit bb8f1b7b91
10 changed files with 131 additions and 12 deletions

View file

@ -2,6 +2,7 @@ TESTS_ENVIRONMENT = PKG_CONFIG='$(TESTS_PKG_CONFIG)' $(TESTS_SHELL)
TESTS = \
check-cflags \
check-cflags-private \
check-libs \
check-mixed-flags \
check-non-l-flags \
@ -40,6 +41,7 @@ EXTRA_DIST = \
requires-test.pc \
public-dep.pc \
private-dep.pc \
private-cflags.pc \
includedir.pc \
missing-requires-private.pc \
missing-requires.pc \

11
check/check-cflags-private Executable file
View file

@ -0,0 +1,11 @@
#! /bin/sh
set -e
. ${srcdir}/common
RESULT="-I/dummy/include"
run_test --cflags private-cflags
RESULT="-DDUMMY_STATIC=1 $RESULT"
run_test --static --cflags private-cflags

7
check/private-cflags.pc Normal file
View file

@ -0,0 +1,7 @@
Name: Requires test package
Description: Dummy pkgconfig test package for testing Cflags/Cflags.private
Version: 1.0.0
Libs: -L/dummy/lib -ldummy
Cflags: -I/dummy/include
Cflags.private: -DDUMMY_STATIC=1

10
main.c
View file

@ -615,9 +615,15 @@ main (int argc, char **argv)
debug_spew ("Error printing disabled\n");
if (want_static_lib_list)
enable_private_libs();
{
enable_private_libs();
enable_cflags_private();
}
else
disable_private_libs();
{
disable_private_libs();
disable_cflags_private();
}
/* honor Requires.private if any Cflags are requested or any static
* libs are requested */

64
parse.c
View file

@ -896,6 +896,57 @@ parse_cflags (Package *pkg, const char *str, const char *path)
g_free (trimmed);
}
static void
parse_cflags_private (Package *pkg, const char *str, const char *path)
{
/*
List of private Cflags. Private Cflags are flags which
are needed in the case of static linking. This can be required for
example on platforms which require special attributes to be set
for variables or functions which are defined in a shared library,
as is the case for Microsoft Windows. Affected libraries will need
to have ifdefs in their public headers to change the attributes
depending on whether they are being linked to staticly or dynamicly.
*/
char *trimmed;
char **argv = NULL;
int argc = 0;
GError *error = NULL;
if (pkg->cflags_private_num)
{
verbose_error ("Cflags.private field occurs twice in '%s'\n", path);
if (parse_strict)
exit (1);
else
return;
}
trimmed = trim_and_sub (pkg, str, path);
if (trimmed && *trimmed &&
!g_shell_parse_argv (trimmed, &argc, &argv, &error))
{
verbose_error ("Couldn't parse Cflags.private field into an argument vector: %s\n",
error ? error->message : "unknown");
if (parse_strict)
exit (1);
else
{
g_free (trimmed);
return;
}
}
_do_parse_cflags(pkg, argc, argv);
g_strfreev (argv);
g_free (trimmed);
pkg->cflags_private_num++;
}
static void
parse_url (Package *pkg, const char *str, const char *path)
{
@ -914,7 +965,7 @@ 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_cflags_private)
{
char *str;
char *p;
@ -979,6 +1030,12 @@ parse_line (Package *pkg, const char *untrimmed, const char *path,
else if (strcmp (tag, "Cflags") == 0 ||
strcmp (tag, "CFlags") == 0)
parse_cflags (pkg, p, path);
else if (strcmp (tag, "Cflags.private") == 0 ||
strcmp (tag, "CFlags.private") == 0)
{
if (!ignore_cflags_private)
parse_cflags_private (pkg, p, path);
}
else if (strcmp (tag, "Conflicts") == 0)
parse_conflicts (pkg, p, path);
else if (strcmp (tag, "URL") == 0)
@ -1096,7 +1153,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_cflags_private)
{
FILE *f;
Package *pkg;
@ -1141,7 +1199,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_cflags_private);
g_string_truncate (str, 0);
}

View file

@ -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_cflags_private);
GList *parse_module_list (Package *pkg, const char *str, const char *path);

View file

@ -137,6 +137,13 @@ Libs: -L${libdir} -lfoo</pre>
libraries support <tt>pkg-config</tt>, they should be added to
<tt>Requires</tt> or <tt>Requires.private</tt>.</li>
<li><b>Cflags.private</b>: The compiler flags specific to the static version
of your package. This may be required if your public headers need to change
according to linkage mode, as is for example the case on Microsoft Windows
if a library exposes a variable.
Don't add any flags for required packages supporting <tt>pkg-config</tt>;
<tt>pkg-config</tt> will add those automatically.</li>
<li><b>Libs</b>: The link flags specific to this package and any required
libraries that don't support <tt>pkg-config</tt>. The same rule as
<tt>Cflags</tt> applies here.</li>
@ -186,8 +193,9 @@ includedir=${prefix}/include
Cflags: -I${includedir}/foo</pre>
<p>The most important <tt>pkg-config</tt> metadata fields are
<tt>Requires</tt>, <tt>Requires.private</tt>, <tt>Cflags</tt>, <tt>Libs</tt>
and <tt>Libs.private</tt>. They will define the metadata used by external
<tt>Requires</tt>, <tt>Requires.private</tt>, <tt>Cflags</tt>, <tt>Cflags.private</tt>
<tt>Libs</tt> and <tt>Libs.private</tt>.
They will define the metadata used by external
projects to compile and link with the library.</p>
<p><tt>Requires</tt> and <tt>Requires.private</tt> define other modules
@ -214,9 +222,9 @@ Cflags: -I${includedir}/foo</pre>
additional direct dependency.</p>
<p>Finally, the <tt>Cflags</tt> contains the compiler flags for using the
library. Unlike the <tt>Libs</tt> field, there is not a private variant of
<tt>Cflags</tt>. This is because the data types and macro definitions are
needed regardless of the linking scenario.</p>
library. <tt>Cflags.private</tt> contain compiler flags specific to only the
static version of the library; they will be used in addition to the regular
<tt>Cflags</tt> if <tt>--static</tt> is set.</p>
<h2><a name="using">Using pkg-config files</a></h2>

View file

@ -629,6 +629,14 @@ installed.
This line should list the compile flags specific to your package.
Don't add any flags for required packages; \fIpkg-config\fP will
add those automatically.
.TP
.I "Cflags.private:"
This line should list the compile flags specific to the static version
of your package. This may be required if your public headers need to change
according to linkage mode, as is for example the case on Microsoft Windows
if a library exposes a variable.
Don't add any flags for required packages; \fIpkg-config\fP will
add those automatically.
.\"
.SH AUTHOR

17
pkg.c
View file

@ -50,6 +50,7 @@ gboolean disable_uninstalled = FALSE;
gboolean ignore_requires = FALSE;
gboolean ignore_requires_private = TRUE;
gboolean ignore_private_libs = TRUE;
gboolean ignore_cflags_private = TRUE;
void
add_search_dir (const char *path)
@ -300,7 +301,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_cflags_private);
g_free (key);
if (pkg != NULL && strstr (location, "uninstalled.pc"))
@ -1181,6 +1183,7 @@ print_package_list (void)
ignore_requires = TRUE;
ignore_requires_private = TRUE;
ignore_cflags_private = 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 +1249,15 @@ disable_requires_private(void)
{
ignore_requires_private = TRUE;
}
void
enable_cflags_private(void)
{
ignore_cflags_private = FALSE;
}
void
disable_cflags_private(void)
{
ignore_cflags_private = TRUE;
}

3
pkg.h
View file

@ -84,6 +84,7 @@ struct Package_
int path_position; /* used to order packages by position in path of their .pc file, lower number means earlier in path */
int libs_num; /* Number of times the "Libs" header has been seen */
int libs_private_num; /* Number of times the "Libs.private" header has been seen */
int cflags_private_num; /* Number of times the "Cflags.private" header has been seen */
char *orig_prefix; /* original prefix value before redefinition */
};
@ -122,6 +123,8 @@ void enable_requires(void);
void disable_requires(void);
void enable_requires_private(void);
void disable_requires_private(void);
void enable_cflags_private(void);
void disable_cflags_private(void);
/* If TRUE, do not automatically prefer uninstalled versions */
extern gboolean disable_uninstalled;