diff mbox series

[2/3] libselinux: generate Python glue code using "sed"

Message ID 20191101092724.7650-2-nicolas.iooss@m4x.org (mailing list archive)
State Rejected
Headers show
Series [1/3] Makefile: fix cleaning files that starts with "-" | expand

Commit Message

Nicolas Iooss Nov. 1, 2019, 9:27 a.m. UTC
libselinux currently uses "gcc -aux-info" in order to generate glue code
for its Python bindings that throws an exception when a function returns
a negative integer value. This causes issues when another compiler than
gcc is used (such as clang or icc), as option -aux-info is specific to
gcc.

Replace "gcc -aux-info" with a command that parses the content of header
files using "sed". As this is more fragile (because the declaration of
functions is not normalized), add a new target to the Makefile in order
to test that the new method does not produce different results with
"make CC=gcc test".

When reverting commit cfe487409307 ("libselinux: mark all exported
function "extern""), "make test" now fails as expected:

    bash -e exception.sh test
    Error ensuring that all exported functions that return an int are handled by exception.sh.
    Here are functions that were not found in "gcc -aux-info" but that were collected by "sed":
    Here are functions in "gcc -aux-info" that may be missing "extern" in header file:
    selinuxfs_exists
    make: *** [Makefile:202: test] Error 1

Original thread: https://lore.kernel.org/selinux/20191012172357.GB19655@imap.altlinux.org/T/#ma78bd7fe71fb5784387a8c0cebd867d6c02ee6e4

Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
Cc: Michael Shigorin <mike@altlinux.org>
---
 libselinux/Makefile         |  1 +
 libselinux/src/Makefile     |  5 ++++-
 libselinux/src/exception.sh | 38 +++++++++++++++++++++++++++++++------
 3 files changed, 37 insertions(+), 7 deletions(-)

Comments

Stephen Smalley Nov. 4, 2019, 3:18 p.m. UTC | #1
On 11/1/19 5:27 AM, Nicolas Iooss wrote:
> libselinux currently uses "gcc -aux-info" in order to generate glue code
> for its Python bindings that throws an exception when a function returns
> a negative integer value. This causes issues when another compiler than
> gcc is used (such as clang or icc), as option -aux-info is specific to
> gcc.
> 
> Replace "gcc -aux-info" with a command that parses the content of header
> files using "sed". As this is more fragile (because the declaration of
> functions is not normalized), add a new target to the Makefile in order
> to test that the new method does not produce different results with
> "make CC=gcc test".
> 
> When reverting commit cfe487409307 ("libselinux: mark all exported
> function "extern""), "make test" now fails as expected:
> 
>      bash -e exception.sh test
>      Error ensuring that all exported functions that return an int are handled by exception.sh.
>      Here are functions that were not found in "gcc -aux-info" but that were collected by "sed":
>      Here are functions in "gcc -aux-info" that may be missing "extern" in header file:
>      selinuxfs_exists
>      make: *** [Makefile:202: test] Error 1
> 
> Original thread: https://lore.kernel.org/selinux/20191012172357.GB19655@imap.altlinux.org/T/#ma78bd7fe71fb5784387a8c0cebd867d6c02ee6e4

I'm not excited about moving to a more fragile method of generating this 
glue code. Would it perhaps suffice for us to pre-generate the files and 
keep them in-tree (or package them as part of the tar file 
distributions) so that downstream users without gcc can just use the 
generated files?

> 
> Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
> Cc: Michael Shigorin <mike@altlinux.org>
> ---
>   libselinux/Makefile         |  1 +
>   libselinux/src/Makefile     |  5 ++++-
>   libselinux/src/exception.sh | 38 +++++++++++++++++++++++++++++++------
>   3 files changed, 37 insertions(+), 7 deletions(-)
> 
> diff --git a/libselinux/Makefile b/libselinux/Makefile
> index 16531fe95bf5..c0ae884f8ede 100644
> --- a/libselinux/Makefile
> +++ b/libselinux/Makefile
> @@ -67,3 +67,4 @@ clean-rubywrap:
>   	$(MAKE) -C src clean-rubywrap $@
>   
>   test:
> +	$(MAKE) -C src test
> diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile
> index 63d6b0eda270..c12230a17b1d 100644
> --- a/libselinux/src/Makefile
> +++ b/libselinux/src/Makefile
> @@ -198,7 +198,10 @@ clean: clean-pywrap clean-rubywrap
>   distclean: clean
>   	rm -f $(GENERATED) $(SWIGFILES)
>   
> +test:
> +	bash -e exception.sh test
> +
>   indent:
>   	../../scripts/Lindent $(filter-out $(GENERATED),$(wildcard *.[ch]))
>   
> -.PHONY: all clean clean-pywrap clean-rubywrap pywrap rubywrap swigify install install-pywrap install-rubywrap distclean
> +.PHONY: all clean clean-pywrap clean-rubywrap pywrap rubywrap swigify install install-pywrap install-rubywrap distclean test indent
> diff --git a/libselinux/src/exception.sh b/libselinux/src/exception.sh
> index d6c8c71713ad..adbb632c2f04 100755
> --- a/libselinux/src/exception.sh
> +++ b/libselinux/src/exception.sh
> @@ -1,11 +1,12 @@
> +#!/bin/bash -e
>   function except() {
> -case $1 in
> +case "$1" in
>       selinux_file_context_cmp) # ignore
>       ;;
>       *)
>   echo "
>   %exception $1 {
> -  \$action
> +  \$action
>     if (result < 0) {
>        PyErr_SetFromErrno(PyExc_OSError);
>        SWIG_fail;
> @@ -15,10 +16,35 @@ echo "
>   ;;
>   esac
>   }
> -if ! ${CC:-gcc} -x c -c -I../include - -aux-info temp.aux < ../include/selinux/selinux.h
> +
> +if [ $# -eq 1 ] && [ "$1" = "test" ]
>   then
> -    # clang does not support -aux-info so fall back to gcc
> -    gcc -x c -c -I../include - -aux-info temp.aux < ../include/selinux/selinux.h
> +    # Ensure that "gcc -aux-info" produces the same list of functions as the sed command.
> +    # The main difference between these way of producing the list of exported
> +    # functions is that "gcc -aux-info" automatically inserts "extern" to all
> +    # declarations and writes each one on a single line.
> +    # clang does not support -aux-info, so skip the test if generating the aux file failed.
> +    if ${CC:-gcc} -x c -c -I../include - -aux-info temp.aux < ../include/selinux/selinux.h
> +    then
> +        FCT_FROM_AUX="$(awk '/<stdin>.*extern int/ { print $6 }' temp.aux | sort -u)"
> +        FCT_FROM_SED="$(sed -n 's/^extern \+int \+\([0-9A-Za-z_]\+\) *(.*$/\1/p' < ../include/selinux/selinux.h | sort -u)"
> +        if [ "$FCT_FROM_AUX" != "$FCT_FROM_SED" ]
> +        then
> +            echo >&2 'Error ensuring that all exported functions that return an int are handled by exception.sh.'
> +            echo >&2 'Here are functions that were not found in "gcc -aux-info" but that were collected by "sed":'
> +            comm -13 <(echo "$FCT_FROM_AUX") <(echo "$FCT_FROM_SED")
> +            echo >&2 'Here are functions in "gcc -aux-info" that may be missing "extern" in header file:'
> +            comm -23 <(echo "$FCT_FROM_AUX") <(echo "$FCT_FROM_SED")
> +            exit 1
> +        fi
> +    fi
> +    rm -f -- temp.aux -.o
> +    exit
>   fi
> -for i in `awk '/<stdin>.*extern int/ { print $6 }' temp.aux`; do except $i ; done
> +
> +# shellcheck disable=SC2013
> +for i in $(sed -n 's/^extern \+int \+\([0-9A-Za-z_]\+\) *(.*$/\1/p' < ../include/selinux/selinux.h)
> +do
> +    except "$i"
> +done
>   rm -f -- temp.aux -.o
>
diff mbox series

Patch

diff --git a/libselinux/Makefile b/libselinux/Makefile
index 16531fe95bf5..c0ae884f8ede 100644
--- a/libselinux/Makefile
+++ b/libselinux/Makefile
@@ -67,3 +67,4 @@  clean-rubywrap:
 	$(MAKE) -C src clean-rubywrap $@
 
 test:
+	$(MAKE) -C src test
diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile
index 63d6b0eda270..c12230a17b1d 100644
--- a/libselinux/src/Makefile
+++ b/libselinux/src/Makefile
@@ -198,7 +198,10 @@  clean: clean-pywrap clean-rubywrap
 distclean: clean
 	rm -f $(GENERATED) $(SWIGFILES)
 
+test:
+	bash -e exception.sh test
+
 indent:
 	../../scripts/Lindent $(filter-out $(GENERATED),$(wildcard *.[ch]))
 
-.PHONY: all clean clean-pywrap clean-rubywrap pywrap rubywrap swigify install install-pywrap install-rubywrap distclean
+.PHONY: all clean clean-pywrap clean-rubywrap pywrap rubywrap swigify install install-pywrap install-rubywrap distclean test indent
diff --git a/libselinux/src/exception.sh b/libselinux/src/exception.sh
index d6c8c71713ad..adbb632c2f04 100755
--- a/libselinux/src/exception.sh
+++ b/libselinux/src/exception.sh
@@ -1,11 +1,12 @@ 
+#!/bin/bash -e
 function except() {
-case $1 in
+case "$1" in
     selinux_file_context_cmp) # ignore
     ;;
     *)
 echo "
 %exception $1 {
-  \$action 
+  \$action
   if (result < 0) {
      PyErr_SetFromErrno(PyExc_OSError);
      SWIG_fail;
@@ -15,10 +16,35 @@  echo "
 ;;
 esac
 }
-if ! ${CC:-gcc} -x c -c -I../include - -aux-info temp.aux < ../include/selinux/selinux.h
+
+if [ $# -eq 1 ] && [ "$1" = "test" ]
 then
-    # clang does not support -aux-info so fall back to gcc
-    gcc -x c -c -I../include - -aux-info temp.aux < ../include/selinux/selinux.h
+    # Ensure that "gcc -aux-info" produces the same list of functions as the sed command.
+    # The main difference between these way of producing the list of exported
+    # functions is that "gcc -aux-info" automatically inserts "extern" to all
+    # declarations and writes each one on a single line.
+    # clang does not support -aux-info, so skip the test if generating the aux file failed.
+    if ${CC:-gcc} -x c -c -I../include - -aux-info temp.aux < ../include/selinux/selinux.h
+    then
+        FCT_FROM_AUX="$(awk '/<stdin>.*extern int/ { print $6 }' temp.aux | sort -u)"
+        FCT_FROM_SED="$(sed -n 's/^extern \+int \+\([0-9A-Za-z_]\+\) *(.*$/\1/p' < ../include/selinux/selinux.h | sort -u)"
+        if [ "$FCT_FROM_AUX" != "$FCT_FROM_SED" ]
+        then
+            echo >&2 'Error ensuring that all exported functions that return an int are handled by exception.sh.'
+            echo >&2 'Here are functions that were not found in "gcc -aux-info" but that were collected by "sed":'
+            comm -13 <(echo "$FCT_FROM_AUX") <(echo "$FCT_FROM_SED")
+            echo >&2 'Here are functions in "gcc -aux-info" that may be missing "extern" in header file:'
+            comm -23 <(echo "$FCT_FROM_AUX") <(echo "$FCT_FROM_SED")
+            exit 1
+        fi
+    fi
+    rm -f -- temp.aux -.o
+    exit
 fi
-for i in `awk '/<stdin>.*extern int/ { print $6 }' temp.aux`; do except $i ; done 
+
+# shellcheck disable=SC2013
+for i in $(sed -n 's/^extern \+int \+\([0-9A-Za-z_]\+\) *(.*$/\1/p' < ../include/selinux/selinux.h)
+do
+    except "$i"
+done
 rm -f -- temp.aux -.o