Allow custom_target and generator to accept include_directories objects
Recently, I've noticed a number of places where either a custom_target or a generator would benefit from directly understanding an include_directories object.
I considered adding a to_string() method to include_directories, but rejected it for a couple of reasons:
- In a generator that would require storing the strings as absolute paths, this would break the ability to move a meson build directory, since the include directories would break. This is required because a generator must be usable in a different directory than the one it is defined in.
- The same problem will exist for custom_targets if my work at adding a templating helper ever lands
- Meson generally prefers smart objects to strings, consider
include_directoriesandfilesas examples of this.
As such, I have opted to add the include_directories field to custom_target, generator and generator.process. This formatted for the underlying program by a new custom_target_template parameter, which takes an array of strings which must include the @DIR@ formatter, and defaults to ['-I@DIR@']. This provides a way to tell Meson how the tools expects include_directories to be passed. Additionally, when using this functionality the command or arguments must include a @INCLUDE_DIRS@ formatter.
For example:
custom_target(
command : [gcc, '@INPUT@', '-o', '@OUTPUT@', '@INCLUDE_DIRS@']
include_directories : include_directories('include'),
# include_directories_template : ['-I@DIR@'], # just for demonstration
)
For generators include_directories can also be passed to process(), which will be placed before the include_directories passed to the generator() call.
I understand how you came to this conclusion but it feels very worrisome to require three different kwargs working in tandem to get the minimal usecase to work.
I would also not really expect people to care about include_directories objects in the first place since that can be manually constructed -- it's known by the author of the meson.build -- whereas dependency objects have the same issue, but also have to handle link arguments, and need to be used in a variety of places including as input to configure_file
I can put the include directories in the command directly if that's the problem. I did that initially but it ended up being really messy and I didn't like it.
This was spurred by the following pattern in several places in mesa:
global_includes = include_directoriers('../../../include', '../../../utils') # actually defined in ../../../, but here for illistration
local_includes = include_directories('private', 'hardware')
generated = custom_target = (
...
command : [
...
'-I' + meson.current_source_dir() / 'private',
'-I' + meson.current_source_dir() / 'hardware',
'-I' + meson.current_source_dir() / '../../../include',
'-I' + meson.current_source_dir() / '../../../utils',
'-I' + meson.current_build_dir() / '../../../utils',
]
)
lib = static_library(
'lib',
'src.c', 'src.cpp' , generated,
include_directories : [global_includes, local_includes,
)
So yeah, writing:
generated = custom_target = (
...
command : [
..., '@INCLUDE_DIRS@',
],
include_directories : [local_includes, global_includes],
)
is a huge win. if the include_directories field is the problem I can rewrite it to do:
generated = custom_target = (
...
command : [
..., local_includes, global_includes,
],
)
but I also ran into this working with generators, where strings are a really big problem because you have to use strings that are absolute with generators, which is a thing we've (at least the in the past) tried very hard not to do for things that are relative to the build directory or source directory.
I think I would rather see a .flags() method on include_directories. Similar to what we want to do with dependency.
Edit: dcbaker has shown me the potential pitfalls here.
@dcbaker
This was spurred by the following pattern in several places in mesa: ... '-I' + meson.current_source_dir() / 'hardware',
FWIW: After a fair bit of pain, I figured out that constructing include paths this way can lead to depfiles with different paths than when meson invokes the compiler. The problem is that meson.current_build_dir() and meson.current_source_dir() can return absolute paths, but meson uses relative paths when constructing -I from many uses of include_directories(). Which can lead to missing rebuilds and things like ninja -t missingdeps not working reliably.
While ninja does some canonicalization of paths, it doesn't turn absolute paths to relative ones or vice versa.