Repository: mesonbuild/meson

Issue:
Includes order: local includes should go first (part 2)
There is still or again something wrong with the include order. Local includes from library() include_directories are not before dependency include paths:

> [9/59] Compiling c object 'gst-libs/gst/audio/gstaudio-1.0@sha/gst-libs_gst_audio_audio-enumtypes.c.o'
> FAILED: gst-libs/gst/audio/gstaudio-1.0@sha/gst-libs_gst_audio_audio-enumtypes.c.o 
> ccache cc  '-Igst-libs/gst/audio/gstaudio-1.0@sha' '-I../gst-libs/gst/audio' '-Igst-libs/gst/audio' '-pipe' '-Wall' '-Winvalid-pch' '-std=gnu99' '-DHAVE_CONFIG_H' '-O2' '-g' '-fPIC' '-pthread' **'-I/usr/include/gstreamer-1.0' '-I/usr/lib/x86_64-linux-gnu/gstreamer-1.0/include'** '-I/usr/include/glib-2.0' '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include' '-pthread' '-I/usr/include/gstreamer-1.0' '-I/usr/lib/x86_64-linux-gnu/gstreamer-1.0/include' '-I/usr/include/glib-2.0' '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include' '-I/usr/include/glib-2.0' '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include' **'-Igst-libs' '-I../gst-libs' '-I.' '-I../.' '-Igst-libs/gst/audio'** '-MMD' '-MQ' 'gst-libs/gst/audio/gstaudio-1.0@sha/gst-libs_gst_audio_audio-enumtypes.c.o' '-MF' 'gst-libs/gst/audio/gstaudio-1.0@sha/gst-libs_gst_audio_audio-enumtypes.c.o.d' -o 'gst-libs/gst/audio/gstaudio-1.0@sha/gst-libs_gst_audio_audio-enumtypes.c.o' -c gst-libs/gst/audio/audio-enumtypes.c
> gst-libs/gst/audio/audio-enumtypes.c: In function ‘gst_audio_channel_mixer_flags_get_type’:
> gst-libs/gst/audio/audio-enumtypes.c:156:9: error: ‘GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED’ undeclared (first use in this function)
>        { GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED, "GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED", "non-interleaved" },
>          ^

This tries to compile the (generated) gst-libs/gst/audio/audio-enumtypes.c.

This does an `#include "audio.h"` which in turn does an `#include <gst/audio/audio-channel-mixer.h>` amongst other headers.

Now because of the include order this finds /usr/include/gstreamer-1.0/gst/audio/audio-channel-mixer.h instead of ../gst-libs/gst/audio/audio-channel-mixer.h (which has the new include) and compilation fails.



Discussion:
I did some testing and don't think it's a regression but #372 was never fully fixed.

I tried with

```
commit 79537b54dbdaf467e425f6337d1c1566c4ccf26b
Author: Jussi Pakkanen <jpakkane@gmail.com>
Date:   Fri Mar 11 00:03:55 2016 +0200

    Implicit include directories go first.

```

which follows

```
commit 4b3c43649eef34ced681d76f515113038117492e
Author: Jussi Pakkanen <jpakkane@gmail.com>
Date:   Mon Feb 1 21:42:00 2016 +1100

    Put internal include dirs at the beginning so they override ones coming from the system. Closes #372.

```

and the same happens.

In fact, I think this is what we discussed in the last few comments on bug #372 but it was never re-opened

I have a test case. It needs some pkg-config dependency():

```
project('include order', 'c')

ext_dep = dependency('glib-2.0')
#ext_dep = declare_dependency(include_directories : include_directories('external-subdir'))

global_internal_inc = include_directories('some-subdir')

somelib = library('some', 'some.c',
  include_directories : global_internal_inc,
  dependencies : ext_dep)
```

Plus `mkdir some-subdir; mkdir external-subdir; touch some.c`

With the pkg-config dependency():

> [1/2] ccache cc  '-Isome@sha' '-I../../home/tpm/Devel/meson/test cases/common/114 include order' '-I.' '-pipe' '-Wall' '-Winvalid-pch' '-O0' '-g' '-fPIC' **'-I/usr/include/glib-2.0' '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include' '-Isome-subdir'** '-I../../home/tpm/Devel/meson/test cases/common/114 include order/some-subdir' '-MMD' '-MQ' 'some@sha/some.c.o' '-MF' 'some@sha/some.c.o.d' -o 'some@sha/some.c.o' -c '../../home/tpm/Devel/meson/test cases/common/114 include order/some.c'
> [2/2] ccache cc    -o libsome.so 'some@sha/some.c.o' '-Wl,--no-undefined' '-shared' '-fPIC' '-Wl,-soname,libsome.so' '-lglib-2.0' '-Wl,-rpath,/tmp/_build/' 

With the internal subdir and declare_dependency() (the commented out part) it's not triggered though.


Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1422,21 +1422,21 @@ def generate_single_compile(self, target, outfile, src, is_generated=False, head
         commands = []
         # The first thing is implicit include directories: source, build and private.
         commands += compiler.get_include_args(self.get_target_private_dir(target), False)
+        # Compiler args for compiling this target
         commands += compilers.get_base_compile_args(self.environment.coredata.base_options,
                                                     compiler)
+        # Add the root source and build directories as include dirs
         curdir = target.get_subdir()
         tmppath = os.path.normpath(os.path.join(self.build_to_src, curdir))
         commands += compiler.get_include_args(tmppath, False)
         if curdir ==  '':
             curdir = '.'
         commands += compiler.get_include_args(curdir, False)
-        commands += self.generate_basic_compiler_args(target, compiler)
-        # -I args work differently than other ones. In them the
-        # first found directory is used whereas for other flags
-        # (such as -ffoo -fno-foo) the latest one is used.
-        # Therefore put the internal include directories here
-        # at the beginning so they override args coming from
-        # e.g. pkg-config.
+        # -I args work differently than other ones. In them the first found
+        # directory is used whereas for other flags (such as -ffoo -fno-foo) the
+        # latest one is used.  Therefore put the internal include directories
+        # here before generating the "basic compiler args" so they override args
+        # coming from e.g. pkg-config.
         for i in target.get_include_dirs():
             basedir = i.get_curdir()
             for d in i.get_incdirs():
@@ -1448,6 +1448,7 @@ def generate_single_compile(self, target, outfile, src, is_generated=False, head
                 commands += sargs
             for d in i.get_extra_build_dirs():
                 commands += compiler.get_include_args(d, i.is_system)
+        commands += self.generate_basic_compiler_args(target, compiler)
         for d in target.external_deps:
             if d.need_threads():
                 commands += compiler.thread_flags()


Test patch:
diff --git a/test cases/linuxlike/6 subdir include order/meson.build b/test cases/linuxlike/6 subdir include order/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/linuxlike/6 subdir include order/meson.build	
@@ -0,0 +1,12 @@
+project('subdir include order', 'c')
+
+# Ensure that headers in subdirs override external dependencies
+cc = meson.get_compiler('c')
+
+glib = dependency('glib-2.0')
+
+# This will fail to compile if it picks up the system-installed glib instead of
+# the glib in the subdir
+executable('prog', 'prog.c',
+  include_directories : include_directories('subdir'),
+  dependencies : glib)
diff --git a/test cases/linuxlike/6 subdir include order/prog.c b/test cases/linuxlike/6 subdir include order/prog.c
new file mode 100644
--- /dev/null
+++ b/test cases/linuxlike/6 subdir include order/prog.c	
@@ -0,0 +1,7 @@
+#include <glib.h>
+
+#ifndef MESON_OUR_GLIB
+#error "Failed"
+#endif
+
+int main() { return 0; }
diff --git a/test cases/linuxlike/6 subdir include order/subdir/glib.h b/test cases/linuxlike/6 subdir include order/subdir/glib.h
new file mode 100644
--- /dev/null
+++ b/test cases/linuxlike/6 subdir include order/subdir/glib.h	
@@ -0,0 +1 @@
+#define MESON_OUR_GLIB 1