Optimization to load only needed .pc files

Currently pkg-config scans all .pc files from the search path during
initialization. That makes some of the code simpler and works fine when
there are not many .pc files on the system. When there are a lot of .pc
files, then this represents a lot of wasted effort.

Rework the package gathering so that it happens as needed. To support
the --list-all mode, the scanning at initialization can still be done.

https://bugs.freedesktop.org/show_bug.cgi?id=98215
This commit is contained in:
Marco Diego Aurélio Mesquita 2016-10-12 12:08:40 -03:00 committed by Dan Nicholson
parent a38d5c58a1
commit c53385b5db
3 changed files with 59 additions and 98 deletions

2
main.c
View file

@ -643,7 +643,7 @@ main (int argc, char **argv)
return 1; return 1;
} }
package_init (); package_init (want_list);
if (want_list) if (want_list)
{ {

153
pkg.c
View file

@ -43,11 +43,8 @@
static void verify_package (Package *pkg); static void verify_package (Package *pkg);
static GHashTable *packages = NULL; static GHashTable *packages = NULL;
static GHashTable *locations = NULL;
static GHashTable *path_positions = NULL;
static GHashTable *globals = NULL; static GHashTable *globals = NULL;
static GList *search_dirs = NULL; static GList *search_dirs = NULL;
static int scanned_dir_count = 0;
gboolean disable_uninstalled = FALSE; gboolean disable_uninstalled = FALSE;
gboolean ignore_requires = FALSE; gboolean ignore_requires = FALSE;
@ -121,6 +118,8 @@ name_ends_in_uninstalled (const char *str)
return FALSE; return FALSE;
} }
static Package *
internal_get_package (const char *name, gboolean warn);
/* Look for .pc files in the given directory and add them into /* Look for .pc files in the given directory and add them into
* locations, ignoring duplicates * locations, ignoring duplicates
@ -129,7 +128,7 @@ static void
scan_dir (char *dirname) scan_dir (char *dirname)
{ {
GDir *dir; GDir *dir;
const gchar *d_name; const gchar *filename;
int dirnamelen = strlen (dirname); int dirnamelen = strlen (dirname);
/* Use a copy of dirname cause Win32 opendir doesn't like /* Use a copy of dirname cause Win32 opendir doesn't like
@ -161,59 +160,20 @@ scan_dir (char *dirname)
dir = g_dir_open (dirname_copy, 0 , NULL); dir = g_dir_open (dirname_copy, 0 , NULL);
g_free (dirname_copy); g_free (dirname_copy);
scanned_dir_count += 1;
if (!dir) if (!dir)
{ {
debug_spew ("Cannot open directory #%i '%s' in package search path: %s\n", debug_spew ("Cannot open directory '%s' in package search path: %s\n",
scanned_dir_count, dirname, g_strerror (errno)); dirname, g_strerror (errno));
return; return;
} }
debug_spew ("Scanning directory #%i '%s'\n", scanned_dir_count, dirname); debug_spew ("Scanning directory '%s'\n", dirname);
while ((d_name = g_dir_read_name(dir))) while ((filename = g_dir_read_name(dir)))
{ {
int len = strlen (d_name); char *path = g_build_filename (dirname, filename, NULL);
internal_get_package (path, FALSE);
if (ends_in_dotpc (d_name)) g_free (path);
{
char *pkgname = g_malloc (len - EXT_LEN + 1);
debug_spew ("File '%s' appears to be a .pc file\n", d_name);
strncpy (pkgname, d_name, len - EXT_LEN);
pkgname[len-EXT_LEN] = '\0';
if (g_hash_table_lookup (locations, pkgname))
{
debug_spew ("File '%s' ignored, we already know about package '%s'\n", d_name, pkgname);
g_free (pkgname);
}
else
{
char *filename = g_malloc (dirnamelen + 1 + len + 1);
strncpy (filename, dirname, dirnamelen);
filename[dirnamelen] = G_DIR_SEPARATOR;
strcpy (filename + dirnamelen + 1, d_name);
if (g_file_test(filename, G_FILE_TEST_IS_REGULAR) == TRUE) {
g_hash_table_insert (locations, pkgname, filename);
g_hash_table_insert (path_positions, pkgname,
GINT_TO_POINTER (scanned_dir_count));
debug_spew ("Will find package '%s' in file '%s'\n",
pkgname, filename);
} else {
debug_spew ("Ignoring '%s' while looking for '%s'; not a "
"regular file.\n", pkgname, filename);
}
}
}
else
{
debug_spew ("Ignoring file '%s' in search directory; not a .pc file\n",
d_name);
}
} }
g_dir_close (dir); g_dir_close (dir);
} }
@ -243,31 +203,31 @@ add_virtual_pkgconfig_package (void)
} }
void void
package_init () package_init (gboolean want_list)
{ {
static gboolean initted = FALSE; if (packages)
return;
if (!initted)
{
initted = TRUE;
packages = g_hash_table_new (g_str_hash, g_str_equal); packages = g_hash_table_new (g_str_hash, g_str_equal);
locations = g_hash_table_new (g_str_hash, g_str_equal);
path_positions = g_hash_table_new (g_str_hash, g_str_equal);
add_virtual_pkgconfig_package ();
g_list_foreach (search_dirs, (GFunc)scan_dir, NULL); if (want_list)
} g_list_foreach (search_dirs, (GFunc)scan_dir, NULL);
else
/* Should not add virtual pkgconfig package when listing to be
* compatible with old code that only listed packages from real
* files */
add_virtual_pkgconfig_package ();
} }
static Package * static Package *
internal_get_package (const char *name, gboolean warn) internal_get_package (const char *name, gboolean warn)
{ {
Package *pkg = NULL; Package *pkg = NULL;
char *key; char *key = NULL;
const char *location; char *location = NULL;
unsigned int path_position = 0;
GList *iter; GList *iter;
GList *dir_iter;
pkg = g_hash_table_lookup (packages, name); pkg = g_hash_table_lookup (packages, name);
@ -280,7 +240,8 @@ internal_get_package (const char *name, gboolean warn)
if ( ends_in_dotpc (name) ) if ( ends_in_dotpc (name) )
{ {
debug_spew ("Considering '%s' to be a filename rather than a package name\n", name); debug_spew ("Considering '%s' to be a filename rather than a package name\n", name);
location = name; location = g_strdup (name);
key = g_strdup (name);
} }
else else
{ {
@ -303,7 +264,18 @@ internal_get_package (const char *name, gboolean warn)
} }
} }
location = g_hash_table_lookup (locations, name); for (dir_iter = search_dirs; dir_iter != NULL;
dir_iter = g_list_next (dir_iter))
{
path_position++;
location = g_strdup_printf ("%s%c%s.pc", (char*)dir_iter->data,
G_DIR_SEPARATOR, name);
if (g_file_test (location, G_FILE_TEST_IS_REGULAR))
break;
g_free (location);
location = NULL;
}
} }
if (location == NULL) if (location == NULL)
@ -317,21 +289,13 @@ internal_get_package (const char *name, gboolean warn)
return NULL; return NULL;
} }
if (location != name) if (key == NULL)
key = g_strdup (name); key = g_strdup (name);
else else
{ {
/* need to strip package name out of the filename */ /* need to strip package name out of the filename */
int len = strlen (name); key = g_path_get_basename (name);
const char *end = name + (len - EXT_LEN); key[strlen (key) - EXT_LEN] = '\0';
const char *start = end;
while (start != name && *start != G_DIR_SEPARATOR)
--start;
g_assert (end >= start);
key = g_strndup (start, end - start);
} }
debug_spew ("Reading '%s' from file '%s'\n", name, location); debug_spew ("Reading '%s' from file '%s'\n", name, location);
@ -339,17 +303,18 @@ internal_get_package (const char *name, gboolean warn)
ignore_private_libs, ignore_requires_private); ignore_private_libs, ignore_requires_private);
g_free (key); g_free (key);
if (pkg != NULL && strstr (location, "uninstalled.pc"))
pkg->uninstalled = TRUE;
g_free (location);
if (pkg == NULL) if (pkg == NULL)
{ {
debug_spew ("Failed to parse '%s'\n", location); debug_spew ("Failed to parse '%s'\n", location);
return NULL; return NULL;
} }
if (strstr (location, "uninstalled.pc")) pkg->path_position = path_position;
pkg->uninstalled = TRUE;
pkg->path_position =
GPOINTER_TO_INT (g_hash_table_lookup (path_positions, pkg->key));
debug_spew ("Path position of '%s' is %d\n", debug_spew ("Path position of '%s' is %d\n",
pkg->key, pkg->path_position); pkg->key, pkg->path_position);
@ -1170,19 +1135,15 @@ max_len_foreach (gpointer key, gpointer value, gpointer data)
static void static void
packages_foreach (gpointer key, gpointer value, gpointer data) packages_foreach (gpointer key, gpointer value, gpointer data)
{ {
Package *pkg = get_package (key); Package *pkg = value;
char *pad;
if (pkg != NULL) pad = g_strnfill (GPOINTER_TO_INT (data) - strlen (pkg->key), ' ');
{
char *pad;
pad = g_strnfill (GPOINTER_TO_INT (data) - strlen (pkg->key), ' ');
printf ("%s%s%s - %s\n", printf ("%s%s%s - %s\n",
pkg->key, pad, pkg->name, pkg->description); pkg->key, pad, pkg->name, pkg->description);
g_free (pad); g_free (pad);
}
} }
void void
@ -1193,8 +1154,8 @@ print_package_list (void)
ignore_requires = TRUE; ignore_requires = TRUE;
ignore_requires_private = TRUE; ignore_requires_private = TRUE;
g_hash_table_foreach (locations, max_len_foreach, &mlen); g_hash_table_foreach (packages, max_len_foreach, &mlen);
g_hash_table_foreach (locations, packages_foreach, GINT_TO_POINTER (mlen + 1)); g_hash_table_foreach (packages, packages_foreach, GINT_TO_POINTER (mlen + 1));
} }
void void

2
pkg.h
View file

@ -98,7 +98,7 @@ char * packages_get_var (GList *pkgs,
void add_search_dir (const char *path); void add_search_dir (const char *path);
void add_search_dirs (const char *path, const char *separator); void add_search_dirs (const char *path, const char *separator);
void package_init (void); void package_init (gboolean want_list);
int compare_versions (const char * a, const char *b); int compare_versions (const char * a, const char *b);
gboolean version_test (ComparisonType comparison, gboolean version_test (ComparisonType comparison,
const char *a, const char *a,