File 0001-build-allow-building-with-installed-ovs.patch of Package ovn

From 3a8af96069a74308ced726513ee9ac16ef38c9d2 Mon Sep 17 00:00:00 2001
From: Ferdinand Thiessen <rpm@fthiessen.de>
Date: Sun, 27 Feb 2022 19:16:47 +0100
Subject: [PATCH 1/4] build: allow building with installed ovs

---
 Makefile.am                 |  17 +-
 acinclude.m4                |  92 ++++++--
 automake.mk                 |  61 +++---
 build-aux/build/__init__.py |   0
 build-aux/build/nroff.py    | 404 ++++++++++++++++++++++++++++++++++++
 build-aux/build/soutil.py   |  56 +++++
 build-aux/dot2pic           | 117 +++++++++++
 configure.ac                |   9 +-
 controller-vtep/automake.mk |   2 +-
 controller/automake.mk      |   2 +-
 ic/automake.mk              |   4 +-
 lib/automake.mk             |   1 -
 lib/ovsdb_automake.mk       |   1 -
 northd/automake.mk          |  14 +-
 tests/automake.mk           |   9 +-
 utilities/automake.mk       |  12 +-
 16 files changed, 717 insertions(+), 84 deletions(-)
 create mode 100644 build-aux/build/__init__.py
 create mode 100644 build-aux/build/nroff.py
 create mode 100755 build-aux/build/soutil.py
 create mode 100755 build-aux/dot2pic

diff --git a/Makefile.am b/Makefile.am
index 0169c96ef..135c7ecb6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,6 @@
 
 AUTOMAKE_OPTIONS = foreign subdir-objects
 ACLOCAL_AMFLAGS = -I m4
-#SUBDIRS = ovs
 
 OVS_SRCDIR=@ovs_srcdir@
 OVS_BUILDDIR=@ovs_builddir@
@@ -35,12 +34,7 @@ AM_CPPFLAGS += -I $(top_builddir)/include
 AM_CPPFLAGS += -I $(top_srcdir)/lib
 AM_CPPFLAGS += -I $(top_builddir)/lib
 
-AM_CPPFLAGS += -I $(OVS_SRCDIR)/include
-AM_CPPFLAGS += -I $(OVS_BUILDDIR)/include
-AM_CPPFLAGS += -I $(OVS_SRCDIR)/lib
-AM_CPPFLAGS += -I $(OVS_BUILDDIR)/lib
-AM_CPPFLAGS += -I $(OVS_SRCDIR)
-AM_CPPFLAGS += -I $(OVS_BUILDDIR)
+AM_CPPFLAGS += $(OVS_CFLAGS)
 
 AM_CPPFLAGS += $(SSL_INCLUDES)
 
@@ -62,13 +56,14 @@ psep=";"
 else
 psep=":"
 endif
+
 # PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo
 # files.  Creating .py[co] works OK for any given version of Open
 # vSwitch, but it causes trouble if you switch from a version with
 # foo/__init__.py into an (older) version with plain foo.py, since
 # foo/__init__.pyc will cause Python to ignore foo.py.
 run_python = \
-	PYTHONPATH=$(OVS_SRCDIR)/python$(psep)$$PYTHONPATH \
+	PYTHONPATH=$$PYTHONPATH$(pysep)$(srcdir)/build-aux \
 	PYTHONDONTWRITEBYTECODE=yes $(PYTHON3)
 
 ALL_LOCAL =
@@ -165,7 +160,7 @@ submodules = $(shell grep 'path =' $(srcdir)/.gitmodules | sed -E 's/[\t ]*path
 
 SUFFIXES += .in
 .in:
-	$(AM_V_GEN)PYTHONPATH=$(OVS_SRCDIR)/python$(psep)$$PYTHONPATH$(psep)$(srcdir)/python $(PYTHON3) $(srcdir)/build-aux/soexpand.py -I$(srcdir) -I$(OVS_SRCDIR) < $< | \
+	$(AM_V_GEN)PYTHONPATH=$$PYTHONPATH$(psep)$(srcdir)/python $(PYTHON3) $(srcdir)/build-aux/soexpand.py -I$(srcdir) -I$(OVS_INCLUDEDIR)/openvswitch -I$(OVS_INCLUDEDIR) < $< | \
 	  $(PYTHON3) $(srcdir)/build-aux/dpdkstrip.py $(DPDKSTRIP_FLAGS) | \
 	  sed \
 	    -e 's,[@]PKIDIR[@],$(PKIDIR),g' \
@@ -424,8 +419,8 @@ endif
 CLEANFILES += flake8-check
 
 include $(srcdir)/manpages.mk
-$(srcdir)/manpages.mk: $(MAN_ROOTS) build-aux/sodepends.py $(OVS_SRCDIR)/python/build/soutil.py
-	@PYTHONPATH=$(OVS_SRCDIR)/python$(psep)$$PYTHONPATH$(psep)$(srcdir)/python $(PYTHON3) $(srcdir)/build-aux/sodepends.py -I. -I$(srcdir) -I$(OVS_MANDIR) $(MAN_ROOTS) >$(@F).tmp
+$(srcdir)/manpages.mk: $(MAN_ROOTS) build-aux/sodepends.py $(srcdir)/build-aux/build/soutil.py
+	@PYTHONPATH=$$PYTHONPATH$(psep)$(srcdir)/python $(PYTHON3) $(srcdir)/build-aux/sodepends.py -I. -I$(srcdir) -I$(OVS_MANDIR) $(MAN_ROOTS) >$(@F).tmp
 	@if cmp -s $(@F).tmp $@; then \
 	  touch $@; \
 	  rm -f $(@F).tmp; \
diff --git a/acinclude.m4 b/acinclude.m4
index e7f829520..22c08909f 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -317,7 +317,7 @@ AC_DEFUN([OVS_ENABLE_SPARSE],
    : ${SPARSE=sparse}
    AC_SUBST([SPARSE])
    AC_CONFIG_COMMANDS_PRE(
-     [CC='$(if $(C:0=),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) $(SPARSE_WERROR) -I $(ovs_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')'])
+     [CC='$(if $(C:0=),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) $(SPARSE_WERROR) -I $(ovs_srcdir)/include/sparse -I $(OVS_INCLUDEDIR)/openvswitch/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')'])
 
    AC_ARG_ENABLE(
      [sparse],
@@ -331,7 +331,7 @@ dnl ctags ignores symbols with extras identifiers. This builds a list of
 dnl specially handled identifiers to be ignored.
 AC_DEFUN([OVS_CTAGS_IDENTIFIERS],
     AC_SUBST([OVS_CTAGS_IDENTIFIERS_LIST],
-           [`printf %s '-I "'; sed -n 's/^#define \(OVS_[A-Z_]\+\)(\.\.\.)$/\1+/p' ${OVSDIR}/include/openvswitch/compiler.h  | tr \\\n ' ' ; printf '"'`] ))
+           [`printf %s '-I "'; sed -n 's/^#define \(OVS_[A-Z_]\+\)(\.\.\.)$/\1+/p' ${OVS_INCLUDEDIR}/openvswitch/compiler.h  | tr \\\n ' ' ; printf '"'`] ))
 
 dnl OVS_PTHREAD_SET_NAME
 dnl
@@ -406,20 +406,29 @@ AC_DEFUN([OVN_CHECK_OVS], [
               [AC_HELP_STRING([--with-ovs-build=/path/to/ovs/build/dir],
                               [Specify the OVS build directory])])
 
-  AC_MSG_CHECKING([for OVS source directory])
-  if test X"$with_ovs_source" = X; then
-    with_ovs_source="$srcdir/ovs"
-  fi
-  OVSDIR=$(cd "$(eval echo "$with_ovs_source")"; pwd)
-  if test ! -f "$OVSDIR/vswitchd/bridge.c"; then
-    AC_ERROR([$OVSDIR is not an OVS source directory])
+  PKG_CHECK_MODULES([OVSlib], [libopenvswitch >= 2.7], HAVE_OVSlib=yes, HAVE_OVSlib=no)
+  PKG_CHECK_MODULES([OVSDB], [libovsdb >= 2.7], HAVE_OVSDB=yes, HAVE_OVSDB=no)
+  if test "x$HAVE_OVSlib" = "xyes" && test "x$HAVE_OVSDB" = "xyes"; then
+    system_provided=yes
   fi
 
-  AC_MSG_RESULT([$OVSDIR])
-  AC_SUBST(OVSDIR)
+  if test X"$with_ovs_source" != X || test X"$with_ovs_build" != X || test X"$system_provided" != Xyes; then
+    # last test for backwards compatibility
+
+    # Source directory
+    AC_MSG_CHECKING([for OVS source directory])
+    if test X"$with_ovs_source" = X; then
+      with_ovs_source="$srcdir/ovs"
+    fi
+    OVSDIR=$(cd "$(eval echo "$with_ovs_source")"; pwd)
+    if test ! -f "$OVSDIR/vswitchd/bridge.c"; then
+      AC_ERROR([$OVSDIR is not an OVS source directory])
+    fi
+    OVS_INCLUDEDIR="$OVSDIR/include"
+    AC_MSG_RESULT([$OVSDIR])
 
-  AC_MSG_CHECKING([for OVS build directory])
-  if test X"$with_ovs_build" != X; then
+    # Build directory
+    AC_MSG_CHECKING([for OVS build directory])
     OVSBUILDDIR=`eval echo "$with_ovs_build"`
     case $OVSBUILDDIR in
       /*) ;;
@@ -428,16 +437,61 @@ AC_DEFUN([OVN_CHECK_OVS], [
     if test ! -f "$OVSBUILDDIR/config.h"; then
       AC_ERROR([$OVSBUILDDIR is not a configured OVS build directory])
     fi
-  elif test -f "$OVSDIR/config.h"; then
-    # If separate build dir is not specified, use src dir.
-    OVSBUILDDIR=$OVSDIR
+    AC_MSG_RESULT([$OVSBUILDDIR])
+    OVSVERSION=`sed -n 's/^#define PACKAGE_VERSION//p' $OVSBUILDDIR/config.h | tr \\\n ' ' | sed 's/^[ \t]*//;s/[ \t]*$//' | sed 's/\"//g'`
+    OVSDB_LDFLAGS="$OVSBUILDDIR/lib/libovsdb.la"
+    system_ovs=no
+    AC_SUBST([ovs_srcdir], ['${OVSDIR}'])
+    AC_SUBST([ovs_builddir], ['${OVSBUILDDIR}'])
+  elif test X"$system_provided" = Xyes; then
+    OVSVERSION=`pkg-config --modversion libopenvswitch`
+    OVS_INCLUDEDIR=`pkg-config --variable=includedir libopenvswitch`
+    OVSBUILDDIR="$prefix/share/openvswitch/"
+    OVS_CFLAGS="$OVS_CFLAGS $OVSlib_CFLAGS $OVSDB_CFLAGS"
+    OVS_LDFLAGS="$OVSlib_LIBS -lvtep"
+    OVSDB_LDFLAGS=$OVSDB_LIBS
+    system_ovs=yes
   else
-    AC_ERROR([OVS source dir $OVSDIR is not configured as a build directory (either run configure there or use --with-ovs-build to point to the build directory)])
+    AC_ERROR([Neither OVS source dir configured nor system provided OVS found])
   fi
-  AC_MSG_RESULT([$OVSBUILDDIR])
+  AC_SUBST(OVSDB_LDFLAGS)
   AC_SUBST(OVSBUILDDIR)
-  OVSVERSION=`sed -n 's/^#define PACKAGE_VERSION//p' $OVSBUILDDIR/config.h | tr \\\n ' ' | sed 's/^[ \t]*//;s/[ \t]*$//' | sed 's/\"//g'`
   AC_SUBST(OVSVERSION)
+  AC_SUBST(OVS_INCLUDEDIR)
+  AM_CONDITIONAL([SYSTEM_OVS], [test X$system_ovs = Xyes])
   AC_MSG_CHECKING([OVS version])
   AC_MSG_RESULT([$OVSVERSION])
 ])
+
+dnl OVN_CHECK_OVSDB_TOOLS
+dnl
+dnl Check for OVSDB tools to be present
+AC_DEFUN([OVN_CHECK_OVSDB_TOOLS], [
+  AC_MSG_NOTICE([Checking for OVSDB development tools])
+
+  AC_PATH_PROG(OVSDB_IDLC, [ovsdb-idlc], notfound)
+  if test x"$OVSDB_IDLC" = x"notfound"; then
+    OVSDB_IDLC="$$run_python \"$OVSDIR/ovsdb/ovsdb-idlc.in\""
+    AC_MSG_RESULT([from ovs sources])
+  fi
+  AC_SUBST(OVSDB_IDLC)
+
+  AC_MSG_NOTICE([Checking for OVSDB dot])
+  OVSDB_DOT="$datadir/openvswitch/ovsdb/ovsdb-dot"
+  AC_CHECK_FILE([$OVSDB_DOT], [], [
+    OVSDB_DOT="$OVSDIR/ovsdb/ovsdb-dot.in"
+    AC_MSG_RESULT([from ovs sources])
+  ])
+  AC_SUBST(OVSDB_DOT)
+
+  AC_MSG_NOTICE([Checking for OVSDB doc program])
+  OVSDB_DOC="$datadir/openvswitch/ovsdb/ovsdb-doc"
+  AC_CHECK_FILE([$OVSDB_DOC], [], [
+    OVSDB_DOC=$OVSDIR/ovsdb/ovsdb-doc
+    AC_MSG_RESULT([from ovs sources])
+  ])
+  AC_SUBST(OVSDB_DOC)
+
+])
+
+
diff --git a/automake.mk b/automake.mk
index a7947a3f5..5a7cceeea 100644
--- a/automake.mk
+++ b/automake.mk
@@ -2,16 +2,26 @@ man_MANS += ovn-architecture.7
 EXTRA_DIST += ovn-architecture.7.xml
 CLEANFILES += ovn-architecture.7
 
+if ! SYSTEM_OVS
+    OVS_CFLAGS += -I $OVS_SRCDIR/include
+    OVS_CFLAGS += -I $OVS_BUILDDIR/include
+    OVS_CFLAGS += -I $OVS_SRCDIR/lib
+    OVS_CFLAGS += -I $OVS_BUILDDIR/lib
+    OVS_CFLAGS += -I $OVS_SRCDIR
+    OVS_CFLAGS += -I $OVS_BUILDDIR
+    OVS_LDFLAGS += -L $OVS_LIBDIR/.libs
+    OVS_LDFLAGS += $OVS_LIBDIR/libopenvswitch.la
+endif
+
 # OVN northbound E-R diagram
 #
 # If "python" or "dot" is not available, then we do not add graphical diagram
 # to the documentation.
 if HAVE_DOT
-OVSDB_DOT = $(run_python) ${OVSDIR}/ovsdb/ovsdb-dot.in
-ovn-nb.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-nb.ovsschema
-	$(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-nb.ovsschema > $@
-ovn-nb.pic: ovn-nb.gv ${OVSDIR}/ovsdb/dot2pic
-	$(AM_V_GEN)(dot -T plain < ovn-nb.gv | $(PYTHON3) ${OVSDIR}/ovsdb/dot2pic -f 3) > $@.tmp && \
+ovn-nb.gv: $(srcdir)/ovn-nb.ovsschema
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOT) --no-arrows $(srcdir)/ovn-nb.ovsschema > $@
+ovn-nb.pic: ovn-nb.gv ${srcdir}/build-aux/dot2pic
+	$(AM_V_GEN)(dot -T plain < ovn-nb.gv | $(PYTHON3) ${srcdir}/build-aux/dot2pic -f 3) > $@.tmp && \
 	mv $@.tmp $@
 OVN_NB_PIC = ovn-nb.pic
 OVN_NB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_NB_PIC)
@@ -23,10 +33,9 @@ EXTRA_DIST += ovn-nb.xml
 CLEANFILES += ovn-nb.5
 man_MANS += ovn-nb.5
 
-OVSDB_DOC = $(run_python) ${OVSDIR}/ovsdb/ovsdb-doc
 ovn-nb.5: \
-	${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-nb.xml $(srcdir)/ovn-nb.ovsschema $(OVN_NB_PIC)
-	$(AM_V_GEN)$(OVSDB_DOC) \
+	$(srcdir)/ovn-nb.xml $(srcdir)/ovn-nb.ovsschema $(OVN_NB_PIC)
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOC) \
 		$(OVN_NB_DOT_DIAGRAM_ARG) \
 		--version=$(VERSION) \
 		$(srcdir)/ovn-nb.ovsschema \
@@ -38,10 +47,10 @@ ovn-nb.5: \
 # If "python" or "dot" is not available, then we do not add graphical diagram
 # to the documentation.
 if HAVE_DOT
-ovn-sb.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-sb.ovsschema
-	$(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-sb.ovsschema > $@
-ovn-sb.pic: ovn-sb.gv ${OVSDIR}/ovsdb/dot2pic
-	$(AM_V_GEN)(dot -T plain < ovn-sb.gv | $(PYTHON3) ${OVSDIR}/ovsdb/dot2pic -f 3) > $@.tmp && \
+ovn-sb.gv: $(srcdir)/ovn-sb.ovsschema
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOT) --no-arrows $(srcdir)/ovn-sb.ovsschema > $@
+ovn-sb.pic: ovn-sb.gv ${srcdir}/build-aux/dot2pic
+	$(AM_V_GEN)(dot -T plain < ovn-sb.gv | $(PYTHON3) ${srcdir}/build-aux/dot2pic -f 3) > $@.tmp && \
 	mv $@.tmp $@
 OVN_SB_PIC = ovn-sb.pic
 OVN_SB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_SB_PIC)
@@ -54,8 +63,8 @@ CLEANFILES += ovn-sb.5
 man_MANS += ovn-sb.5
 
 ovn-sb.5: \
-	${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-sb.xml $(srcdir)/ovn-sb.ovsschema $(OVN_SB_PIC)
-	$(AM_V_GEN)$(OVSDB_DOC) \
+	$(srcdir)/ovn-sb.xml $(srcdir)/ovn-sb.ovsschema $(OVN_SB_PIC)
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOC) \
 		$(OVN_SB_DOT_DIAGRAM_ARG) \
 		--version=$(VERSION) \
 		$(srcdir)/ovn-sb.ovsschema \
@@ -67,10 +76,10 @@ ovn-sb.5: \
 # If "python" or "dot" is not available, then we do not add graphical diagram
 # to the documentation.
 if HAVE_DOT
-ovn-ic-nb.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-ic-nb.ovsschema
-	$(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-ic-nb.ovsschema > $@
-ovn-ic-nb.pic: ovn-ic-nb.gv ${OVSDIR}/ovsdb/dot2pic
-	$(AM_V_GEN)(dot -T plain < ovn-ic-nb.gv | $(PYTHON3) ${OVSDIR}/ovsdb/dot2pic -f 3) > $@.tmp && \
+ovn-ic-nb.gv: $(srcdir)/ovn-ic-nb.ovsschema
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOT) --no-arrows $(srcdir)/ovn-ic-nb.ovsschema > $@
+ovn-ic-nb.pic: ovn-ic-nb.gv ${srcdir}/build-aux/dot2pic
+	$(AM_V_GEN)(dot -T plain < ovn-ic-nb.gv | $(PYTHON3) ${srcdir}/build-aux/dot2pic -f 3) > $@.tmp && \
 	mv $@.tmp $@
 OVN_IC_NB_PIC = ovn-ic-nb.pic
 OVN_IC_NB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_IC_NB_PIC)
@@ -83,8 +92,8 @@ CLEANFILES += ovn-ic-nb.5
 man_MANS += ovn-ic-nb.5
 
 ovn-ic-nb.5: \
-	${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-ic-nb.xml $(srcdir)/ovn-ic-nb.ovsschema $(OVN_IC_NB_PIC)
-	$(AM_V_GEN)$(OVSDB_DOC) \
+	$(srcdir)/ovn-ic-nb.xml $(srcdir)/ovn-ic-nb.ovsschema $(OVN_IC_NB_PIC)
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOC) \
 		$(OVN_IC_NB_DOT_DIAGRAM_ARG) \
 		--version=$(VERSION) \
 		$(srcdir)/ovn-ic-nb.ovsschema \
@@ -96,10 +105,10 @@ ovn-ic-nb.5: \
 # If "python" or "dot" is not available, then we do not add graphical diagram
 # to the documentation.
 if HAVE_DOT
-ovn-ic-sb.gv: ${OVSDIR}/ovsdb/ovsdb-dot.in $(srcdir)/ovn-ic-sb.ovsschema
-	$(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn-ic-sb.ovsschema > $@
-ovn-ic-sb.pic: ovn-ic-sb.gv ${OVSDIR}/ovsdb/dot2pic
-	$(AM_V_GEN)(dot -T plain < ovn-ic-sb.gv | $(PYTHON3) ${OVSDIR}/ovsdb/dot2pic -f 3) > $@.tmp && \
+ovn-ic-sb.gv: $(srcdir)/ovn-ic-sb.ovsschema
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOT) --no-arrows $(srcdir)/ovn-ic-sb.ovsschema > $@
+ovn-ic-sb.pic: ovn-ic-sb.gv ${srcdir}/build-aux/dot2pic
+	$(AM_V_GEN)(dot -T plain < ovn-ic-sb.gv | $(PYTHON3) ${srcdir}/build-aux/dot2pic -f 3) > $@.tmp && \
 	mv $@.tmp $@
 OVN_IC_SB_PIC = ovn-ic-sb.pic
 OVN_IC_SB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_IC_SB_PIC)
@@ -112,8 +121,8 @@ CLEANFILES += ovn-ic-sb.5
 man_MANS += ovn-ic-sb.5
 
 ovn-ic-sb.5: \
-	${OVSDIR}/ovsdb/ovsdb-doc $(srcdir)/ovn-ic-sb.xml $(srcdir)/ovn-ic-sb.ovsschema $(OVN_IC_SB_PIC)
-	$(AM_V_GEN)$(OVSDB_DOC) \
+	$(srcdir)/ovn-ic-sb.xml $(srcdir)/ovn-ic-sb.ovsschema $(OVN_IC_SB_PIC)
+	$(AM_V_GEN)$(run_python) $(OVSDB_DOC) \
 		$(OVN_IC_SB_DOT_DIAGRAM_ARG) \
 		--version=$(VERSION) \
 		$(srcdir)/ovn-ic-sb.ovsschema \
diff --git a/build-aux/build/__init__.py b/build-aux/build/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/build-aux/build/nroff.py b/build-aux/build/nroff.py
new file mode 100644
index 000000000..0a18227e2
--- /dev/null
+++ b/build-aux/build/nroff.py
@@ -0,0 +1,404 @@
+# Copyright (c) 2010, 2011, 2012, 2015, 2016, 2017 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+import sys
+
+from ovs.db import error
+
+
+def text_to_nroff(s, font=r'\fR', escape_dot=True):
+    def escape(match):
+        c = match.group(0)
+
+        # In Roman type, let -- in XML be \- in nroff.  That gives us a way to
+        # write minus signs, which is important in some places in manpages.
+        #
+        # Bold in nroff usually represents literal text, where there's no
+        # distinction between hyphens and minus sign.  The convention in nroff
+        # appears to be to use a minus sign in such cases, so we follow that
+        # convention.
+        #
+        # Finally, we always output - as a minus sign when it is followed by a
+        # digit.
+        if c.startswith('-'):
+            if c == '--' and font == r'\fR':
+                return r'\-'
+            if c != '-' or font in (r'\fB', r'\fL'):
+                return c.replace('-', r'\-')
+            else:
+                return '-'
+
+        if c == '\\':
+            return r'\e'
+        elif c == '"':
+            return r'\(dq'
+        elif c == "'":
+            return r'\(cq'
+        elif c == ".":
+            if escape_dot:
+                # groff(7) says that . can be escaped by \. but in practice
+                # groff still gives an error with \. at the beginning of a
+                # line.
+                return r'\[char46]'
+            else:
+                return '.'
+        else:
+            raise error.Error("bad escape")
+
+    # Escape - \ " ' . as needed by nroff.
+    s = re.sub('(-[0-9]|--|[-"\'\\\\.])', escape, s)
+    return s
+
+
+def escape_nroff_literal(s, font=r'\fB'):
+    return font + r'%s\fR' % text_to_nroff(s, font)
+
+
+def inline_xml_to_nroff(node, font, to_upper=False, newline='\n'):
+    if node.nodeType == node.TEXT_NODE:
+        if to_upper:
+            s = text_to_nroff(node.data.upper(), font)
+        else:
+            s = text_to_nroff(node.data, font)
+        return s.replace('\n', newline)
+    elif node.nodeType == node.ELEMENT_NODE:
+        if node.tagName in ['code', 'em', 'option', 'env', 'b']:
+            s = r'\fB'
+            for child in node.childNodes:
+                s += inline_xml_to_nroff(child, r'\fB', to_upper, newline)
+            return s + font
+        elif node.tagName == 'ref':
+            if node.hasAttribute('column'):
+                s = node.attributes['column'].nodeValue
+                if node.hasAttribute('key'):
+                    s += ':' + node.attributes['key'].nodeValue
+            elif node.hasAttribute('table'):
+                s = node.attributes['table'].nodeValue
+            elif node.hasAttribute('group'):
+                s = node.attributes['group'].nodeValue
+            elif node.hasAttribute('db'):
+                s = node.attributes['db'].nodeValue
+            elif node.hasAttribute('field'):
+                s = node.attributes['field'].nodeValue
+            elif node.hasAttribute('section'):
+                s = node.attributes['section'].nodeValue
+            else:
+                raise error.Error("'ref' lacks required attributes: %s"
+                                  % list(node.attributes.keys()))
+            return r'\fB' + re.sub(r'\s+', ' ', s) + font
+        elif node.tagName in ['var', 'dfn', 'i', 'cite']:
+            s = r'\fI'
+            for child in node.childNodes:
+                s += inline_xml_to_nroff(child, r'\fI', to_upper, newline)
+            return s + font
+        elif node.tagName in ['literal']:
+            s = r'\fL'
+            for child in node.childNodes:
+                s += inline_xml_to_nroff(child, r'\fL')
+            return s + font
+        elif node.tagName == 'url':
+            return ('\n.URL "'
+                    + text_to_nroff(node.attributes['href'].nodeValue,
+                                    escape_dot=False)
+                    + '"\n')
+        else:
+            raise error.Error("element <%s> unknown or invalid here"
+                              % node.tagName)
+    elif node.nodeType == node.COMMENT_NODE:
+        return ''
+    else:
+        raise error.Error("unknown node %s in inline xml" % node)
+
+
+def pre_to_nroff(nodes, para, font):
+    # This puts 'font' at the beginning of each line so that leading and
+    # trailing whitespace stripping later doesn't removed leading spaces
+    # from preformatted text.
+    s = para + '\n.nf\n' + font
+    for node in nodes:
+        s += inline_xml_to_nroff(node, font, False, '\n.br\n' + font) + '\\fR'
+    s += '\n.fi\n'
+    return s
+
+
+def tbl_to_nroff(nodes, para):
+    s = para + '\n.TS\n'
+    for node in nodes:
+        if node.nodeType != node.TEXT_NODE:
+            fatal("<tbl> element may only have text children")
+        s += node.data + '\n'
+    s += '.TE\n'
+    return s
+
+
+def fatal(msg):
+    sys.stderr.write('%s\n' % msg)
+    sys.exit(1)
+
+
+def put_text(text, x, y, s):
+    x = int(x)
+    y = int(y)
+    extend = x + len(s) - len(text[y])
+    if extend > 0:
+        text[y] += ' ' * extend
+    text[y] = text[y][:x] + s + text[y][x + len(s):]
+
+
+def put_centered(text, x, width, y, s):
+    put_text(text, x + (width - len(s)) / 2, y, s)
+
+
+def diagram_header_to_nroff(header_node, text, x):
+    # Parse header.
+    header_fields = []
+    i = 0
+    for node in header_node.childNodes:
+        if node.nodeType == node.ELEMENT_NODE and node.tagName == 'bits':
+            name = node.attributes['name'].nodeValue
+            width = node.attributes['width'].nodeValue
+            above = node.getAttribute('above')
+            below = node.getAttribute('below')
+            fill = node.getAttribute('fill')
+            header_fields += [{"name": name,
+                              "tag": "B%d" % i,
+                              "width": width,
+                              "above": above,
+                              "below": below,
+                              "fill": fill}]
+            i += 1
+        elif node.nodeType == node.COMMENT_NODE:
+            pass
+        elif node.nodeType == node.TEXT_NODE and node.data.isspace():
+            pass
+        else:
+            fatal("unknown node %s in diagram <header> element" % node)
+
+    # Format pic version.
+    pic_s = ""
+    for f in header_fields:
+        name = f['name'].replace('...', '. . .')
+        pic_s += "  %s: box \"%s\" width %s" % (f['tag'], name, f['width'])
+        if f['fill'] == 'yes':
+            pic_s += " fill"
+        pic_s += '\n'
+    for f in header_fields:
+        pic_s += "  \"%s\" at %s.n above\n" % (f['above'], f['tag'])
+        pic_s += "  \"%s\" at %s.s below\n" % (f['below'], f['tag'])
+    name = header_node.getAttribute('name')
+    if name == "":
+        visible = " invis"
+    else:
+        visible = ""
+    pic_s += "line <->%s \"%s\" above " % (visible, name)
+    pic_s += "from %s.nw + (0,textht) " % header_fields[0]['tag']
+    pic_s += "to %s.ne + (0,textht)\n" % header_fields[-1]['tag']
+
+    # Format text version.
+    header_width = 1
+    for f in header_fields:
+        field_width = max(len(f['above']), len(f['below']), len(f['name']))
+        f['width'] = field_width
+        header_width += field_width + 1
+    min_header_width = 2 + len(name)
+    while min_header_width > header_width:
+        for f in header_fields:
+            f['width'] += 1
+            header_width += 1
+            if header_width >= min_header_width:
+                break
+
+    if name != "":
+        put_centered(text, x, header_width, 0, name)
+        if header_width >= 4:
+            arrow = '<' + '-' * (header_width - 4) + '>'
+            put_text(text, x + 1, 1, arrow)
+    for f in header_fields:
+        box1 = '+' + '-' * f['width'] + '+'
+        box2 = '|' + ' ' * f['width'] + '|'
+        put_text(text, x, 3, box1)
+        put_text(text, x, 4, box2)
+        put_text(text, x, 5, box1)
+
+        put_centered(text, x + 1, f['width'], 2, f['above'])
+        put_centered(text, x + 1, f['width'], 4, f['name'])
+        put_centered(text, x + 1, f['width'], 6, f['below'])
+
+        x += f['width'] + 1
+
+    return pic_s, x + 1
+
+
+def diagram_to_nroff(nodes, para):
+    pic_s = ''
+    text = [''] * 7
+    x = 0
+    move = False
+    for node in nodes:
+        if node.nodeType == node.ELEMENT_NODE and node.tagName == 'header':
+            if move:
+                pic_s += "move .1\n"
+                x += 1
+            elif x > 0:
+                x -= 1
+            pic_header, x = diagram_header_to_nroff(node, text, x)
+            pic_s += "[\n" + pic_header + "]\n"
+            move = True
+        elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'nospace':
+            move = False
+        elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'dots':
+            pic_s += "move .1\n"
+            pic_s += '". . ." ljust\n'
+
+            put_text(text, x, 4, " ... ")
+            x += 5
+        elif node.nodeType == node.COMMENT_NODE:
+            pass
+        elif node.nodeType == node.TEXT_NODE and node.data.isspace():
+            pass
+        else:
+            fatal("unknown node %s in diagram <header> element" % node)
+
+    text_s = '.br\n'.join(["\\fL%s\n" % s for s in text if s != ""])
+    return para + """
+.\\" check if in troff mode (TTY)
+.if t \\{
+.PS
+boxht = .2
+textht = 1/6
+fillval = .2
+""" + pic_s + """\
+.PE
+\\}
+.\\" check if in nroff mode:
+.if n \\{
+.nf
+""" + text_s + """\
+.fi
+\\}"""
+
+
+def flatten_header(s):
+    s = s.strip()
+    return re.sub(r'\s+', ' ', s)
+
+
+def block_xml_to_nroff(nodes, para='.PP'):
+    HEADER_TAGS = ('h1', 'h2', 'h3', 'h4')
+    s = ''
+    prev = ''
+    for node in nodes:
+        if node.nodeType == node.TEXT_NODE:
+            if s == '' and para != '.IP':
+                s = para + '\n'
+            text = re.sub(r'\s+', ' ', node.data)
+            if s.endswith(' '):
+                text = text.lstrip()
+            s += text_to_nroff(text)
+            s = s.lstrip()
+        elif node.nodeType == node.ELEMENT_NODE:
+            if node.tagName in ['ul', 'ol']:
+                if s != "":
+                    s += "\n"
+                s += ".RS\n"
+                i = 0
+                for li_node in node.childNodes:
+                    if (li_node.nodeType == node.ELEMENT_NODE
+                        and li_node.tagName == 'li'):
+                        i += 1
+                        if node.tagName == 'ul':
+                            s += ".IP \\(bu\n"
+                        else:
+                            s += ".IP %d. .4in\n" % i
+                        s += block_xml_to_nroff(li_node.childNodes, ".IP")
+                    elif li_node.nodeType == node.COMMENT_NODE:
+                        pass
+                    elif (li_node.nodeType != node.TEXT_NODE
+                          or not li_node.data.isspace()):
+                        raise error.Error("<%s> element may only have "
+                                          "<li> children" % node.tagName)
+                s += ".RE\n"
+            elif node.tagName == 'dl':
+                indent = True
+                if prev in HEADER_TAGS:
+                    indent = False
+                if s != "":
+                    s += "\n"
+                if indent:
+                    s += ".RS\n"
+                prev = "dd"
+                for li_node in node.childNodes:
+                    if (li_node.nodeType == node.ELEMENT_NODE
+                        and li_node.tagName == 'dt'):
+                        if prev == 'dd':
+                            s += '.TP\n'
+                        else:
+                            s += '.TQ .5in\n'
+                        prev = 'dt'
+                    elif (li_node.nodeType == node.ELEMENT_NODE
+                          and li_node.tagName == 'dd'):
+                        if prev == 'dd':
+                            s += '.IP\n'
+                        prev = 'dd'
+                    elif li_node.nodeType == node.COMMENT_NODE:
+                        continue
+                    elif (li_node.nodeType != node.TEXT_NODE
+                          or not li_node.data.isspace()):
+                        raise error.Error("<dl> element may only have "
+                                          "<dt> and <dd> children")
+                    s += block_xml_to_nroff(li_node.childNodes, ".IP")
+                if indent:
+                    s += ".RE\n"
+            elif node.tagName == 'p':
+                if s != "":
+                    if not s.endswith("\n"):
+                        s += "\n"
+                    s += para + "\n"
+                s += block_xml_to_nroff(node.childNodes, para)
+            elif node.tagName in HEADER_TAGS:
+                if s != "":
+                    if not s.endswith("\n"):
+                        s += "\n"
+                nroffTag, font = {'h1': ('SH', r'\fR'),
+                                  'h2': ('SS', r'\fB'),
+                                  'h3': ('ST', r'\fI'),
+                                  'h4': ('SU', r'\fI')}[node.tagName]
+                to_upper = node.tagName == 'h1'
+                s += ".%s \"" % nroffTag
+                s += flatten_header(''.join([
+                    inline_xml_to_nroff(child_node, font, to_upper)
+                    for child_node in node.childNodes]))
+                s += "\"\n"
+            elif node.tagName == 'pre':
+                fixed = node.getAttribute('fixed')
+                if fixed == 'yes':
+                    font = r'\fL'
+                else:
+                    font = r'\fB'
+                s += pre_to_nroff(node.childNodes, para, font)
+            elif node.tagName == 'tbl':
+                s += tbl_to_nroff(node.childNodes, para)
+            elif node.tagName == 'diagram':
+                s += diagram_to_nroff(node.childNodes, para)
+            else:
+                s += inline_xml_to_nroff(node, r'\fR')
+            prev = node.tagName
+        elif node.nodeType == node.COMMENT_NODE:
+            pass
+        else:
+            raise error.Error("unknown node %s in block xml" % node)
+    if s != "" and not s.endswith('\n'):
+        s += '\n'
+    return s
diff --git a/build-aux/build/soutil.py b/build-aux/build/soutil.py
new file mode 100755
index 000000000..a65882302
--- /dev/null
+++ b/build-aux/build/soutil.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2008, 2017, 2020 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import getopt
+import os
+import re
+import sys
+
+
+def parse_include_dirs():
+    include_dirs = []
+    options, args = getopt.gnu_getopt(sys.argv[1:], 'I:', ['include='])
+    for key, value in options:
+        if key in ['-I', '--include']:
+            include_dirs.append(value)
+        else:
+            assert False
+
+    include_dirs.append('.')
+    return include_dirs, args
+
+
+def find_file(include_dirs, name):
+    for dir in include_dirs:
+        file = "%s/%s" % (dir, name)
+        try:
+            os.stat(file)
+            return file
+        except OSError:
+            pass
+    sys.stderr.write("%s not found in: %s\n" % (name, ' '.join(include_dirs)))
+    return None
+
+
+so_re = re.compile(r'^\.so (\S+)$')
+
+
+def extract_include_directive(line):
+    m = so_re.match(line)
+    if m:
+        return m.group(1)
+    else:
+        return None
diff --git a/build-aux/dot2pic b/build-aux/dot2pic
new file mode 100755
index 000000000..2f858e19d
--- /dev/null
+++ b/build-aux/dot2pic
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2009, 2010, 2011, 2013, 2017, 2020 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import getopt
+import sys
+
+def dot2pic(src, dst):
+    scale = 1.0
+    while True:
+        line = src.readline()
+        if not line:
+            break
+
+        words = line.split()
+        command = words[0]
+        if command == 'graph':
+            scale = float(words[1])
+        elif command == 'node':
+            name = words[1]
+            x = float(words[2])
+            y = float(words[3])
+            width = float(words[4])
+            height = float(words[5])
+            label, style, shape, color, fillcolor = words[6:11]
+            x *= scale
+            y *= scale
+            width *= scale
+            height *= scale
+            dst.write("linethick = %f;\n" % (0.5 if style == 'bold' else 1.0))
+            dst.write('box at %f,%f wid %f height %f "%s"\n'
+                      % (x, y, width, height, name))
+            if style == 'bold':
+                inset = 2.0 / 72.0
+                width -= inset * 2
+                height -= inset * 2
+                dst.write("box at %f,%f wid %f height %f\n"
+                          % (x, y, width, height))
+        elif command == 'edge':
+            tail = words[1]
+            head = words[2]
+            n = int(words[3])
+
+            # Extract x,y coordinates.
+            words = words[4:]
+            xy = []
+            for i in range(n):
+                x = float(words[0]) * scale
+                y = float(words[1]) * scale
+                words = words[2:]
+                xy.append((x, y))
+
+            # Extract style and color from end of words.
+            style, color = words[-2:]
+            words = words[:-2]
+
+            # If there's anything left, that's the label.
+            if words:
+                xl = float(words[-2]) * scale
+                yl = float(words[-1]) * scale
+                label = ' '.join(words[:-2])
+                if label.startswith('"') and label.endswith('"'):
+                    label = label[1:-1]
+            else:
+                label = None
+
+            dst.write("linethick = %f;\n"
+                      % (0.5 if style == 'dotted' else 1.0))
+            dst.write("spline -> from %f,%f" % xy[0])
+            for x, y in xy:
+                dst.write(" to %f,%f" % (x, y))
+            dst.write('\n')
+
+            if label:
+                dst.write('"%s" at %f,%f\n' % (label, xl, yl))
+        elif command == 'stop':
+            break
+        else:
+            sys.stderr.write("%s\n" % command)
+            assert False
+
+
+options, args = getopt.gnu_getopt(sys.argv[1:], 'f:', [])
+
+font_scale = 0
+for key, value in options:
+    if key == '-f':
+        font_scale = int(value)
+    else:
+        raise False
+
+if font_scale:
+    print(".ps %+d" % -font_scale)
+
+print(".PS")
+print("linethick = 1;")
+if args:
+    for arg in args:
+        dot2pic(open(arg), sys.stdout)
+else:
+    dot2pic(sys.stdin, sys.stdout)
+if font_scale:
+    print(".ps %+d" % font_scale)
+print(".PE")
+
diff --git a/configure.ac b/configure.ac
index 12d2509cd..ecf1b354b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -171,7 +171,10 @@ OVS_ENABLE_SPARSE
 
 OVS_CHECK_DDLOG([0.47])
 OVS_CHECK_PRAGMA_MESSAGE
+
 OVN_CHECK_OVS
+OVN_CHECK_OVSDB_TOOLS
+
 OVS_CTAGS_IDENTIFIERS
 AC_SUBST([OVS_CFLAGS])
 AC_SUBST([OVS_LDFLAGS])
@@ -179,12 +182,6 @@ AC_SUBST([DDLOG_EXTRA_FLAGS])
 AC_SUBST([DDLOG_EXTRA_RUSTFLAGS])
 AC_SUBST([DDLOG_NORTHD_LIB_ONLY])
 
-AC_SUBST([ovs_srcdir], ['${OVSDIR}'])
-AC_SUBST([ovs_builddir], ['${OVSBUILDDIR}'])
-AC_SUBST([ovs_libdir], ['${OVSBUILDDIR}/lib'])
-AC_SUBST([ovsdb_libdir], ['${OVSBUILDDIR}/ovsdb'])
-AC_SUBST([ovs_mandir], ['${OVSDIR}'])
-
 AC_CONFIG_FILES(Makefile)
 AC_CONFIG_FILES(tests/atlocal)
 AC_CONFIG_FILES(include/ovn/version.h)
diff --git a/controller-vtep/automake.mk b/controller-vtep/automake.mk
index a5779eba6..6b52ddc9b 100644
--- a/controller-vtep/automake.mk
+++ b/controller-vtep/automake.mk
@@ -8,7 +8,7 @@ controller_vtep_ovn_controller_vtep_SOURCES = \
 	controller-vtep/ovn-controller-vtep.h \
 	controller-vtep/vtep.c \
 	controller-vtep/vtep.h
-controller_vtep_ovn_controller_vtep_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la $(OVSBUILDDIR)/vtep/libvtep.la
+controller_vtep_ovn_controller_vtep_LDADD = lib/libovn.la $(OVS_LDFLAGS)
 man_MANS += controller-vtep/ovn-controller-vtep.8
 EXTRA_DIST += controller-vtep/ovn-controller-vtep.8.xml
 CLEANFILES += controller-vtep/ovn-controller-vtep.8
diff --git a/controller/automake.mk b/controller/automake.mk
index 41f907d6e..31dfe1c70 100644
--- a/controller/automake.mk
+++ b/controller/automake.mk
@@ -37,7 +37,7 @@ controller_ovn_controller_SOURCES = \
 	controller/local_data.c \
 	controller/local_data.h
 
-controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la
+controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LDFLAGS)
 man_MANS += controller/ovn-controller.8
 EXTRA_DIST += controller/ovn-controller.8.xml
 CLEANFILES += controller/ovn-controller.8
diff --git a/ic/automake.mk b/ic/automake.mk
index 8e71bc334..47da1fa3e 100644
--- a/ic/automake.mk
+++ b/ic/automake.mk
@@ -3,8 +3,8 @@ bin_PROGRAMS += ic/ovn-ic
 ic_ovn_ic_SOURCES = ic/ovn-ic.c
 ic_ovn_ic_LDADD = \
 	lib/libovn.la \
-	$(OVSDB_LIBDIR)/libovsdb.la \
-	$(OVS_LIBDIR)/libopenvswitch.la
+	$(OVSDB_LDFLAGS) \
+	$(OVS_LDFLAGS)
 man_MANS += ic/ovn-ic.8
 EXTRA_DIST += ic/ovn-ic.8.xml
 CLEANFILES += ic/ovn-ic.8
diff --git a/lib/automake.mk b/lib/automake.mk
index 9f9f447d5..ddfe33948 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -2,7 +2,6 @@ lib_LTLIBRARIES += lib/libovn.la
 lib_libovn_la_LDFLAGS = \
         $(OVS_LTINFO) \
         -Wl,--version-script=$(top_builddir)/lib/libovn.sym \
-        $(OVS_LIBDIR)/libopenvswitch.la \
         $(AM_LDFLAGS)
 lib_libovn_la_SOURCES = \
 	lib/acl-log.c \
diff --git a/lib/ovsdb_automake.mk b/lib/ovsdb_automake.mk
index 32f22b672..4384d801b 100644
--- a/lib/ovsdb_automake.mk
+++ b/lib/ovsdb_automake.mk
@@ -1,6 +1,5 @@
 # ovsdb-idlc
 SUFFIXES += .ovsidl .ovsschema
-OVSDB_IDLC = $(run_python) ${OVSDIR}/ovsdb/ovsdb-idlc.in
 .ovsidl.c:
 	$(AM_V_GEN)$(OVSDB_IDLC) c-idl-source $< > $@.tmp && mv $@.tmp $@
 .ovsidl.h:
diff --git a/northd/automake.mk b/northd/automake.mk
index 35ad8c09d..736d11b19 100644
--- a/northd/automake.mk
+++ b/northd/automake.mk
@@ -8,8 +8,8 @@ northd_ovn_northd_SOURCES = \
 	northd/ipam.h
 northd_ovn_northd_LDADD = \
 	lib/libovn.la \
-	$(OVSDB_LIBDIR)/libovsdb.la \
-	$(OVS_LIBDIR)/libopenvswitch.la
+	$(OVSDB_LDFLAGS) \
+	$(OVS_LDFLAGS)
 man_MANS += northd/ovn-northd.8
 EXTRA_DIST += northd/ovn-northd.8.xml
 CLEANFILES += northd/ovn-northd.8
@@ -48,8 +48,8 @@ nodist_northd_ovn_northd_ddlog_SOURCES = \
 northd_ovn_northd_ddlog_LDADD = \
 	northd/ovn_northd_ddlog/target/release/libovn_northd_ddlog.la \
 	lib/libovn.la \
-	$(OVSDB_LIBDIR)/libovsdb.la \
-	$(OVS_LIBDIR)/libopenvswitch.la
+	$(OVSDB_LDFLAGS) \
+	$(OVS_LDFLAGS)
 
 nb_opts = $$(cat $(srcdir)/northd/ovn-nb.dlopts)
 northd/OVN_Northbound.dl: ovn-nb.ovsschema northd/ovn-nb.dlopts
@@ -79,8 +79,7 @@ DDLOGFLAGS = -L $(DDLOGLIBDIR) -L $(builddir)/northd $(DDLOG_EXTRA_FLAGS)
 
 RUSTFLAGS = \
 	-L ../../lib/.libs \
-	-L $(OVS_LIBDIR)/.libs \
-	$$LIBOPENVSWITCH_DEPS \
+	$(OVS_LDFLAGS) \
 	$$LIBOVN_DEPS \
 	-Awarnings $(DDLOG_EXTRA_RUSTFLAGS)
 
@@ -102,9 +101,8 @@ cargo_build_10 = --lib
 cargo_build_11 = --features command-line
 
 libtool_deps = $(srcdir)/build-aux/libtool-deps
-$(ddlog_targets): northd/ddlog.stamp lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la
+$(ddlog_targets): northd/ddlog.stamp lib/libovn.la $(OVS_LDFLAGS)
 	$(AM_V_GEN)LIBOVN_DEPS=`$(libtool_deps) lib/libovn.la` && \
-	LIBOPENVSWITCH_DEPS=`$(libtool_deps) $(OVS_LIBDIR)/libopenvswitch.la` && \
 	cd northd/ovn_northd_ddlog && \
 	RUSTC='$(RUSTC)' RUSTFLAGS="$(RUSTFLAGS)" \
 	    cargo build --release $(CARGO_VERBOSE) $(cargo_build) --no-default-features --features ovsdb,c_api
diff --git a/tests/automake.mk b/tests/automake.mk
index 5b890d644..a9f530bee 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -252,8 +252,13 @@ tests_ovstest_SOURCES = \
 	northd/ipam.c \
 	northd/ipam.h
 
-tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \
-    $(OVS_LIBDIR)/libopenvswitch.la lib/libovn.la
+tests_ovstest_LDADD = \
+	$(OVS_LDFLAGS) \
+    lib/libovn.la
+
+if ! SYSTEM_OVS
+	tests_ovstest_LDADD += $(OVS_LIBDIR)/daemon.lo
+endif
 
 # Python tests.
 CHECK_PYFILES = \
diff --git a/utilities/automake.mk b/utilities/automake.mk
index a03892f20..2e3b4d5cd 100644
--- a/utilities/automake.mk
+++ b/utilities/automake.mk
@@ -75,7 +75,7 @@ utilities_ovn_nbctl_SOURCES = \
     utilities/ovn-dbctl.c \
     utilities/ovn-dbctl.h \
     utilities/ovn-nbctl.c
-utilities_ovn_nbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
+utilities_ovn_nbctl_LDADD = lib/libovn.la $(OVSDB_LDFLAGS) $(OVS_LDFLAGS)
 
 # ovn-sbctl
 bin_PROGRAMS += utilities/ovn-sbctl
@@ -83,26 +83,26 @@ utilities_ovn_sbctl_SOURCES = \
     utilities/ovn-dbctl.c \
     utilities/ovn-dbctl.h \
     utilities/ovn-sbctl.c
-utilities_ovn_sbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
+utilities_ovn_sbctl_LDADD = lib/libovn.la $(OVSDB_LDFLAGS) $(OVS_LDFLAGS)
 
 # ovn-ic-nbctl
 bin_PROGRAMS += utilities/ovn-ic-nbctl
 utilities_ovn_ic_nbctl_SOURCES = utilities/ovn-ic-nbctl.c
-utilities_ovn_ic_nbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
+utilities_ovn_ic_nbctl_LDADD = lib/libovn.la $(OVSDB_LDFLAGS) $(OVS_LDFLAGS)
 
 # ovn-ic-sbctl
 bin_PROGRAMS += utilities/ovn-ic-sbctl
 utilities_ovn_ic_sbctl_SOURCES = utilities/ovn-ic-sbctl.c
-utilities_ovn_ic_sbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
+utilities_ovn_ic_sbctl_LDADD = lib/libovn.la $(OVSDB_LDFLAGS) $(OVS_LDFLAGS)
 
 # ovn-trace
 bin_PROGRAMS += utilities/ovn-trace
 utilities_ovn_trace_SOURCES = utilities/ovn-trace.c
-utilities_ovn_trace_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
+utilities_ovn_trace_LDADD = lib/libovn.la $(OVSDB_LDFLAGS) $(OVS_LDFLAGS)
 
 # ovn-nbctl
 bin_PROGRAMS += utilities/ovn-appctl
 utilities_ovn_appctl_SOURCES = utilities/ovn-appctl.c
-utilities_ovn_appctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
+utilities_ovn_appctl_LDADD = lib/libovn.la $(OVSDB_LDFLAGS) $(OVS_LDFLAGS)
 
 include utilities/bugtool/automake.mk
-- 
2.35.1

openSUSE Build Service is sponsored by