[ 
https://issues.apache.org/jira/browse/MINIFICPP-824?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17096123#comment-17096123
 ] 

Dániel Bakai commented on MINIFICPP-824:
----------------------------------------

[~aboda] [~aldrin]

The fix for this is not trivial. The issue is if that a single third party 
dependency is included from /usr/include (or /usr/local/include), it will 
"pollute" the include paths, and headers installed to the system will get 
priority over our headers, if /usr/include comes before our include paths in 
the compile command.

When we are defining the transitive include usage requirements for a target, we 
are populating the INTERFACE_INCLUDE_DIRECTORIES property of that target. This 
could normally be done with `target_include_directories(<targetname> INTERFACE 
/path/to/headers)`, but unfortunately until about 2 years ago this did not work 
for IMPORTED targets because of an oversight in CMake, so currently, to keep 
supporting older CMake versions, we are manually appending to this list with 
`set_property(TARGET <targetname> APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES 
/path/to/headers)` - but the end result is the same. Now, 
`target_include_directories` has a BEFORE directive, which places the include 
path at the beginning of the INTERFACE_INCLUDE_DIRECTORIES list of the target, 
not the end. But this does not solve our problem, because this is not a 
magically transitive property - that is, this will just mean that it will place 
the include path to the beginning of the include paths list transitively 
required by our IMPORTED target (libfoobar). When we link with libfoobar (let's 
assume we are linking minifi), we will inherit its list of 
INTERFACE_INCLUDE_DIRECTORIES, but that sublist will just be placed in our list 
of include directories with all other ones inherited from other targets we 
depend on - the BEFORE directive in libfoobar's INTERFACE_INCLUDE_DIRECTORIES 
won't influence the placement of that sublist in our include directories list, 
it just influences the place of the include directory in libfoobar's sublist. 
So if another target's sublist comes before us that requires /usr/include - and 
we can't really influence the order in which these sublists will be added - 
then every further include path sublist will have a lesser priority, meaning 
that every further third party header will be sourced from /usr/include, if it 
exists there.

To solve this, I see 3 main options, each with its advantages and disadvantages:

1. Install every bundled third party's include directory in a way that is 
incompatible with the ones normally installed on the system, and include them 
that way.
For example, when we are building libfoobar, it would normally install its 
headers into `libfoobar-install/include/foobar`. We would be adding `-I 
libfoobar-install/include/` to our compile command, and include the header with 
`#include <foobar/foobar.h>`.
Instead of this, we can do the following: make libfoobar install its headers 
into `libfoobar-install/include/thirdparty/foobar`, add `-I 
libfoobar-install/include/` to our compile command, and include the header with 
`#include <thirdparty/foobar/foobar.h>`.
This has the advantage of:
 - not having to deal with CMake's include path order in any way: there is no 
chance of a collision with headers installed on the system.
The disadvantages of this approach is:
 - we have to change every bundled third party build to install the include 
directories in a special way. This means either patching the third parties, or 
manually moving the includes after installation.
 - we would lose the ability to easily change between bundled and system 
dependencies - as the include directives are different. This can be worked 
around by creating a "proxy" include if we want to use a system dependency, and 
adding it to our include path in that case - this would just be a 
"thirdparty/foobar/foobar.h" header file that would include the real 
"foobar/foobar.h" header in the system case.

2. Install every bundled third party's includes to the same directory and force 
CMake to use that directory as the first include path.
Currently, each bundled third party has its own install directory. libfoobar 
libs and headers will be installed to lifoobar-install/lib and 
libfoobar-install/include, respectively, while libbuzz will be installed to 
libbuzz-install/lib and libbuzz-install/include.
In our compile commands we have all of these include directories added as 
include paths.
Instead of this, we can make all third parties install their headers to the 
same directory - lets call it thirdpary-include/ -, thereby creating our very 
own /usr/include.
After this, we can force CMake to use that singular include path as the first 
include path for every command (for example, by adding it to the compiler 
flags). This is much easier to do with a single directory than with multiple, 
conditionally existing and used include directories.
This has the advantage of:
 - third parties almost without exception support installing headers to a 
different place (this is easier then #1, as there we would have to change to 
structure of how headers get installed, in this case we only have to change the 
location).
 - we override CMake's include path order resolution, but in a minimally 
invasive way
This has the disadvantage of:
 - we have to make sure we force this directory to the be the very first in all 
cases

3. Use only bundled dependencies AND sysroot.
If we only use bundled dependencies, there will be no need to add the real 
/usr/include to our include paths. We will still have to use system headers, 
which are in /usr/include, but if we use a sysroot, we will use the system 
headers from the sysroot's /usr/include, which, unlike the real /usr/include on 
our system does not contain installed third parties.
This has the advantage of:
 - not having to deal with dependencies installed on the system at all
This has the disadvantage of:
 - completely prevents us from using installed third parties. Even if we don't, 
some extensions might still want to do it, and they would still be broken then.
 - we have to have a sysroot - macOS compilations already use one by default, 
Windows is not really affected by this whole issue, but we would have to create 
one on Linux. It would be generally advantageous to do so, because it gives 
better control over what type and level of system APIs (libc version, for 
example) we end up using, but it is not a trivial undertaking.


It seems to me that option #2 is the most feasible right now.

> MacOS build uses RocksDB from brew, if installed, causing segfaults
> -------------------------------------------------------------------
>
>                 Key: MINIFICPP-824
>                 URL: https://issues.apache.org/jira/browse/MINIFICPP-824
>             Project: Apache NiFi MiNiFi C++
>          Issue Type: Bug
>            Reporter: Dániel Bakai
>            Assignee: Dániel Bakai
>            Priority: Major
>
> If RocksDB is installed from brew, libminifi/test/rocksdb-tests/RepoTests 
> segfaults, because of a wrong call to rocksdb::DBImpl's destructor (instead 
> of the destructor, an another member function is called).
> It seems like somehow the shipped and the system versions mix up, probably 
> causing an ODR violation and ultimately the segfault. (Or the version shipped 
> in brew is _very_ incompatible.)
> After RocksDB was uninstalled from brew, and minifi was rebuilt, the test ran 
> correctly.
> This might affect other platforms as well.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to