From a3ec43a2190dc5ead8e63310a1df743f36a673a5 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Wed, 4 Feb 2004 18:02:55 +0000 Subject: [PATCH] add gst-run wrapper code build original tools as versioned tools Original commit message from CVS: add gst-run wrapper code build original tools as versioned tools --- ChangeLog | 10 ++ configure.ac | 4 - tools/Makefile.am | 87 ++++++++--- tools/gst-run.c | 387 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 465 insertions(+), 23 deletions(-) create mode 100644 tools/gst-run.c diff --git a/ChangeLog b/ChangeLog index c2469850e9..27fd5a7a6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2004-02-04 Thomas Vander Stichele + + * configure.ac: + * tools/Makefile.am: + * tools/gst-run.c: (popt_callback), (hash_print_key), + (find_highest_version), (unmangle_libtool), (get_dir_of_binary), + (get_candidates), (main): + add new source file to generate non-versioned wrapper binaries + for our tools. + 2004-02-04 Benjamin Otte * gst/gstevent.c: (_gst_event_free): diff --git a/configure.ac b/configure.ac index ba85187f00..297a1e58c4 100644 --- a/configure.ac +++ b/configure.ac @@ -5,10 +5,6 @@ dnl when going to/from release please set the nano (fourth number) right ! dnl releases only do Wall, cvs and prerelease does Werror too AS_VERSION(gstreamer, GST_VERSION, 0, 7, 3, 1, GST_CVS="no", GST_CVS="yes") -if test x$program_suffix = xNONE ; then - program_suffix=-$GST_VERSION_MAJOR.$GST_VERSION_MINOR -fi - dnl AM_MAINTAINER_MODE only provides the option to configure to enable it AM_MAINTAINER_MODE AM_INIT_AUTOMAKE($PACKAGE,$VERSION) diff --git a/tools/Makefile.am b/tools/Makefile.am index eb32b3a51f..9ceecb2a64 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,33 +1,93 @@ +### assemble a list of programs we want to build and install if GST_DISABLE_REGISTRY GST_REGISTRY_SRC = +GST_REGISTRY_SRC_V = else GST_REGISTRY_SRC = gst-register +GST_REGISTRY_SRC_V = gst-register-@GST_MAJORMINOR@ endif if GST_DISABLE_LOADSAVE GST_LOADSAVE_SRC = +GST_LOADSAVE_SRC_V = else -GST_LOADSAVE_SRC = gst-compprep gst-xmllaunch gst-complete +GST_LOADSAVE_SRC = gst-complete gst-compprep gst-xmllaunch +GST_LOADSAVE_SRC_V = \ + gst-complete-@GST_MAJORMINOR@ \ + gst-compprep-@GST_MAJORMINOR@ \ + gst-xmllaunch-@GST_MAJORMINOR@ endif +GST_OTHER_SRC = \ + gst-inspect \ + gst-launch \ + gst-md5sum \ + gst-typefind \ + gst-xmlinspect +GST_OTHER_SRC_V = \ + gst-inspect-@GST_MAJORMINOR@ \ + gst-launch-@GST_MAJORMINOR@ \ + gst-md5sum-@GST_MAJORMINOR@ \ + gst-typefind-@GST_MAJORMINOR@ \ + gst-xmlinspect-@GST_MAJORMINOR@ + + +### so all of the programs we want to build +bin_PROGRAMS = \ + $(GST_REGISTRY_SRC) $(GST_REGISTRY_SRC_V) \ + $(GST_LOADSAVE_SRC) $(GST_LOADSAVE_SRC_V) \ + $(GST_OTHER_SRC) $(GST_OTHER_SRC_V) + manpages = gst-launch.1 gst-md5sum.1 gst-register.1 gst-inspect.1 \ gst-complete.1 gst-compprep.1 gst-xmllaunch.1 \ gst-feedback.1 CLEANFILES = $(manpages) man_MANS = $(manpages) + EXTRA_DIST = gst-launch.1.in gst-md5sum.1.in gst-register.1.in gst-inspect.1.in \ gst-complete.1.in gst-compprep.1.in gst-xmllaunch.1.in \ gst-feedback.1.in EXTRA_DIST += gst-feedback -bin_PROGRAMS = gst-launch \ - $(GST_REGISTRY_SRC) \ - gst-inspect \ - gst-xmlinspect \ - gst-typefind \ - $(GST_LOADSAVE_SRC) \ - gst-md5sum +# make sure each versioned tool has the right source file +if !GST_DISABLE_REGISTRY +gst_register_@GST_MAJORMINOR@_SOURCES = gst-register.c +endif +if !GST_DISABLE_LOADSAVE +gst_complete_@GST_MAJORMINOR@_SOURCES = gst-complete.c +gst_compprep_@GST_MAJORMINOR@_SOURCES = gst-compprep.c +gst_xmllaunch_@GST_MAJORMINOR@_SOURCES = gst-launch.c +endif +gst_inspect_@GST_MAJORMINOR@_SOURCES = gst-inspect.c +gst_launch_@GST_MAJORMINOR@_SOURCES = gst-launch.c +gst_md5sum_@GST_MAJORMINOR@_SOURCES = gst-md5sum.c +gst_typefind_@GST_MAJORMINOR@_SOURCES = gst-typefind.c +gst_xmlinspect_@GST_MAJORMINOR@_SOURCES = gst-inspect.c + +# make sure each unversioned tool comes from gst-run.c +if !GST_DISABLE_REGISTRY +gst_register_SOURCES = gst-run.c +endif +if !GST_DISABLE_LOADSAVE +gst_complete_SOURCES = gst-run.c +gst_compprep_SOURCES = gst-run.c +gst_xmllaunch_SOURCES = gst-run.c +endif +gst_inspect_SOURCES = gst-run.c +gst_launch_SOURCES = gst-run.c +gst_md5sum_SOURCES = gst-run.c +gst_typefind_SOURCES = gst-run.c +gst_xmlinspect_SOURCES = gst-run.c + +# set correct compile and link flags for all, as well as for specific +AM_CFLAGS = $(GST_CFLAGS) +AM_LDFLAGS = $(GST_LIBS) + +gst_inspect_@GST_MAJORMINOR@_LDADD = ../libs/gst/control/libgstcontrol-@GST_MAJORMINOR@.la + +gst_xmlinspect_@GST_MAJORMINOR@_LDADD = ../libs/gst/control/libgstcontrol-@GST_MAJORMINOR@.la + bin_SCRIPTS = gst-feedback @@ -44,14 +104,3 @@ SUFFIXES = .1 .1.in -e s,gst.-xmllaunch,`echo gst-xmllaunch | sed $(program_transform_name) | sed s/-/\\-/`,g \ $< >$@ -AM_CFLAGS = $(GST_CFLAGS) -AM_LDFLAGS = $(GST_LIBS) - -gst_inspect_LDADD = ../libs/gst/control/libgstcontrol-@GST_MAJORMINOR@.la - -gst_xmlinspect_LDADD = ../libs/gst/control/libgstcontrol-@GST_MAJORMINOR@.la - -if !GST_DISABLE_REGISTRY -gst_xmllaunch_SOURCES = gst-launch.c -endif - diff --git a/tools/gst-run.c b/tools/gst-run.c new file mode 100644 index 0000000000..56a8caaea5 --- /dev/null +++ b/tools/gst-run.c @@ -0,0 +1,387 @@ +/* GStreamer + * Copyright (C) 2004 Thomas Vander Stichele + * + * gst-run.c: tool to launch GStreamer tools with correct major/minor + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +enum +{ + ARG_MM = 1, + ARG_LIST_MM, + ARG_PRINT, + ARG_HELP +}; + +/* global statics for option parsing */ +static gboolean _print = FALSE; +static gchar * _arg_mm = NULL; +static gboolean _arg_list_mm = FALSE; + +/* callback to parse arguments */ +static void +popt_callback (poptContext context, enum poptCallbackReason reason, + const struct poptOption *option, const char *arg, void *data) +{ + if (reason == POPT_CALLBACK_REASON_OPTION) + { + switch (option->val) + { + case ARG_MM: + _arg_mm = g_strdup (arg); + break; + case ARG_LIST_MM: + _arg_list_mm = TRUE; + break; + case ARG_PRINT: + _print = TRUE; + break; + case ARG_HELP: + poptPrintHelp (context, stdout, 0); + g_print ("\n"); + break; + } + } + else + { + g_print ("Unknown reason for callback\n"); + } +} + +/* popt options table for the wrapper */ +static struct poptOption wrapper_options[] = +{ + { NULL, '\0', + POPT_ARG_CALLBACK, + (void *) &popt_callback, 0, NULL, NULL }, + { "help", '\0', + POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, + NULL, ARG_HELP, ("Show help"), NULL }, + { "?", '\0', + POPT_ARG_NONE | POPT_ARGFLAG_STRIP | POPT_ARGFLAG_ONEDASH + | POPT_ARGFLAG_DOC_HIDDEN, + NULL, ARG_HELP, NULL, NULL }, + /* We cheat by specifying -p as long "p" with onedash, so that it + also gets stripped properly from our arg flags */ + { "p", '\0', + POPT_ARG_NONE | POPT_ARGFLAG_STRIP | POPT_ARGFLAG_ONEDASH + | POPT_ARGFLAG_DOC_HIDDEN, + NULL, ARG_PRINT, NULL, NULL }, + { "print", '\0', + POPT_ARG_NONE | POPT_ARGFLAG_STRIP, + NULL, ARG_PRINT, ("Print wrapped command line"), NULL }, + { "gst-mm", '\0', + POPT_ARG_STRING | POPT_ARGFLAG_STRIP, + NULL, ARG_MM, ("Force major/minor version"), NULL }, + { "gst-list-mm", '\0', + POPT_ARG_NONE | POPT_ARGFLAG_STRIP, + NULL, ARG_LIST_MM, ("List found major/minor versions"), NULL }, + POPT_TABLEEND +}; + +/* helper table including our wrapper options */ +static struct poptOption options[] = +{ + { NULL, '\0', POPT_ARG_INCLUDE_TABLE, wrapper_options, 0, + "Wrapper options:", NULL}, + POPT_TABLEEND +}; + + +/* print out the major/minor, which is the hash key */ +static void +hash_print_key (gchar * key, gchar * value) +{ + g_print ("%s\n", (gchar *) key); +} + +static void +find_highest_version (gchar * key, gchar * value, gchar ** highest) +{ + if (*highest == NULL) + { + /* first value, so just set it */ + *highest = key; + } + if (strcmp (key, *highest) > 0) *highest = key; +} + +/* Libtool creates shell scripts named "base" that calls actual binaries as + * .libs/lt-base. If we detect this is a libtool script, unmangle so we + * find the right binaries */ +static void +unmangle_libtool (gchar ** dir, gchar ** base) +{ + gchar *new_dir, *new_base; + + if (!*dir) return; + if (!*base) return; + + /* we assume libtool when base starts with lt- and dir ends with .libs */ + if (!g_str_has_prefix (*base, "lt-")) return; + if (!g_str_has_suffix (*dir, ".libs")) return; + + new_base = g_strdup (&((*base)[3])); + new_dir = g_path_get_dirname (*dir); + g_free (*base); + g_free (*dir); + *base = new_base; + *dir = new_dir; +} + +/* Returns a directory path that contains the binary given as an argument. + * If the binary given contains a path, it gets looked for in that path. + * If it doesn't contain a path, it gets looked for in the standard path. + * + * The returned string is newly allocated. + */ +gchar * +get_dir_of_binary (const gchar * binary) +{ + gchar *base, *dir; + gchar *full; + + base = g_path_get_basename (binary); + dir = g_path_get_dirname (binary); + + /* if putting these two together yields the same as binary, + * then we have the right breakup. If not, it's because no path was + * specified which caused get_basename to return "." */ + full = g_build_filename (dir, base, NULL); + + if (strcmp (full, binary) != 0) + { + if (strcmp (dir, ".") != 0) + { + g_warning ("This should not happen, g_path_get_dirname () has changed."); + g_free (base); + g_free (dir); + g_free (full); + return NULL; + } + + /* we know no path was specified, so search standard path for binary */ + g_free (full); + full = g_find_program_in_path (base); + if (!full) + { + g_warning ("This should not happen, %s not in standard path.", base); + g_free (base); + g_free (dir); + return NULL; + } + } + + g_free (base); + g_free (dir); + dir = g_path_get_dirname (full); + g_free (full); + + return dir; +} + +/* Search the given directory for candidate binaries matching the base binary. + * Return a GHashTable of major/minor -> directory pairs + */ +GHashTable * +get_candidates (const gchar * dir, const gchar * base) +{ + GDir *gdir; + GError *error = NULL; + const gchar *entry; + gchar *path; + gchar *suffix; + + gchar *pattern; + GPatternSpec *spec; + + + gchar *test; + + gchar **dirs; + gchar **cur; + + GHashTable *candidates = NULL; + + candidates = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* compile our pattern spec */ + pattern = g_strdup_printf ("%s-*.*", base); + spec = g_pattern_spec_new (pattern); + g_free (pattern); + + /* get all dirs from the path and prepend with given dir */ + path = g_strdup_printf ("%s%c%s", + dir, G_SEARCHPATH_SEPARATOR, getenv ("PATH")); + dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0); + g_free (path); + + /* check all of these in reverse order by winding to bottom and going up */ + cur = &dirs[0]; + while (*cur) ++cur; + + while (cur != &dirs[0]) + { + --cur; + if (! g_file_test (*cur, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + continue; + + gdir = g_dir_open (*cur, 0, &error); + if (! gdir) + { + g_warning ("Could not open dir %s: %s", *cur, error->message); + g_error_free (error); + return NULL; + } + while ((entry = g_dir_read_name (gdir))) + { + if (g_pattern_match_string (spec, entry)) + { + gchar *full; + + /* is it executable ? */ + full = g_build_filename (*cur, entry, NULL); + if (! g_file_test (full, G_FILE_TEST_IS_EXECUTABLE)) + { + g_free (full); + continue; + } + g_free (full); + + /* strip base and dash from it */ + suffix = g_strdup (&(entry[strlen (base) + 1])); + + /* stricter pattern check: check if it only contains digits or dots */ + test = g_strdup (suffix); + g_strcanon (test, "0123456789.", 'X'); + if (strstr (test, "X")) + { + g_free (test); + continue; + } + g_free (test); + g_hash_table_insert (candidates, suffix, g_strdup (*cur)); + } + } + } + + g_strfreev (dirs); + g_pattern_spec_free (spec); + return candidates; +} + +int main +(int argc, char **argv) +{ + GHashTable *candidates; + gchar *dir; + gchar *base; + gchar *highest = NULL; + gchar *binary; /* actual binary we're going to run */ + gchar *path = NULL; /* and its path */ + poptContext ctx; + int nextopt; + + /* parse command line options */ + ctx = poptGetContext ("gst-run", argc, (const char **) argv, options, 0); + poptReadDefaultConfig (ctx, TRUE); + while ((nextopt = poptGetNextOpt (ctx)) > 0) + ; + + argc = poptStrippedArgv (ctx, argc, argv); + argv[argc] = NULL; + poptFreeContext (ctx); + + /* detect stuff */ + dir = get_dir_of_binary (argv[0]); + base = g_path_get_basename (argv[0]); + + /* unmangle libtool if necessary */ + unmangle_libtool (&dir, &base); + + /* get all candidate binaries */ + candidates = get_candidates (dir, base); + g_free (dir); + + if (_arg_mm) + { + /* if a version was forced, look it up in the hash table */ + dir = g_hash_table_lookup (candidates, _arg_mm); + if (!dir) + { + g_print ("ERROR: Major/minor %s of tool %s not found.\n", _arg_mm, base); + return 1; + } + binary = g_strdup_printf ("%s-%s", base, _arg_mm); + } + else + { + /* otherwise, just look up the highest version */ + g_hash_table_foreach (candidates, (GHFunc) find_highest_version, + &highest); + dir = g_hash_table_lookup (candidates, highest); + if (!dir) + { + g_print ("ERROR: No version of tool %s not found.\n", base); + return 1; + } + binary = g_strdup_printf ("%s-%s", base, highest); + } + + path = g_build_filename (dir, binary, NULL); + g_free (binary); + + /* print out list of major/minors we found if asked for */ + /* FIXME: do them in order by creating a GList of keys and sort them */ + if (_arg_list_mm) + { + g_hash_table_foreach (candidates, (GHFunc) hash_print_key, NULL); + } + + /* print out command line if asked for */ + argv[0] = path; + if (_print) + { + int i; + for (i = 0; i < argc; ++i) + { + g_print ("%s", argv[i]); + if (i < argc - 1) g_print (" "); + } + g_print ("\n"); + } + + /* execute */ + if (execv (path, argv) == -1) + { + g_warning ("Error executing %s: %s (%d)", path, g_strerror (errno), errno); + } + g_free (path); + + return 0; +}