Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dependency not inherited? #495

Closed
germandiagogomez opened this issue Apr 1, 2016 · 15 comments
Closed

dependency not inherited? #495

germandiagogomez opened this issue Apr 1, 2016 · 15 comments

Comments

@germandiagogomez
Copy link
Contributor

Hello,

I have the following config:

SuperProject/
    meson.build
    libProject/
        meson.build
    exeProject/
        meson.build
  1. libProject (static lib) has a public dependency on boost.
  2. exeProject uses link_with: libProject.

The problem is that I do not inherit the boost includes when I link my exe to libProject target.
I think this should work --> the dependency is a libProject dependency, not a dependency
from exeProject. Is there any workaround for this?

@rhd
Copy link
Contributor

rhd commented Apr 1, 2016

You have to use declare_dependency(...) in your libProject dir. Then you can pass along other dependencies, include paths, etc...

@germandiagogomez
Copy link
Contributor Author

You have to use declare_dependency(...) in your libProject dir. Then you can pass along other dependencies, include paths, etc...

I saw that short ago. Thanks, it helps. I just have one question remaining: my package depends on boost publicly:

  • can I extract the includes from boost directly from my boost_dep dependency object?

@rhd
Copy link
Contributor

rhd commented Apr 2, 2016

can I extract the includes from boost directly from my boost_dep dependency object?

You shouldn't have to care about the individual include paths of the dependency... but if for some reason you had to, I don't believe you can.

Maybe post a sample of what you're trying to do...

@germandiagogomez
Copy link
Contributor Author

You shouldn't have to care about the individual include paths of the dependency... but if for some reason you had to, I don't believe you can.

My use case is the following:

I have a library to be consumed (internally) by an executable in a sibling project:

SuperProject/
   meson.build
   libProject/
       meson.build
   exeProject/
       meson.build

libProject has a header file that depends on boost, so I want that when libProject is consumed by exeProject, it inherits the include directories from libProject but also the transitive dependency to boost. I think this use case should be supported. In some cases I can hide the dependency, but, for example, for header-only libraries with templates it is not possible.

@rhd
Copy link
Contributor

rhd commented Apr 3, 2016

@jpakkane the documentation for declare_dependency wasn't updated when #351 got merged.

@germandiagogomez declare_dependency can also take a dependencies list.

mylib_inc = include_directories('.')
mylib_dep = declare_dependency(
    include_directories: mylib_inc,
    dependencies: [boost_dep, another_dep],
)

@germandiagogomez
Copy link
Contributor Author

@rhd Thank you. I like meson more and more 👍

@jpakkane: one more thing I would like to ask. Projects have includes that should be private (only used in .cpp files, for example if I include <somelib.h> privately) and some that should be propagated (used in .h files). Is this kind of propagation segregation supported? It is like CMake PRIVATE and PUBLIC modifier.

If we have this, visual studio (I saw a lot of moves there lately) and XCode, I think I would definitely switch to meson. I am liking it way more than cmake.

@rhd
Copy link
Contributor

rhd commented Apr 4, 2016

If you don't add the include path to the declare_dependency, then it won't propagate. Maybe something like this?

lib = static_library('foo', src,
    include_directories: include_directories('my_privates'),
    dependencies: [foo_dep1, foo_dep2]
)

lib_dep = declare_dependency(
    include_directories: include_directories('.'),
    link_with: lib,
    dependencies: [foo_dep1, foo_dep2],
)

@germandiagogomez
Copy link
Contributor Author

If you don't add the include path to the declare_dependency, then it won't propagate. Maybe something like this?

I want to know this correctly. Sorry because I have no access to the machine with the meson build as of now, I could check then. Here my question:

boost_dep = dependency('boost', modules : ['thread'])

lib = static_library('foo', src,
                              include_directories : include_directories('my_includes'))

lib_dep = declare_dependency(include_directories('.'),
                                                 link_with: lib,
                                                 dependencies: [boost_dep])

my_exe = executable(..., dependencies : lib_dep)
  1. Will boost includes be propagated through lib_dep to my_exe? (They should as I understand). If the reply is no, this would be wrong because either:

    a. now my_exe needs to know about boost indirect dependency but my_exe should not care about it.
    OR
    b. I need to add by hand boost_dep includes to lib_dep but there is not way to extract that information from boost_dep object.

  2. Will lib include_directories be propagated? I understand they will not unless you add them to include_directories in lib_dep.

I really feel it would be much more ergonomic to distiguish between public and private dependencies directly in lib target and just add lib to my executable. Is there any specific reason to do it this way? I guess a dependency and a target is not the same thing (their types in meson I mean), but... at the end you want to use your targets as dependencies.

If I can accomplish the same, it is still quite ok.

@rhd
Copy link
Contributor

rhd commented Apr 6, 2016

Will boost includes be propagated through lib_dep to my_exe? (They should as I understand).

Yes, it will.

Will lib include_directories be propagated? I understand they will not unless you add them to include_directories in lib_dep.

You're right

really feel it would be much more ergonomic to distiguish between public and private dependencies directly in lib target and just add lib to my executable.

Yea, maybe. I don't mind the explicit declare_dependency step... although it does force you to repeat yourself a little bit. The only build system that I've seen that allows you to do that is gn. But meson's DSL is way nicer, IMO. Once you get a convention, it becomes easier. You want libfoo? Just add a dependency on libfoo_dep. You just need the include directory? Use libfoo_inc. Etc...

Jussi, will have to give more details about why things are the way they are... but for me, meson is pretty great.

@germandiagogomez
Copy link
Contributor Author

but for me, meson is pretty great.

👍

I think the difference between a lib and a dependency is confusing. They should be
the same from the point of view of consuming them, unless there are good reasons not to do so.

In library target you can have include_directories, but these are private. In a dependency
include_directories are public.

  • It makes sense for dependencies, you want to propagate the includes in a dependency.
  • But for a target there are 2 kinds of includes -- one kind is propagated, the other kind is not.

For a library target I would use something (maybe, need to give more thought):

  1. private_include_directories. -- not propagated when lib used as a dependency, a synonymous for current include_directories.
  2. public_include_directories -- propagated when you put library in a dependency through link_with.

The result would look like this:

boost_dep = dependency('boost', modules : ['thread'])

lib = static_library('foo', src,
                              public_include_directories : include_directories('my_includes'),
                              private_include_directories: include_directories('internal/include'))

lib_dep = declare_dependency(link_with: lib, #propagates public_include_directories
                                             dependencies: [boost_dep])

my_exe = executable(..., dependencies : lib_dep)

I think this is a bit cleaner and for a newcomer, she understands which includes are of what kind.

I am not sure but I think that there should also be public and private dependencies. Currently we have "private dependencies" only in library targets.

The example then would become in my case:

boost_dep = dependency('boost', modules : ['thread'])

lib = static_library('foo', src,
                              public_include_directories : include_directories('my_includes'),
                              private_include_directories: include_directories('internal/include'),
                              public_dependencies : ['boost_dep'])

#Inherits what makes sense --> boost dependency and my_includes
lib_dep = declare_dependency(link_with: lib)

my_exe = executable(..., dependencies : lib_dep)

What do you think?

@rhd
Copy link
Contributor

rhd commented Apr 6, 2016

If you're original issue is resolved, you should probably close this issue and start another with the specific feature request.

I'm not sure I like the extra private_ and public_. If anything, I'd rather remove the explicit need for declare_dependency like how gn does it... maybe.

Look at test case 93. Looks like some work was started re: private directories... but I'm not sure what the status is...

Besides that, there seems to be a documentation problem with declare_dependency.

@jpakkane
Copy link
Member

jpakkane commented Apr 7, 2016

Sorry I haven't commented on this, busy with other stuff. This is a tricky issue because you need to inherit dependencies in some cases even if they appear to be secret. For example:

executable -> static library -> some dep

In this case you must put the deps when linking the executable and not when linking the static library. Similarly if the dep is self-built static lib that has deps on its own.

I'll try to look into this during the weekend.

@germandiagogomez
Copy link
Contributor Author

In this case you must put the deps when linking the executable and not when linking the static library. Similarly if the dep is self-built static lib that has deps on its own.

Yes, true. I think that when things are linked should be an implementation detail actually: if a static library has a dependency, not in the meson sense strictly, the build system should do bookeeping of that. The command line to issue should be independent of this. Is this doable?

@jpakkane
Copy link
Member

I think the difference between a lib and a dependency is confusing. They should be
the same from the point of view of consuming them, unless there are good reasons not to do so.

There is a very strong semantic reason to do this. A library is just that, a collection of code to link against. A dependency is something bigger: a library + all the headers etc needed to build and link against it.

The reason this is so has to do with the development history. Originally we had only links_with. It was only after some time that the idea of declare_dependency was invented. If someone was writing a new build system from scratch, using the latter as a basic building block might make sense (or it might not, it's hard to tell without doing the work).

In practice it might be useful to think of declare_dependency as a sort of internal pkg-config (which is what it is). Thus you would group your projects like this:

internal_dep = ...
external_dep = ...
internal_incdir = ...
external_incdir = ...

lib = library(...,
  include_directories : [internal_incdir, external_incdir],
  dependencies : [internal_dep, external_dep])

lib_dep = declare_dependency(include_directories : external_incdir,
  dependencies : [external_dep])

# Some unit tests only need public interface so use declare_dep
unit_test_exe = executable(..., dependencies : lib_dep)

# Some unit tests poke the internals so you need to set it up manually.
unit_test_exe2 = executable(...,
  include_directories : [internal_incdir, external_incdir],
  dependencies : [internal_dep, external_dep])

Projects that use this lib as a subproject would only use lib_dep and nothing else.

There are two different build types here, the public usage and internal usage by unit tests. Thus you need two different declarations.

@anordal
Copy link

anordal commented Aug 28, 2020

As a matter of discoverability, people who know how it works in CMake will be searching for the PUBLIC keyword, which is not mentioned in the reference guide, and proceed to read everything about the library() function in search of how else to express public includes and dependencies, and fail the task. I just went through this myself before finding this.

As a matter of practicality, Meson's declare_dependency() is more like the INTERFACE keyword in CMake, whereas PUBLIC (which means both private and interface) is the more common option that avoids listing them two places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants