[PATCH 2/3] Add Cflags.private field

Oneric oneric at oneric.de
Fri Oct 15 20:31:16 UTC 2021


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
---
 check/Makefile.am          |  2 ++
 check/check-cflags-private | 11 +++++++
 check/private-cflags.pc    |  7 +++++
 main.c                     | 10 ++++--
 parse.c                    | 64 ++++++++++++++++++++++++++++++++++++--
 parse.h                    |  3 +-
 pkg-config-guide.html      | 18 ++++++++---
 pkg-config.1               |  8 +++++
 pkg.c                      | 17 +++++++++-
 pkg.h                      |  3 ++
 10 files changed, 131 insertions(+), 12 deletions(-)
 create mode 100755 check/check-cflags-private
 create mode 100644 check/private-cflags.pc

diff --git a/check/Makefile.am b/check/Makefile.am
index 68c6d84..618a90a 100644
--- a/check/Makefile.am
+++ b/check/Makefile.am
@@ -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 \
diff --git a/check/check-cflags-private b/check/check-cflags-private
new file mode 100755
index 0000000..544f1d3
--- /dev/null
+++ b/check/check-cflags-private
@@ -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
diff --git a/check/private-cflags.pc b/check/private-cflags.pc
new file mode 100644
index 0000000..0cfbdfd
--- /dev/null
+++ b/check/private-cflags.pc
@@ -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
+
diff --git a/main.c b/main.c
index cfebdba..7bc560e 100644
--- a/main.c
+++ b/main.c
@@ -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 */
diff --git a/parse.c b/parse.c
index f4a1baa..39a63cd 100644
--- a/parse.c
+++ b/parse.c
@@ -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);
     }
diff --git a/parse.h b/parse.h
index db1bf86..6135a4e 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_cflags_private);
 
 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..79335ff 100644
--- a/pkg-config-guide.html
+++ b/pkg-config-guide.html
@@ -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>
 
diff --git a/pkg-config.1 b/pkg-config.1
index a147fc8..8272d15 100644
--- a/pkg-config.1
+++ b/pkg-config.1
@@ -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
 
diff --git a/pkg.c b/pkg.c
index 4c1523a..202c944 100644
--- a/pkg.c
+++ b/pkg.c
@@ -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;
+}
diff --git a/pkg.h b/pkg.h
index c6732bd..09dd255 100644
--- a/pkg.h
+++ b/pkg.h
@@ -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;
-- 
2.30.2



More information about the pkg-config mailing list