[PATCH] Detect strongly connected components in package chains to handle cycles

yvvaibhav at gmail.com yvvaibhav at gmail.com
Tue Feb 26 13:12:22 UTC 2019


From: Vaibhav Yenamandra <vyenamandra at bloomberg.net>

We introduce the ability to detect strongly connected components to
pkg-config based on the previously added cycle detection and breaking
feature.

Interface changes include:
    * The command line interface to pkg-config will have the following
      new flags:

        - `--emit-groups` to control when pkg-config emits groups

        - `--group-prefix`, `--group-suffix` to control the archive
          group delimiters used on the link-line generated. These two
          flags MUST occur together, when specified

    * The emitted link line changes if cycles are present and
      `--emit-groups` was provided

The changes of this commit can be summarized by the following:

    * Packages that do not have cycles will not generate new link-lines

    * To enable archive groups on the link line, packages with cyclic
      dependency graphs must use `--emit-groups` on the CLI

    * The output interface of pkg-config will change as follows:
        _ The changes only occur if a cycle is detected with both the
        `--emit-groups --static --libs` provided

        - The change is only to the link-line emitted. CFLAGS and `-L`
          flags are not affected.

        - The value of `--group-prefix` defaults to '--start-group'

        - The value of `--group-suffix` defaults to '--end-group'

        - pkg-config will assume that the group delimiters when
          provided, are valid for the target system

The effects of this commit on the link line will be that non-trivial
strongly-connected components (i.e. those comprised of more than one
package) will be surrounded by `--start-group` and `--end-group` on
platforms with linkers that support them.

Examples (in the presence of `--emit-groups`)

1. if `-lin -la -lcycle` was part of a cycle that was broken before
this change will cause it to be emitted as:
`--start-group -lin -la -lcycle --end-group`.

2. if `--group-prefix="-(" --group-suffix="-)"` was also provided, then
the generated link-line becomes:
`-( -lin -la -lcycle -)`.
---
 check/Makefile.am          |   8 ++
 check/check-archive-groups |  58 ++++++++
 check/circular-group-1.pc  |  11 ++
 check/circular-group-2.pc  |  11 ++
 check/circular-group-3.pc  |  11 ++
 check/head-line-1.pc       |  11 ++
 check/head-line-2.pc       |  11 ++
 check/tail-line-1.pc       |  11 ++
 check/tail-line-2.pc       |  10 ++
 main.c                     |  47 ++++++
 parse.c                    |   2 +
 pkg.c                      | 284 +++++++++++++++++++++++++++++++++++--
 pkg.h                      |  30 ++++
 13 files changed, 497 insertions(+), 8 deletions(-)
 create mode 100755 check/check-archive-groups
 create mode 100644 check/circular-group-1.pc
 create mode 100644 check/circular-group-2.pc
 create mode 100644 check/circular-group-3.pc
 create mode 100644 check/head-line-1.pc
 create mode 100644 check/head-line-2.pc
 create mode 100644 check/tail-line-1.pc
 create mode 100644 check/tail-line-2.pc

diff --git a/check/Makefile.am b/check/Makefile.am
index 68c6d84..214e728 100644
--- a/check/Makefile.am
+++ b/check/Makefile.am
@@ -31,6 +31,7 @@ TESTS = \
 	check-variables \
 	check-dependencies \
 	check-system-flags \
+	check-archive-groups \
 	$(NULL)
 
 EXTRA_DIST = \
@@ -118,4 +119,11 @@ EXTRA_DIST = \
 	dependencies/j_dep_k.pc \
 	dependencies/k_dep.pc \
 	system.pc \
+	head-line-1.pc \
+	head-line-2.pc \
+	circular-group-1.pc \
+	circular-group-2.pc \
+	circular-group-3.pc \
+	tail-line-1.pc \
+	tail-line-2.pc \
 	$(NULL)
diff --git a/check/check-archive-groups b/check/check-archive-groups
new file mode 100755
index 0000000..a9977cd
--- /dev/null
+++ b/check/check-archive-groups
@@ -0,0 +1,58 @@
+#! /bin/sh
+
+set -e
+
+. ${srcdir}/common
+
+
+CIRCULAR_FLAGS="-lcirc1 -lcirc2 -lcirc3";
+CIRCULAR_GRP_FLAGS="-lcircgrp1 -lcircgrp2 -lcircgrp3";
+GRP_START="--start-group";
+GRP_END="--end-group"
+
+# Case 1: Whole package chain is a cycle
+RESULT="${CIRCULAR_FLAGS}";
+run_test --libs --static circular-1
+
+RESULT="${GRP_START} ${CIRCULAR_FLAGS} ${GRP_END}";
+run_test --emit-groups --libs --static circular-1
+
+# Case 2: There is one cycle within a linear chain
+RESULT="-lhline1 -lhline2 ${GRP_START} ${CIRCULAR_GRP_FLAGS} ${GRP_END} -ltline1 -ltline2";
+run_test --emit-groups --libs --static head-line-1
+
+RESULT="-lhline1 -lhline2 ${CIRCULAR_GRP_FLAGS} -ltline1 -ltline2";
+run_test --libs --static head-line-1
+
+# Case 3: Two disjoint cycles on separate chains
+RESULT="-lhline1 -lhline2 ${GRP_START} ${CIRCULAR_GRP_FLAGS} ${GRP_END} -ltline1 -ltline2 ${GRP_START} ${CIRCULAR_FLAGS} ${GRP_END}";
+run_test --emit-groups --libs --static head-line-1 circular-1
+
+RESULT="-lhline1 -lhline2 ${CIRCULAR_GRP_FLAGS} -ltline1 -ltline2 ${CIRCULAR_FLAGS}";
+run_test --libs --static head-line-1 circular-1
+
+RESULT="${GRP_START} ${CIRCULAR_FLAGS} ${GRP_END} -lhline1 -lhline2 ${GRP_START} ${CIRCULAR_GRP_FLAGS} ${GRP_END} -ltline1 -ltline2";
+run_test --emit-groups --libs --static circular-1 head-line-1
+
+RESULT="${CIRCULAR_FLAGS} -lhline1 -lhline2 ${CIRCULAR_GRP_FLAGS} -ltline1 -ltline2";
+run_test --libs --static circular-1 head-line-1
+
+# Case 4: Alternative group delimiters passed
+RESULT="-( ${CIRCULAR_FLAGS} -)";
+run_test --emit-groups \
+         --group-prefix="-(" --group-suffix="-)" \
+         --libs --static circular-1
+
+# Case 5: group delimiters not provided in pairs
+EXPECT_RETURN=1
+RESULT="--group-prefix must be accompanied by a --group-suffix"
+run_test --group-prefix="-(" circular-1
+
+EXPECT_RETURN=1
+RESULT="--group-prefix must be accompanied by a --group-suffix"
+run_test --group-suffix="-(" circular-1
+
+unset CIRCULAR_FLAGS;
+unset CIRCULAR_GRP_FLAGS;
+unset GRP_START;
+unset GRP_END;
diff --git a/check/circular-group-1.pc b/check/circular-group-1.pc
new file mode 100644
index 0000000..527c979
--- /dev/null
+++ b/check/circular-group-1.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/circgrp1
+
+Name: Circular Requires test 1
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Requires: circular-group-2
+Libs: -lcircgrp1
+Cflags: -I${includedir}
diff --git a/check/circular-group-2.pc b/check/circular-group-2.pc
new file mode 100644
index 0000000..228dddd
--- /dev/null
+++ b/check/circular-group-2.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/circgrp2
+
+Name: Circular Requires test 2
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Requires: circular-group-3
+Libs: -lcircgrp2
+Cflags: -I${includedir}
diff --git a/check/circular-group-3.pc b/check/circular-group-3.pc
new file mode 100644
index 0000000..3ce5f22
--- /dev/null
+++ b/check/circular-group-3.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/circgrp3
+
+Name: Circular Requires test 3
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Requires: circular-group-1 tail-line-1
+Libs: -lcircgrp3
+Cflags: -I${includedir}
diff --git a/check/head-line-1.pc b/check/head-line-1.pc
new file mode 100644
index 0000000..b90fe3d
--- /dev/null
+++ b/check/head-line-1.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/headline1
+
+Name: Archive Group test, linear-link head 1
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Requires: head-line-2
+Libs: -lhline1
+Cflags: -I${includedir}
diff --git a/check/head-line-2.pc b/check/head-line-2.pc
new file mode 100644
index 0000000..add366e
--- /dev/null
+++ b/check/head-line-2.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/headline2
+
+Name: Archive Group test, linear-link head 2
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Requires: circular-group-1
+Libs: -lhline2
+Cflags: -I${includedir}
diff --git a/check/tail-line-1.pc b/check/tail-line-1.pc
new file mode 100644
index 0000000..276aa3e
--- /dev/null
+++ b/check/tail-line-1.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/headline1
+
+Name: Archive Group test, linear-link tail 1
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Requires: tail-line-2
+Libs: -ltline1
+Cflags: -I${includedir}
diff --git a/check/tail-line-2.pc b/check/tail-line-2.pc
new file mode 100644
index 0000000..7bbcaac
--- /dev/null
+++ b/check/tail-line-2.pc
@@ -0,0 +1,10 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/headline2
+
+Name: Archive Group test, linear-link tail 2
+Description: Dummy package for testing circular Requires
+Version: 1.0.0
+Libs: -ltline2
+Cflags: -I${includedir}
diff --git a/main.c b/main.c
index 9b27d9a..e0361e6 100644
--- a/main.c
+++ b/main.c
@@ -62,6 +62,11 @@ static gboolean want_verbose_errors = FALSE;
 static gboolean want_stdout_errors = FALSE;
 static gboolean output_opt_set = FALSE;
 
+/* Required by get_components */
+gboolean want_groups = FALSE;
+char *group_prefix = NULL;
+char *group_suffix = NULL;
+
 void
 debug_spew (const char *format, ...)
 {
@@ -475,6 +480,17 @@ static const GOptionEntry options_table[] = {
   { "prefix-variable", 0, 0, G_OPTION_ARG_STRING, &prefix_variable,
     "set the name of the variable that pkg-config automatically sets",
     "PREFIX" },
+  { "emit-groups", 0, 0, G_OPTION_ARG_NONE, &want_groups,
+    "enable emitting link lines with cycles surrounded in archive group flags"
+    , NULL },
+  { "group-prefix", 0, 0, G_OPTION_ARG_STRING, &group_prefix,
+    "complete flag to be used as an archive group suffix, "
+    "defaults to '--start-group'",
+    "GROUP_PREFIX"},
+  { "group-suffix", 0, 0, G_OPTION_ARG_STRING, &group_suffix,
+    "complete flag to be used as an archive group suffix, "
+    "defaults to '--end-group'",
+    "GROUP_SUFFIX"},
 #ifdef G_OS_WIN32
   { "msvc-syntax", 0, 0, G_OPTION_ARG_NONE, &msvc_syntax,
     "output -l and -L flags for the Microsoft compiler (cl)", NULL },
@@ -651,6 +667,37 @@ main (int argc, char **argv)
       return 0;
     }
 
+  gboolean saw_group_start = group_prefix != NULL;
+  gboolean saw_group_end = group_suffix != NULL;
+  /* Ensure that --group-prefix and --group-suffix flags occur in pairs
+   * if they are present. Fail fatally if not */
+  if (saw_group_start ^ saw_group_end)
+    {
+      fprintf (stderr,
+               "--group-prefix must be accompanied by a "
+               "--group-suffix\n");
+      exit (1);
+    }
+
+  /* Assign default values to the the group prefix, suffix if both were
+   * unspecified */
+  if (!saw_group_start && !saw_group_end)
+    {
+      if (want_groups)
+        {
+
+          debug_spew ("Using defaults for archive group delimiters:\n"
+                      " group_prefix = '--start-group'\n"
+                      " group_suffix = '--end-group'\n");
+          group_prefix = "--start-group";
+          group_suffix = "--end-group";
+        }
+      else
+        {
+          group_prefix = "";
+          group_suffix = "";
+        }
+    }
   /* Collect packages from remaining args */
   str = g_string_new ("");
   while (argc > 1)
diff --git a/parse.c b/parse.c
index 6e9907c..9d2f5d2 100644
--- a/parse.c
+++ b/parse.c
@@ -1110,6 +1110,8 @@ parse_package_file (const char *key, const char *path,
   
   pkg = g_new0 (Package, 1);
   pkg->key = g_strdup (key);
+  pkg->cycle_edge_destination_count = 0;
+  pkg->cycle_edge_source_count = 0;
 
   if (path)
     {
diff --git a/pkg.c b/pkg.c
index f29ecc7..d08a742 100644
--- a/pkg.c
+++ b/pkg.c
@@ -51,6 +51,21 @@ gboolean ignore_requires = FALSE;
 gboolean ignore_requires_private = TRUE;
 gboolean ignore_private_libs = TRUE;
 
+static void
+print_package(gpointer pkg_, gpointer prefix)
+{
+  Package *pkg = pkg_;
+  debug_spew ("%sPackage{%s, U: %d, D: %d}\n",
+          (char *) prefix, pkg->key, pkg->cycle_edge_source_count,
+          pkg->cycle_edge_destination_count);
+}
+
+static void
+free_component(gpointer component_ptr)
+{
+  g_free (component_ptr);
+}
+
 void
 add_search_dir (const char *path)
 {
@@ -541,6 +556,171 @@ recursive_fill_list (Package *pkg, gboolean include_private,
   *listp = g_list_prepend (*listp, pkg);
 }
 
+static void
+print_component(gpointer component, gpointer line_prefix)
+{
+  Component* c = component;
+  char *start = c->start ? ((Package*) c->start->data)->key : "NULL";
+  char *end = c->end ? ((Package*) c->end->data)->key : "NULL";
+  debug_spew ("%sComponent{start: %s, end: %s}\n",
+              (char *) line_prefix, start, end);
+}
+
+static GList*
+get_components(GList* packages)
+{
+  /* Detect cycles in the dependency chain by finding the smallest dep index
+   * for each pacakge. A cycle occurs if any of its requires does not occur
+   * after the package. */
+  GHashTable *earliest_dep = g_hash_table_new(g_str_hash, g_str_equal);
+  GHashTable *pkg_index = g_hash_table_new(g_str_hash, g_str_equal);
+
+  int num_pkgs = g_list_length (packages);
+  int curr_idx = 0;
+
+  /* Containers to hold data referenced by value pointers passed to pkg_index,
+   * earliest_dep respectively.
+   *  - indices is an array containing the integer index of each package
+   *  - min_dep_idx records the smallest index from a package's requires*/
+  int *indices = g_new0(int, num_pkgs);
+  int *min_dep_idx = g_new(int, num_pkgs);
+
+  /* Default values for smallest dep index, since we only want positive values
+   * when there are cycles */
+  for(int idx = 0; idx < num_pkgs; ++idx)
+    {
+      min_dep_idx[idx] = -1;
+    }
+
+  /* Pre-process to load the index of each key into a table */
+  for (GList *pkg_it = packages; pkg_it != NULL; pkg_it = g_list_next (pkg_it))
+    {
+      indices[curr_idx] = curr_idx;
+      Package *pkg = pkg_it->data;
+      g_hash_table_replace (pkg_index, pkg->key, &indices[curr_idx]);
+      ++curr_idx;
+    }
+
+  /*
+   * Find the index of smallest dependency for each package and
+   * record back edges
+   */
+  curr_idx = 0;
+  for (GList *pkg_it = packages; pkg_it != NULL; pkg_it = g_list_next (pkg_it))
+    {
+      Package *pkg = pkg_it->data;
+      g_hash_table_insert (earliest_dep, pkg->key, &min_dep_idx[curr_idx]);
+      int curr_idx = *( (int *) g_hash_table_lookup (pkg_index, pkg->key));
+
+      for (GList* req_it = pkg->requires_private; req_it != NULL;
+           req_it = g_list_next(req_it))
+        {
+          Package *req = req_it->data;
+          int ridx = *( (int *) g_hash_table_lookup (pkg_index, req->key));
+
+          /*
+           * Back-edges are identified by looking at items in the requires
+           * list that were sorted before the current package, i.e. their
+           * DFS discovery time was earlier than this package's discovery time
+           */
+          if (ridx < curr_idx)
+            {
+              min_dep_idx[curr_idx] = ridx;
+              g_hash_table_replace (earliest_dep,
+                                    pkg->key, &min_dep_idx[curr_idx]);
+              pkg->cycle_edge_source_count += 1;
+              req->cycle_edge_destination_count += 1;
+            }
+        }
+      ++curr_idx;
+    }
+
+  debug_spew ("List of packages with cycle meta-data:\n");
+  g_list_foreach (packages, print_package, " ");
+
+  /* The current non-trivial component being processed */
+  Component *c_nontrivial = NULL;
+  /* Linked list that records all of the components we saw */
+  GList *components = NULL;
+
+  /* Running tally of up/down edges encounted. Always up to date with the
+   * destination count, lags by 1 iteration on the source count */
+  int cycle_tally = 0;
+
+  /* Current state that we are in. We can either be (IN/ON THE CUSP) of a
+   * cycle or not. */
+  gboolean in_cycle = FALSE;
+
+  /* Incorporate packages into components */
+  for (GList *pkg_it = packages; pkg_it != NULL; pkg_it = g_list_next (pkg_it))
+    {
+      Package *curr = pkg_it->data;
+      Package *prev = pkg_it->prev ? pkg_it->prev->data : NULL;
+      debug_spew("in_cycle: %s ", in_cycle ? "TRUE" : "FALSE");
+      print_package(curr, "processing ");
+      // Re-evaluate what the current state should be before assigning the
+      // current package to a component.
+      if (!in_cycle)
+        {
+            if (curr->cycle_edge_source_count != 0 ||
+                curr->cycle_edge_destination_count != 0)
+              {
+                // Initialize the NT component temporary variable
+                c_nontrivial = g_new0(Component, 1);
+                c_nontrivial->start = pkg_it;
+                c_nontrivial->non_trivial = TRUE;
+                in_cycle = TRUE;
+              }
+        }
+      else
+        {
+            if (prev && cycle_tally == prev->cycle_edge_source_count)
+              {
+                // Reset the NT component temporary variable after adding it
+                // to the components list
+                  c_nontrivial->end = pkg_it;
+                  components = g_list_prepend(components, c_nontrivial);
+                  c_nontrivial = NULL;
+                  in_cycle = FALSE;
+                  cycle_tally = 0;
+              }
+        }
+
+      if (!in_cycle)
+        {
+          Component *c = g_new0(Component, 1);
+          c->start = pkg_it;
+          c->end = g_list_next(pkg_it);
+          c->non_trivial = FALSE;
+          components = g_list_prepend(components, c);
+        }
+      else
+        {
+          cycle_tally += curr->cycle_edge_destination_count;
+          cycle_tally -= prev ? prev->cycle_edge_source_count : 0;
+        }
+    }
+
+  /* If we end on a cycle, add the current non_trivial component to the
+   * list of components */
+  if (in_cycle)
+    {
+      components = g_list_prepend(components, c_nontrivial);
+    }
+
+  /* Reverse them to match dependency order since we inserted by
+   * prepending */
+  components = g_list_reverse (components);
+  debug_spew ("Identified components:\n");
+  g_list_foreach (components, &print_component, "  ");
+  g_hash_table_destroy (pkg_index);
+  g_hash_table_destroy (earliest_dep);
+  g_free(indices);
+  g_free(min_dep_idx);
+
+  return components;
+}
+
 /* merge the flags from the individual packages */
 static GList *
 merge_flag_lists (GList *packages, FlagType type)
@@ -575,9 +755,91 @@ merge_flag_lists (GList *packages, FlagType type)
   return merged;
 }
 
+/* merge the flags from the individual packages, incorporating archive groups
+ * if necessary
+ */
+static GList *
+merge_flag_lists_by_component (GList *packages, FlagType type,
+                               gboolean repeat_cycles)
+{
+  if (repeat_cycles)
+    {
+      GList *merged = NULL;
+      GList *components = NULL;
+
+      debug_spew ("Merging flags by component\n");
+      components = get_components (packages);
+      for (; components != NULL; components = g_list_next (components))
+        {
+          Component* c = components->data;
+          GList *dashl = NULL;
+          if (c->non_trivial)
+            {
+              debug_spew ("Reading non-trivial component: ");
+            }
+          else
+            {
+              debug_spew ("Reading trivial component:     ");
+            }
+          print_component (c, "");
+
+          if (c->non_trivial && want_groups)
+            {
+              Flag *start = g_new0(Flag, 1);
+              start->type = GROUP_DELIM;
+              start->arg = group_prefix;
+
+              dashl = g_list_append(dashl, start);
+            }
+          for (GList* begin = c->start;
+               begin != c->end;
+               begin = g_list_next (begin))
+            {
+              Package* pkg = begin->data;
+              print_package (pkg, "  ");
+              GList *flags = (type & LIBS_ANY) ? pkg->libs : pkg->cflags;
+
+              /* manually copy the elements so we can keep track of the end.
+               * This code is duplicated to avoid the quadratic cost
+               * involved in finding the last element for every component chunk.
+               *
+               * SEE: merge_flag_lists(GList *packages, FlagType type)
+               */
+              for (; flags != NULL; flags = g_list_next (flags))
+                {
+                  Flag *flag = flags->data;
+
+                  if (flag->type & type)
+                    {
+                      dashl = g_list_append(dashl, flag);
+                    }
+                }
+            }
+          if (c->non_trivial && want_groups)
+            {
+              Flag *end = g_new0(Flag, 1);
+              end->type = GROUP_DELIM;
+              end->arg = group_suffix;
+              dashl = g_list_append(dashl, end);
+            }
+
+          /* This is only done for flags matching the mask
+           * (LIBS_l | LIBS_OTHER) */
+          merged = g_list_concat(merged, dashl);
+        }
+      g_list_free_full (components, free_component);
+      return merged;
+    }
+  else
+    {
+      return merge_flag_lists (packages, type);
+    }
+}
+
 static GList *
 fill_list (GList *packages, FlagType type,
-           gboolean in_path_order, gboolean include_private)
+           gboolean in_path_order, gboolean include_private,
+           gboolean capture_cycles)
 {
   GList *tmp;
   GList *expanded = NULL;
@@ -599,7 +861,7 @@ fill_list (GList *packages, FlagType type,
       spew_package_list ("  sorted", expanded);
     }
 
-  flags = merge_flag_lists (expanded, type);
+  flags = merge_flag_lists_by_component (expanded, type, capture_cycles);
   g_list_free (expanded);
 
   return flags;
@@ -912,12 +1174,16 @@ verify_package (Package *pkg)
  */
 static char *
 get_multi_merged (GList *pkgs, FlagType type, gboolean in_path_order,
-                  gboolean include_private)
+                  gboolean include_private, gboolean capture_cycles)
 {
   GList *list;
   char *retval;
 
-  list = fill_list (pkgs, type, in_path_order, include_private);
+  list = fill_list (pkgs,
+                    type,
+                    in_path_order,
+                    include_private,
+                    capture_cycles);
   list = flag_list_strip_duplicates (list);
   retval = flag_list_to_string (list);
   g_list_free (list);
@@ -936,21 +1202,21 @@ 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, TRUE, FALSE);
       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, TRUE, FALSE);
       debug_spew ("adding CFLAGS_I string \"%s\"\n", cur);
       g_string_append (str, cur);
       g_free (cur);
     }
   if (flags & LIBS_L)
     {
-      cur = get_multi_merged (pkgs, LIBS_L, TRUE, !ignore_private_libs);
+      cur = get_multi_merged (pkgs, LIBS_L, TRUE, !ignore_private_libs, FALSE);
       debug_spew ("adding LIBS_L string \"%s\"\n", cur);
       g_string_append (str, cur);
       g_free (cur);
@@ -958,7 +1224,9 @@ packages_get_flags (GList *pkgs, FlagType flags)
   if (flags & (LIBS_OTHER | LIBS_l))
     {
       cur = get_multi_merged (pkgs, flags & (LIBS_OTHER | LIBS_l), FALSE,
-                              !ignore_private_libs);
+                              !ignore_private_libs,
+                              (!ignore_requires_private &&
+                               !ignore_private_libs));
       debug_spew ("adding LIBS_OTHER | LIBS_l string \"%s\"\n", cur);
       g_string_append (str, cur);
       g_free (cur);
diff --git a/pkg.h b/pkg.h
index c6732bd..01272e3 100644
--- a/pkg.h
+++ b/pkg.h
@@ -29,6 +29,7 @@ typedef guint8 FlagType; /* bit mask for flag types */
 #define LIBS_OTHER   (1 << 2)
 #define CFLAGS_I     (1 << 3)
 #define CFLAGS_OTHER (1 << 4)
+#define GROUP_DELIM  (1 << 5)
 
 #define LIBS_ANY     (LIBS_l | LIBS_L | LIBS_OTHER)
 #define CFLAGS_ANY   (CFLAGS_I | CFLAGS_OTHER)
@@ -47,6 +48,7 @@ typedef enum
 
 typedef struct Flag_ Flag;
 typedef struct Package_ Package;
+typedef struct Component_ Component;
 typedef struct RequiredVersion_ RequiredVersion;
 
 struct Flag_
@@ -85,6 +87,29 @@ struct Package_
   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 */
   char *orig_prefix; /* original prefix value before redefinition */
+  int cycle_edge_source_count; /* The number of times this vertex was the
+                                  source vertex of an edge that was skipped
+                                  to maintain acyclicicty */
+  int cycle_edge_destination_count; /* The number of times this vertex was the
+                                       destination vertex of an edge that was
+                                       skipped to main acyclicicy */
+};
+
+/* A component is any collection of possibly inter-dependent packages
+* which contains *AT MOST 1* strongly connected component from the original
+* dependency graph.
+*
+* Two components from the same dependency graph *CANNOT* overlap as they are
+* disjoint partitionings of the DAG generated after partitioning the original
+* topologically sorted dependency graph.
+*/
+struct Component_
+{
+  GList* start;     /* Iterator to the first package in the component */
+  GList* end;       /* Iterator to the last package in the component,
+                       can be the same as `start` */
+  gboolean non_trivial; /* A flag that is set if this component contains more
+                           than one package */
 };
 
 Package *get_package               (const char *name);
@@ -142,6 +167,11 @@ extern gboolean define_prefix;
 /* The name of the variable that acts as prefix, unless it is "prefix" */
 extern char *prefix_variable;
 
+/* Hold the group prefix and suffix to be used when emitting archive groups */
+extern gboolean want_groups;
+extern char *group_prefix;
+extern char *group_suffix;
+
 #ifdef G_OS_WIN32
 /* If TRUE, output flags in MSVC syntax. */
 extern gboolean msvc_syntax;
-- 
2.19.1



More information about the pkg-config mailing list