From 74576db959cbb762780fdc3feadf6778a62c955b Mon Sep 17 00:00:00 2001 From: Duncan Roe Date: Sun, 17 Oct 2021 12:39:51 +1100 Subject: build: doc: `make` generates requested documentation Generate man pages, HTML, neither or both according to ./configure. Based on the work done for libnetfilter_queue. [ This patch updates the default ./configure option to build the manpages in case that doxygen is available. ] Signed-off-by: Duncan Roe Signed-off-by: Pablo Neira Ayuso --- .gitignore | 6 +- Makefile.am | 2 +- autogen.sh | 8 ++ configure.ac | 46 +++++++++- doxygen.cfg.in | 24 ------ doxygen/Makefile.am | 39 +++++++++ doxygen/build_man.sh | 227 +++++++++++++++++++++++++++++++++++++++++++++++++ doxygen/doxygen.cfg.in | 25 ++++++ 8 files changed, 349 insertions(+), 28 deletions(-) delete mode 100644 doxygen.cfg.in create mode 100644 doxygen/Makefile.am create mode 100755 doxygen/build_man.sh create mode 100644 doxygen/doxygen.cfg.in diff --git a/.gitignore b/.gitignore index ef6bb0f..2dc0dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ Makefile.in /configure /libtool -/doxygen/ -/doxygen.cfg +/doxygen/doxygen.cfg +/doxygen/doxyfile.stamp +/doxygen/man/ +/doxygen/html/ /*.pc diff --git a/Makefile.am b/Makefile.am index c7b86f7..46b14f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = include src utils +SUBDIRS = include src utils doxygen ACLOCAL_AMFLAGS = -I m4 diff --git a/autogen.sh b/autogen.sh index 5e1344a..93e2a23 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,4 +1,12 @@ #!/bin/sh -e +BUILD_MAN=doxygen/build_man.sh + +# Allow to override build_man.sh url for local testing +# E.g. export NFQ_URL=file:///usr/src/libnetfilter_queue +curl ${NFQ_URL:-https://git.netfilter.org/libnetfilter_queue/plain}/$BUILD_MAN\ + -o$BUILD_MAN +chmod a+x $BUILD_MAN + autoreconf -fi rm -Rf autom4te.cache diff --git a/configure.ac b/configure.ac index 85e49ed..589eb59 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,23 @@ m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AC_ARG_ENABLE([html-doc], + AS_HELP_STRING([--enable-html-doc], [Enable html documentation]), + [], [enable_html_doc=no]) +AM_CONDITIONAL([BUILD_HTML], [test "$enable_html_doc" = yes]) +AS_IF([test "$enable_html_doc" = yes], + [AC_SUBST(GEN_HTML, YES)], + [AC_SUBST(GEN_HTML, NO)]) + +AC_ARG_ENABLE([man-pages], + AS_HELP_STRING([--disable-man-pages], + [Disable man page documentation]), + [], [enable_man_pages=yes]) +AM_CONDITIONAL([BUILD_MAN], [test "$enable_man_pages" = yes]) +AS_IF([test "$enable_man_pages" = yes], + [AC_SUBST(GEN_MAN, YES)], + [AC_SUBST(GEN_MAN, NO)]) + AC_PROG_CC AM_PROG_CC_C_O LT_INIT([disable_static]) @@ -37,6 +54,27 @@ PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.2], [HAVE_LNFCT=1], [HAVE_LNFCT=0]) AM_CONDITIONAL([BUILD_NFCT], [test "$HAVE_LNFCT" -eq 1]) +AS_IF([test "$enable_man_pages" = no -a "$enable_html_doc" = no], + [with_doxygen=no], [with_doxygen=yes]) + +AS_IF([test "x$with_doxygen" != xno], [ + AC_CHECK_PROGS([DOXYGEN], [doxygen], [""]) + AC_CHECK_PROGS([DOT], [dot], [""]) + AS_IF([test "x$DOT" != "x"], + [AC_SUBST(HAVE_DOT, YES)], + [AC_SUBST(HAVE_DOT, NO)]) +]) + +AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) +AS_IF([test "x$DOXYGEN" = x], [ + AS_IF([test "x$with_doxygen" != xno], [ + dnl Only run doxygen Makefile if doxygen installed + AC_MSG_WARN([Doxygen not found - not building documentation]) + enable_html_doc=no + enable_man_pages=no + ]) +]) + dnl Output the makefile AC_CONFIG_FILES([Makefile src/Makefile @@ -45,5 +83,11 @@ AC_CONFIG_FILES([Makefile utils/Makefile libnetfilter_log.pc libnetfilter_log_libipulog.pc - doxygen.cfg]) + doxygen/Makefile + doxygen/doxygen.cfg]) AC_OUTPUT + +echo " +libnetfilter_log configuration: +man pages: ${enable_man_pages} +html docs: ${enable_html_doc}" diff --git a/doxygen.cfg.in b/doxygen.cfg.in deleted file mode 100644 index dc2fddb..0000000 --- a/doxygen.cfg.in +++ /dev/null @@ -1,24 +0,0 @@ -# Difference with default Doxyfile 1.8.20 -PROJECT_NAME = @PACKAGE@ -PROJECT_NUMBER = @VERSION@ -OUTPUT_DIRECTORY = doxygen -ABBREVIATE_BRIEF = -FULL_PATH_NAMES = NO -TAB_SIZE = 8 -OPTIMIZE_OUTPUT_FOR_C = YES -INPUT = . -FILE_PATTERNS = *.c -RECURSIVE = YES -EXCLUDE_SYMBOLS = nflog_g_handle \ - nflog_handle \ - ipulog_errmap_t \ - ipulog_handle -EXAMPLE_PATTERNS = -SOURCE_BROWSER = YES -ALPHABETICAL_INDEX = NO -GENERATE_LATEX = NO -LATEX_CMD_NAME = latex -GENERATE_MAN = YES -HAVE_DOT = YES -DOT_TRANSPARENT = YES -SEARCHENGINE = NO diff --git a/doxygen/Makefile.am b/doxygen/Makefile.am new file mode 100644 index 0000000..582db4e --- /dev/null +++ b/doxygen/Makefile.am @@ -0,0 +1,39 @@ +if HAVE_DOXYGEN + +doc_srcs = $(top_srcdir)/src/libnetfilter_log.c\ + $(top_srcdir)/src/nlmsg.c\ + $(top_srcdir)/src/libipulog_compat.c + +doxyfile.stamp: $(doc_srcs) Makefile + rm -rf html man + doxygen doxygen.cfg >/dev/null + +if BUILD_MAN + $(abs_top_srcdir)/doxygen/build_man.sh +endif + + touch doxyfile.stamp + +CLEANFILES = doxyfile.stamp + +all-local: doxyfile.stamp +clean-local: + rm -rf man html +install-data-local: +if BUILD_MAN + mkdir -p $(DESTDIR)$(mandir)/man3 + cp --no-dereference --preserve=links,mode,timestamps man/man3/*.3\ + $(DESTDIR)$(mandir)/man3/ +endif +if BUILD_HTML + mkdir -p $(DESTDIR)$(htmldir) + cp --no-dereference --preserve=links,mode,timestamps html/*\ + $(DESTDIR)$(htmldir) +endif + +# make distcheck needs uninstall-local +uninstall-local: + rm -rf $(DESTDIR)$(mandir) man html doxyfile.stamp $(DESTDIR)$(htmldir) +endif + +EXTRA_DIST = build_man.sh diff --git a/doxygen/build_man.sh b/doxygen/build_man.sh new file mode 100755 index 0000000..852c7b8 --- /dev/null +++ b/doxygen/build_man.sh @@ -0,0 +1,227 @@ +#!/bin/bash -p + +# Script to process man pages output by doxygen. +# We need to use bash for its associative array facility. +# (`bash -p` prevents import of functions from the environment). + +declare -A renamed_page + +main(){ + set -e + cd man/man3; rm -f _* + count_real_pages + rename_real_pages + make_symlinks + post_process +} + +count_real_pages(){ + page_count=0 + # + # Count "real" man pages (i.e. not generated by MAN_LINKS) + # MAN_LINKS pages are 1-liners starting .so + # Method: list files in descending order of size, + # looking for the first 1-liner + # + for i in $(ls -S) + do head -n1 $i | grep -E -q '^\.so' && break + page_count=$(($page_count + 1)) + done + first_link=$(($page_count + 1)) +} + +rename_real_pages(){ + for i in $(ls -S | head -n$page_count) + do for j in $(ls -S | tail -n+$first_link) + do grep -E -q $i$ $j && break + done + mv -f $i $j + renamed_page[$i]=$j + done +} + +make_symlinks(){ + for j in $(ls -S | tail -n+$first_link) + do ln -sf ${renamed_page[$(cat $j | cut -f2 -d/)]} $j + done +} + +post_process(){ + make_temp_files + # + # DIAGNOSTIC / DEVELOPMENT CODE + # set -x and restrict processing to keep_me: un-comment to activate + # Change keep_me as required + # + #keep_me=nfq_icmp_get_hdr.3;\ + #do_diagnostics;\ + # + # Work through the "real" man pages + for target in $(ls -S | head -n$page_count) + do mygrep "^\\.SH \"Function Documentation" $target + # Next file if this isn't a function page + [ $linnum -ne 0 ] || continue + + del_modules + del_bogus_synopsis + fix_name_line + move_synopsis + del_empty_det_desc + del_def_at_lines + fix_double_blanks + + # Fix rendering of verbatim "\n" (in code snippets) + sed -i 's/\\n/\\\\n/' $target + + done + + remove_temp_files +} + +fix_double_blanks(){ + linnum=1 + # + # Older versions of man display a blank line on encountering "\fB\fP"; + # newer versions of man do not. + # doxygen emits "\fB\fP" on seeing "\par" on a line by itself. + # "\par" gives us double-spacing in the web doc, which we want, but double- + # spacing looks odd in a man page so remove "\fB\fP". + # + while [ $linnum -ne 0 ] + do mygrep \\\\fB\\\\fP $target + [ $linnum -eq 0 ] || delete_lines $linnum $linnum + done +} + +del_def_at_lines(){ + linnum=1 + while [ $linnum -ne 0 ] + do mygrep "^Definition at line [[:digit:]]* of file" $target + [ $linnum -eq 0 ] || delete_lines $(($linnum - 1)) $linnum + done +} + +# Only invoked if you un-comment the 2 diagnostic / development lines above +do_diagnostics(){ + mv $keep_me xxx + rm *.3 + mv xxx $keep_me + page_count=1 + set -x +} + +del_empty_det_desc(){ + mygrep "^\\.SH \"Function Documentation" $target + i=$linnum + mygrep "^\\.SH \"Detailed Description" $target + [ $linnum -ne 0 ] || return 0 + [ $(($i - $linnum)) -eq 3 ] || return 0 + # A 1-line Detailed Description is also 3 lines long, + # but the 3rd line is not empty + i=$(($i -1)) + [ $(tail -n+$i $target | head -n1 | wc -c) -le 2 ] || return 0 + delete_lines $linnum $i +} + +move_synopsis(){ + mygrep "SH SYNOPSIS" $target + [ $linnum -ne 0 ] || return 0 + i=$linnum + # If this is a doxygen-created synopsis, leave it. + # (We haven't inserted our own one in the source yet) + mygrep "^\\.SS \"Functions" $target + [ $i -gt $linnum ] || return 0 + + mygrep "^\\.SH \"Function Documentation" $target + j=$(($linnum - 1)) + head -n$(($j - 1)) $target | tail -n$(($linnum - $i - 1)) >$fileC + delete_lines $i $j + mygrep "^\\.SS \"Functions" $target + head -n$(($linnum - 1)) $target >$fileA + tail -n+$(($linnum + 1)) $target >$fileB + cat $fileA $fileC $fileB >$target +} + +fix_name_line(){ + all_funcs="" + + # Search a shortened version of the page in case there are .RI lines later + mygrep "^\\.SH \"Function Documentation" $target + head -n$linnum $target >$fileC + + while : + do mygrep ^\\.RI $fileC + [ $linnum -ne 0 ] || break + # Discard this entry + tail -n+$(($linnum + 1)) $fileC >$fileB + cp $fileB $fileC + + func=$(cat $fileG | cut -f2 -d\\ | cut -c3-) + [ -z "$all_funcs" ] && all_funcs=$func ||\ + all_funcs="$all_funcs, $func" + done + # For now, assume name is at line 5 + head -n4 $target >$fileA + desc=$(head -n5 $target | tail -n1 | cut -f3- -d" ") + tail -n+6 $target >$fileB + cat $fileA >$target + echo "$all_funcs \\- $desc" >>$target + cat $fileB >>$target +} + +del_modules(){ + mygrep "^\.SS \"Modules" $target + [ $linnum -ne 0 ] || return 0 + i=$linnum + mygrep "^\\.SS \"Functions" $target + delete_lines $i $(($linnum - 1)) +} + +del_bogus_synopsis(){ + mygrep "SH SYNOPSIS" $target + # + # doxygen 1.8.20 inserts its own SYNOPSIS line but there is no mention + # in the documentation or git log what to do with it. + # So get rid of it + # + [ $linnum -ne 0 ] || return 0 + i=$linnum + # Look for the next one + tail -n+$(($i + 1)) $target >$fileC;\ + mygrep "SH SYNOPSIS" $fileC + [ $linnum -ne 0 ] || return 0 + + mygrep "^\\.SS \"Functions" $target + delete_lines $i $(($linnum - 1)) +} + +# Delete lines $1 through $2 from $target +delete_lines(){ + head -n$(($1 - 1)) $target >$fileA + tail -n+$(($2 +1)) $target >$fileB + cat $fileA $fileB >$target +} + +mygrep(){ + set +e + grep -En "$1" $2 2>/dev/null >$fileH + [ $? -ne 0 ] && linnum=0 ||\ + { head -n1 $fileH >$fileG; linnum=$(cat $fileG | cut -f1 -d:); } + set -e +} + +make_temp_files(){ + temps="A B C G H" + for i in $temps + do declare -g file$i=$(mktemp) + done +} + +remove_temp_files(){ + for i in $temps + do j=file$i + rm ${!j} + done +} + +main diff --git a/doxygen/doxygen.cfg.in b/doxygen/doxygen.cfg.in new file mode 100644 index 0000000..b6c27dc --- /dev/null +++ b/doxygen/doxygen.cfg.in @@ -0,0 +1,25 @@ +# Difference with default Doxyfile 1.8.20 +PROJECT_NAME = @PACKAGE@ +PROJECT_NUMBER = @VERSION@ +ABBREVIATE_BRIEF = +FULL_PATH_NAMES = NO +TAB_SIZE = 8 +OPTIMIZE_OUTPUT_FOR_C = YES +INPUT = @abs_top_srcdir@/src +FILE_PATTERNS = *.c +RECURSIVE = YES +EXCLUDE_SYMBOLS = nflog_g_handle \ + nflog_handle \ + ipulog_errmap_t \ + ipulog_handle +EXAMPLE_PATTERNS = +SOURCE_BROWSER = YES +ALPHABETICAL_INDEX = NO +GENERATE_LATEX = NO +LATEX_CMD_NAME = latex +GENERATE_MAN = @GEN_MAN@ +GENERATE_HTML = @GEN_HTML@ +MAN_LINKS = YES +HAVE_DOT = @HAVE_DOT@ +DOT_TRANSPARENT = YES +SEARCHENGINE = NO -- cgit v1.2.3