From yvvaibhav at gmail.com Tue Feb 26 13:12:22 2019 From: yvvaibhav at gmail.com (yvvaibhav at gmail.com) Date: Tue, 26 Feb 2019 08:12:22 -0500 Subject: [PATCH] Detect strongly connected components in package chains to handle cycles Message-ID: <20190226131222.99362-1-vyenamandra@bloomberg.net> From: Vaibhav Yenamandra 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