#!/usr/bin/perl -w # vi: set ts=4: # # # GStreamer developers: please add comments on any tests you think # are dumb or have too many false positives. # # # Future ideas: # - spell check comments # - check each function for at least one assertion (?) # - check parameters that init/set/get have consistent types # - check for gst_caps_set() without check for writeability # - check .so files for stray symbols # # # Random "other" testing ideas # - load each plugin individually # sub check_copyright(); sub check_license(); sub check_buffer_alloc(); sub check_bad_includes(); sub check_begin_decls(); sub check_c99_comments(); sub check_carriage_returns(); sub check_printf_lld(); sub check_glibisms(); sub check_indentation(); sub check_gst_props_set(); sub check_deprecated(); sub check_config_h(); sub check_varargs_functions(); sub check_debugging(); sub check_old_typefind(); sub check_bad_casts(); sub check_old_plugin(); sub check_signal_new(); sub check_gnuc_const(); sub check_caps(); sub check_lib_deprecated(); sub check_typo(); sub check_explicit_caps(); sub m_check_plugindir(); sub m_check_interfaces(); open FIND, "find . -name \"*.[ch]\" -print|"; foreach $filename () { chomp $filename; open FILE, "$filename"; @lines = ; close FILE; print "I: $filename\n"; # important stuff check_bad_includes(); check_printf_lld(); check_gst_props_set(); check_deprecated(); check_config_h(); check_old_typefind(); check_old_plugin(); check_caps(); check_lib_deprecated(); check_typo(); check_glibisms(); check_explicit_caps(); # less important stuff if (0) { check_copyright(); check_license(); check_gnuc_const(); check_begin_decls(); check_buffer_alloc(); check_c99_comments(); check_carriage_returns(); #check_indentation(); check_varargs_functions(); check_debugging(); check_bad_casts(); check_signal_new(); } } open FIND, "find . -name \"Makefile.am\" -print|"; foreach $filename () { chomp $filename; open FILE, "$filename"; @lines = ; close FILE; print "I: $filename\n"; m_check_plugindir(); m_check_interfaces(); } # # Every source file must have a copyright block # sub check_copyright() { if (! grep { /copyright/i; } @lines) { print "E: no copyright block\n"; } } # # Every source file should have a license statement # sub check_license() { if (grep { /Lesser General Public License/; } @lines) { print "I: license is LGPL\n"; } elsif (grep { /Library General Public License/; } @lines) { print "I: license is LGPL\n"; print "W: copyright header uses \"Library\" LGPL\n"; } elsif (grep { /General Public License/; } @lines) { print "I: license is GPL\n"; } else { print "E: unknown license or no copyright block\n"; } } # # Suggest usage of gst_buffer_new_and_alloc() # sub check_buffer_alloc() { my $n = 0; my $lineno = 1; foreach $line (@lines){ if($line =~ /gst_buffer_new/){ $n=5; } if($n>0 && $line =~ /malloc/){ print "W: ($lineno) gst_buffer_new() followed by malloc(), suggest gst_buffer_new_and_alloc()\n"; return; } $n--; $lineno++; } } sub check_bad_includes() { # # malloc.h is non-standard (and probably not what is indended) # if (grep { /^#include\s+/; } @lines) { print "E: bad header: malloc.h\n" } } sub check_begin_decls() { # # Prefer "G_BEGIN_DECLS" to 'extern "C" {' # if($filename =~ /\.h$/){ if (grep { /extern\s*\"C\"\s*/; } @lines) { print "W: extern \"C\" { should be changed to G_BEGIN_DECLS,G_END_DECLS\n"; }elsif (!grep { /G_BEGIN_DECLS/; } @lines) { print "E: header doesn't use G_BEGIN_DECLS\n"; } } } # # Prefer c89-style comments # sub check_c99_comments() { if (grep { /\/\//; } @lines) { print "W: //-style comments should be converted to /* */\n" } } # # DOS end-of-line characters are just wrong # sub check_carriage_returns() { if (grep { /\r/; } @lines) { print "E: source has carriage returns (DOS-style files)\n" } } # # Many uses of %lld are wrong. This could have a lot of false-positives # sub check_printf_lld() { if (grep { /\".*\%\d*ll[du].*\"/; } @lines) { print "W: Possible \%lld or \%llu in printf format\n" } } # # Glib functions are preferred # sub check_glibisms() { if (grep { /\bcalloc\s*\(/; } @lines) { print "E: use g_malloc0() instead of calloc()\n" } if (grep { /\bfree\s*\(/; } @lines) { print "E: use g_free() instead of free()\n" } if (grep { /\bmalloc\s*\(/; } @lines) { print "E: use g_malloc() instead of malloc()\n" } if (grep { /\bprintf\s*\(/; } @lines) { print "E: use g_print() instead of printf()\n" } if (grep { /\brealloc\s*\(/; } @lines) { print "E: use g_realloc() instead of realloc()\n" } if (grep { /^#include\s+/; } @lines) { print "E: ctype.h functions are not locale-independent and don't work in UTF-8 locales. Use g_ascii_is*()\n" } } # # I don't think that indentation necessarily needs to be fixed, since # it causes problems with patching and cvs annotate. # # This takes forever and isn't very useful # sub check_indentation() { my $changed_lines; my $percent; `indent -br -bad -cbi0 -cli2 -bls -l80 -ut -ce $filename -o .check_plugin.tmp`; $changed_lines = `diff $filename .check_plugin.tmp | grep '^>' | wc -l`; `rm -f .check_plugin.tmp`; $percent = int(100 * $changed_lines / $#lines); if($percent < 10){ print "I: indent changed $percent % of the lines\n"; }elsif($percent <20){ print "W: indent changed $percent % of the lines\n"; }else{ print "E: indent changed $percent % of the lines\n"; } } # # gst_props_set() returns a value that should never be ignored # may have false positives # sub check_gst_props_set() { if (grep { /^\s+gst_props_set\s*\(/; } @lines) { print "E: return value of gst_props_set() possibly ignored\n"; } } # # Check for some deprecated stuff (that _shouldn't_ be around anymore) # sub check_deprecated() { # # Check for old GST_DEBUG() usage # (none found) # if (grep { /GST_DEBUG\s*\(\s+\d/; } @lines) { print "E: old-style GST_DEBUG()\n"; } if (grep { /GST_INFO\s*\(\s+\d/; } @lines) { print "E: old-style GST_DEBUG()\n"; } if (grep { /GstEventFlags/; } @lines) { print "W: who uses GstEventFlags\n"; } if (grep { /g_type_class_ref/ } @lines) { print "W: g_type_class_ref should be changed to g_type_class_peek_parent\n"; } } # # Every .c file should include config.h before any other headers # No .h file should include config.h # sub check_config_h() { if($filename =~ /\.c$/){ # # config.h should be wrapped # my @includes = grep { /^#include/; } @lines; if (!grep { /^#include\s+["<]config.h[">]/; } @includes) { print "E: #include missing\n"; }else{ if (!($includes[0] =~ /^#include\s+["<]config.h[">]/)){ print "E: #include is not first include\n"; } if(!grep { /^#ifdef HAVE_CONFIG_H/; } @lines) { print "E: #include not surrounded by #ifdef HAVE_CONFIG_H\n"; } } } if($filename =~ /\.h$/){ if (grep { /^#include\s+["<]config.h[">]/; } @lines) { print "E: headers should not #include \n"; } } } # # Check for functions that take varargs to make sure they are # named correctly # sub check_varargs_functions() { if($filename =~ /\.h$/){ if (grep { /varargs/; } @lines) { print "I: has varargs\n"; } } } # # Debugging checks # sub check_debugging() { if (grep { /\Wg_print\W/ || /\Wprintf\W/ && /\Wfprintf\W/; } @lines) { print "W: friendly libraries don't print to stdio or stderr\n"; } if (grep { /GST_DEBUG.*\\n"/; } @lines) { print "W: possible newline in GST_DEBUG()\n"; } } # # check for plugindir= # sub m_check_plugindir() { if (grep { /plugindir\s*=/; } @lines) { print "E: plugindir= is no longer necessary\n"; } } # # check for old typefinding code # sub check_old_typefind() { if (grep { /GstTypeDefinition/ || /GstTypeFactory/ } @lines) { print "E: old typefind interface has been removed\n"; } } # # check for casts that we've deemed incorrect (fix the prototype) # sub check_bad_casts() { if (grep { /GBaseInitFunc/ || /GBaseFinalizeFunc/ || /GClassInitFunc/ || /GClassFinalizeFunc/ || /GInstanceInitFunc/ || /GInterfaceInitFunc/ || /GInterfaceFinalizeFunc/ } @lines) { print "W: bad casts (fix prototype)\n"; } if (grep { /\(\s*Gst[A-Z][A-Za-z]*\s*\*\s*\)/ } @lines ) { print "W: use GST_XXX() instead of (GstXxx *)\n"; } } # # check for old plugin code # sub check_old_plugin() { if (grep { /plugin_init.*GModule.*GstPlugin/ } @lines) { print "E: old plugin interface detected\n"; } if (grep { /GstPluginDesc.*plugin_desc/ } @lines) { print "W: should use GST_PLUGIN_DEFINE() instead of GstPluginDesc\n"; } } # # Check for calls to g_signal_new() with a callback type of G_TYPE_POINTER # sub check_signal_new() { my $n = 0; my $lineno = 1; foreach $line (@lines){ if($line =~ /g_signal_new/){ $n=5; } if($n>0 && $line =~ /G_TYPE_POINTER/){ print "W: ($lineno) g_signal_new() with callback type of G_TYPE_POINTER. Register and use a boxed type instead.\n"; return; } $n--; $lineno++; } } # # Check that libgstinterfaces is in LDADD # sub m_check_interfaces() { if (grep { /libgstinterfaces.la/ } @lines) { if (! grep { /libgstinterfaces_la/ } @lines) { if (! grep { /_LDADD.*libgstinterfaces.la/ } @lines) { print "E: libgstinterfaces.la not in LDADD\n"; } } } } # # Check that get_type() functions return G_CONST_RETURN GType # sub check_gnuc_const() { my $n = 0; my $lineno = 1; foreach $line (@lines){ if($line =~ /GType.*get_type.*/ && !($line =~ /GType.*get_type.*G_GNUC_CONST/)) { print "E: get_type function does not have G_GNUC_CONST attribute\n"; } } } # # Check caps usage # sub check_caps() { if (grep { /gst_pad_get_caps/ } @lines) { print "E: elements should not call gst_pad_get_caps(), use gst_pad_get_allowed_caps()\n"; } } # # Check for use of deprecated functions # sub check_lib_deprecated() { if (grep { /bzero/ } @lines) { print "E: change bzero() to memset()\n"; } } # # Check for typos # sub check_typo() { if (grep { /;\s*;\s*$/ } @lines) { print "W: typo? \";;\"\n"; } } # # set_explicit_caps() should preceed pad_add() # sub check_explicit_caps() { my $n = 0; my $lineno = 1; foreach $line (@lines){ if($line =~ /gst_element_add_pad/){ $n=10; } if($n>0 && $line =~ /gst_pad_set_explicit_caps/){ print "W: ($lineno) explicit caps should be set before adding pad\n"; return; } $n--; $lineno++; } }