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

Allow configure_file, custom_target and generator output file to subdirectories under builddir #2320

Open
jasonszang opened this issue Sep 14, 2017 · 104 comments

Comments

@jasonszang
Copy link

jasonszang commented Sep 14, 2017

I've encountered two cases that would require configurating / generating file into subdirectories.

  1. Project configuration header that would be included from other headers in the same project

Say I have a library project named foo and the project layout is like this:

include/
    foo/
        bar/
            foobar/
                foobar.h # includes config.h
            bar.h        # includes config.h
        foo.h            # includes config.h
        config.h.in # configured by meson
src/
    foo/
        foo.cpp
...

We would want our client code use #include <foo/config.h> to include our config.h since we don't install config.h directly to includedir. But for our own code this would be impossible since generated config.h is directly under <builddir>, not <builddir/foo>.

Of course, for foo.cpp the work-around is easy: just #include <config.h>. But it becomes complicated we need to include config.h from bar.h and foobar.h.

In fact, there is no way to correctly include config.h that both work in project directory and after installation. If we want bar.h and foobar.h to be correct after installation we must #include <foo/config.h> or use relative path in them. Including <foo/config.h> won't compile, and although relative path happens to work for bar.h, it won't for foobar.h. Neither will it work if we reside in a subdir. Using #include <config.h> allows us to compile, but bar.h and foobar.h breaks up after installation.

The correctly way to configure and include config.h should be just generate it under builddir/foo and #include <foo/config.h> everywhere, just as if it were in the position of config.h.in. Currently meson does not allow this, and forces a flat include directory, or hacking into builddir.

  1. Protobuf generation

This one is simpler. If we have some protos like

protos/
    foo/
        foo.proto
    bar.proto

in which bar.proto imports foo.proto.
it would be nice if we could use one generator for both protos and and get under build directory something like this:
For the compiled protos to work the directory structure of generated sources must mirror the protos, because bar.pb.h will #include "foo/foo.pb.h". The generated sources must look like this:

protos/
    foo/
        foo.pb.h
        foo.pb.cc
    bar.pb.h
    bar.pb.cc

Althouth this would also require something like @dirname@ substitution if we are to pass generated sources to build targets properly, but without supporting generating source trees we cannot use protos with subdirectories. This also prevents wrapping of existing projects that use protos this way, which is quite a common practice.

PS: I suggest putting every generated file from configure_file() and custom_target() (including ones in subdirs) into one directory tree named something like <builddir>/sources@gen, add it to include directory, and allow outputing to subdirectories under it. This should avoid potential name clashes and give developers control over generated folder layout.

@jasonszang jasonszang changed the title Allow configure_file, custom_target and generator output file to subdirectories Allow configure_file, custom_target and generator output file to subdirectories under builddir Sep 14, 2017
@jasonszang jasonszang reopened this Sep 15, 2017
@rom1v
Copy link

rom1v commented Oct 13, 2017

I think I have a similar case: https://stackoverflow.com/questions/46729488/how-to-compile-aidl-files-using-meson

I need these source files:

  • src/com/rom1v/example/Service.aidl
  • src/another/package/Other.aidl

to be compiled under:

  • builddir/gen/com/rom1v/example/Service.java
  • builddir/gen/another/package/Other.java

From the java compiler point of view, this is an error for a java file not be in a directory matching the package name.

@liugang
Copy link
Contributor

liugang commented Oct 31, 2017

#2548 is possible way to fix this type of issue.

if one project call configure_file() with install_dir, install_headers(), and so on. copy these header files to a include_intermediates_dir, and all compile target include this intermediate directory.

@jasonszang
Copy link
Author

jasonszang commented Nov 1, 2017

@nirbheek
Would you please be kind enough to explain to me the considerations behind the design choice of Output must not contain a path segment. in source generation targets like custom_target and why are generating code into subdirectories of build dir considered harmful?
I am trying to figure out a way to properly compile protobuf proto files with custom_target (by properly I mean get meson to figure out the dependency relations correctly), which I would like to use in a small production project which for some reason does not fit in our in-house build system. I acturally found a way that work with proto files that has no subdirectories (all protos must live in a single folder), but proto subtrees like the one I wrote at the top can not be built by meson. Then I commented out these two lines in build.py

    if '/' in i:
        raise InvalidArguments('Output must not contain a path segment.')

And it worked surprisingly well. But I'm afraid by doing this I may be breaking something else inside meson. Could you please give me some explanation about this limitation and let me know your thought about its removal (or replacement with something like must not contain .. component if escaping is the problem)? With this limitation proto trees with subdirectories seems never could be properly built by meson.

@rib
Copy link
Contributor

rib commented Feb 7, 2018

Just to mention that I recently (#3023) also found it a little awkward that configure_file()'s output filename couldn't include a subdirectory while I wanted to generate a .java file based on some build time config options. The javac compiler and Meson's jar() require source code to be laid out according to the namespace of the Java package.

In my case it's possible to work around the limitation by creating a meson.build under src/java/com/impossible/glimpse/meson.build where I use configure_file() which results in a file like <builddir>/src/java/com/impossible/glimpse/<output file>

I wonder if the subdirectory were limited to matching the subdirectory of the input filename that might appease some of the general concern around having random control over the build directory layout. This would still allow meson control to change the top level structure of the build directory but it would have to preserve the subdirectories requested for an output file.

E.g. if I want to generate a GlimpseConfig.java from GlimpseConfig.java.in in the package com.impossible.glimpse with a file under src/java/com/impossible/glimpse/GlimpseConfig.java.in then ideally I could do:

configure_file(input: 'com/impossible/glimpse/GlimpseConfig.java.in',
        output: 'com/impossible/glimpse/GlimpseConfig.java',
        configuration: conf_data)

And in the build directory meson can create:
<build>/any/meson/private/top/level/com/impossible/glimpse/GlimpseConfig.java

The top level layout doesn't really matter (at least in my case) so long as the java compiler + ninja backends knows what it is.

So it might not imply spaghetti access to arbitrary build directories, just some control to build a sub-heirachy within the build directory.

@NickeZ
Copy link
Contributor

NickeZ commented Mar 5, 2018

Also ran into this problem with custom_target and doxygen. The call to doxygen will produce the html files needed and the latex sources needed. Doxygen also produces a Makefile that helps you run pdflatex the right amount of times and in the right order.

I would like to write something like this:

cdata = configuration_data()
cdata.set('TOP_SRCDIR', meson.source_root())
cdata.set('TOP_BUILDDIR', meson.build_root())
cdata.set('VERSION', meson.project_version())

doxyfile = configure_file(
    input: 'Doxyfile.in',
    output: 'Doxyfile',
    configuration: cdata,
    install: false
)

html_target = custom_target(
    'htmldocs',
    build_by_default: false,
    input: doxyfile,
    output: ['html/index.html', 'latex/Makefile'],
    command: [doxygen, doxyfile],
)

pdf_target = custom_target(
    'pdfdocs',
    build_by_default: false,
    input: 'latex/Makefile',
    output: 'latex/refman.pdf',
    command: [make, '-C', '@OUTDIR@/latex'],
)
docs/meson.build:15:0: ERROR: Output must not contain a path segment.

@nussbrot
Copy link

I also ran into this problem and currently don't know how to work around it.
I'm evaluating meson and am trying to figure out if i can use it to build a FPGA project.
One of the problems is IP-generation.

This usually works like this: You generate something like a IP-variation file, which is some kind of XML format, good for versioning and then you run a IP-generator on this file and get a bunch of output files.

Example output files for generating the IP cms_sys_pll:

cms_sys_pll.cmp
cms_sys_pll.csv
cms_sys_pll.html
cms_sys_pll.spd
cms_sys_pll.xml
cms_sys_pll_generation.rpt
simulation\aldec\rivierapro_setup.tcl
simulation\cadence\cds.lib
simulation\cadence\cds_libs\pll_0.cds.lib
simulation\cadence\hdl.var
simulation\cadence\ncsim_setup.sh
simulation\cms_sys_pll.sip
simulation\cms_sys_pll.v
simulation\mentor\msim_setup.tcl
simulation\submodules\cms_sys_pll_pll_0.vo
simulation\synopsys\vcs\vcs_setup.sh
simulation\synopsys\vcsmx\synopsys_sim.setup
simulation\synopsys\vcsmx\vcsmx_setup.sh
synthesis\cms_sys_pll.debuginfo
synthesis\cms_sys_pll.qip
synthesis\cms_sys_pll.vhd
synthesis\submodules\cms_sys_pll_pll_0.qip
synthesis\submodules\cms_sys_pll_pll_0.v

So i tried to put this into a cutom target like so:

qsys = find_program('qsys-generate')
python = import('python').find_installation('python')

files = run_command(
  python, 'cat.py', files('ip/cms_sys_pll.outputs'),
).stdout().strip().split('\n')

custom_target('cms_sys_pll',
  build_by_default: true,
  output: files,
  input: 'ip/cms_sys_pll/cms_sys_pll.qsys',
  command: [
      qsys,
      '--output-directory=@OUTDIR@/cms_sys_pll',
      '--synthesis=VHDL',
      '--simulation=VERILOG',
      '@INPUT@']
)

And of course i ran into the error:

Output 'simulation\\aldec\\rivierapro_setup.tcl' must not contain a path segment.

Do you have any suggestions to work around this issue?

@jasonszang
Copy link
Author

jasonszang commented Feb 16, 2019

Well personally I decided to give up meson and went back to the good old CMake because of this one. And I find modern style CMake comfortable enough I stopped looking for another build system generator.
And unfortunately I am not aware of any universal work-around beside removing this limitation from meson.

@nussbrot
Copy link

Hi @jasonszang, thanks for your answer. I see this issue was first posted by you. If you don't mind i would like to ask you if you have experience with CMake and let's say "custom toolchains"?
But it is really off-topic and this is probably not the right place to ask you that.

@jpakkane
Copy link
Member

The question here is does the file really need to be in a cm_sys_pll subdirectory (some tools are nasty in this way). If no, then write it to @OUTDIR@. If yes, then you need to have the custom target declaration in the cm_sys_pll subdirectory (and also have just --outdir=@OUTDIR@. In Meson all outputs go to the build directory that corresponds to the source directory they are declared in. This simplifies debugging build problems, since you always know that files in build directory X must come from the corresponding source dir X.

@nussbrot
Copy link

Yeah, you're right, that is a mistake in my build file. I can give the tool any directory, so --outdir=@OUTDIR@ works fine. But i can not control the subfolders like synthesis\... or simulation\... that are created in @OUTDIR@.
So i can not add the resulting files as outputs since stuff like output: 'synthesis\cms_sys_pll.qip' does not work.

@ptsneves
Copy link
Contributor

@jasonszang I have the same frustration. One way to work around it is that you create a sub meson.buil in the subdir and do the remaining thing there. Then subdir the newly created meson.build. Not optimal but i think it works

@valpackett
Copy link
Contributor

valpackett commented Jul 10, 2019

gir-to-d also generates subdirectories, like

somelib/c/types.d
somelib/c/functions.d
somelib/Obj1.d
somelib/Thingy.d

Why is this ridiculous limitation still present? >_<

UPD: seems like there are workarounds in gir-to-d for this

Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Aug 28, 2019
Meson has a limitation[1] about generated artifacts being placed in
subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

[1] mesonbuild/meson#2320
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 2, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 3, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
rubenk added a commit to rubenk/collectd that referenced this issue Oct 13, 2019
Meson doesn't allow putting the generated header in a subdir of the builddir without jumping through hoops.
See mesonbuild/meson#2320 for more details.

So we move the header to the top level build dir and adjust some include paths.
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 17, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 18, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 25, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 25, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Oct 29, 2019
Summary:
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168

Reviewers: bu5hm4n, woohyun, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, brunobelo, felipealmeida, segfaultxavi

Tags: #efl, #do_not_merge

Maniphest Tasks: T8168

Differential Revision: https://phab.enlightenment.org/D9717
Enlightenment-bot pushed a commit to Enlightenment/efl that referenced this issue Nov 6, 2019
Instead of building with a patched meson version, make use of custom
targets and generated csproj files so we can used upstream meson
normally.

This avoids digging into "non official" dotnet stuff like calling
the CSC.dll directly that the patched meson tried to do.

To enable, run meson with `-Ddotnet=true`.

Regarding source file dependencies, Meson has a limitation[1]
about generated artifacts being placed in subdirectories.

In order to correctly track these generated artifacts for dotnet, we
generated them in the same folder as the csproj file through
`dotnet build -o`.

Instead of installing the dll like we do for mono, a nupkg is generated
and installed in the same folder as the dll would be
(<prefix>/lib/x86_64-linux-gnu/efl-mono-1)

To avoid messing around with Nupkg caches, we reference the source
project for the library directly instead of the nupkg when building the
test suite.

[1] mesonbuild/meson#2320

Fixes T8168
Differential Revision: https://phab.enlightenment.org/D9717
@eli-schwartz
Copy link
Member

@jpakkane if there's consensus that this is a good idea to explore / implement with the restrictions mentioned, I can take a stab at it. But I need to know that it clears the design hurdle first...

@anarazel
Copy link
Contributor

This ties into extending custom_targets that I have been thinking about. Maybe it would be useful to be able to define a custom target that is a an opaque directory rather than a file. Which means you could do something like this:

I think that'd be useful for custom targets that generate many files, e.g. some types of documentation generators. You can kind of do that today, but it doesn't work quite right everywhere today.

I suspect that this would also make more or less everyone who currently wants to create outputs in directories, happy. It easily allows conforming to existing limitations of existing codebases without creation of excessively nested meson.build files, permits some clever foreach looping of outputs, unlocks a few use cases such as @keith-packard's current experiment in #11954, etc.

It'd not make me entirely happy. If you have lots of small binaries for example, you might want to generate those in the directory corresponding to the source code, but without lots of redundant buildsystem code in each directory. The obvious way to implement that would be to have per-directory meson.build files that populate an array, and then higher up iterate over the array and generate the relevant targets. But the output-directory restriction doesn't allow that. Generating all the outputs in a top-level directory gets pretty messy, so also isn't really an option.

In Meson all outputs go to the build directory that corresponds to the source directory they are declared in. This simplifies debugging build problems, since you always know that files in build directory X must come from the corresponding source dir X.

The thing is, that I agree with that, as a goal. It's just that there's tension between a) the 1:1 source/build mapping b) buildsystem code duplication c) not allowing user defined functions. I think something gotta give.

Perhaps part of this concern could be addressed by allowing output into a subdir with a meson.build by having the subdir/meson.build doing something like output_here = current_directory() and then allowing targets above that directory to use that directory as the output? That'd allow to avoid duplication by creating targets in a higher up dir, while still leaving a trace in the build specification that something additional will define targets in the dir.

@keith-packard
Copy link

Perhaps part of this concern could be addressed by allowing output into a subdir with a meson.build by having the subdir/meson.build doing something like output_here = current_directory() and then allowing targets above that directory to use that directory as the output? That'd allow to avoid duplication by creating targets in a higher up dir, while still leaving a trace in the build specification that something additional will define targets in the dir.

That wouldn't help the case where the desired directory doesn't existing in the source tree (like the multilib setup). It would be nice to solve both the multilib issue and the subdir problem with the same mechanism, but if not, we can do them separately.

@eli-schwartz
Copy link
Member

I think that'd be useful for custom targets that generate many files, e.g. some types of documentation generators. You can kind of do that today, but it doesn't work quite right everywhere today.

Indeed, you can use output: 'htmldir' and then just have the documentation builder build into that -- the problem is that ninja can recognize a directory as output, but not recognize much about it other than that it was created at least once. I'm not sure how an "opaque directory" approach solves that problem at all though -- ninja still can't tell much of anything about it.

The obvious way to implement that would be to have per-directory meson.build files that populate an array, and then higher up iterate over the array and generate the relevant targets. But the output-directory restriction doesn't allow that. Generating all the outputs in a top-level directory gets pretty messy, so also isn't really an option.

Right, this is currently not possible and while my proposal would allow it as long as the filelist isn't generated by populating the array via per-directory meson.build files (I honestly don't see the problem with hoisting the list up, or just using fs.read('subdir/files.lst')), @jpakkane's would forbid it. I am not sure how important that restriction is, and am not particularly attached to the idea of restricting it, but it was suggested by @jpakkane, so...

by having the subdir/meson.build doing something like output_here = current_directory() and then allowing targets above that directory to use that directory as the output? That'd allow to avoid duplication by creating targets in a higher up dir, while still leaving a trace in the build specification that something additional will define targets in the dir.

I don't think that would really solve the concern, because you would not know what that additional thing would be. Moreover, would this allow you to create the output from anywhere? e.g. given a project with two subdirs "sub1" and "sub2", could I define a current_directory() and then have sub1 produce outputs in sub2 instead even though it isn't a parent but rather a sibling? (Even worse: what if it's a nephew? Or a granddaughter?)

Sure, you could forbid it to be anything other than a direct parent but then you could also use my original suggestion and not use sentinel "I know what I'm doing" types.

@anarazel
Copy link
Contributor

anarazel commented Aug 1, 2023

Right, this is currently not possible and while my proposal would allow it as long as the filelist isn't generated by populating the array via per-directory meson.build files (I honestly don't see the problem with hoisting the list up, or just using fs.read('subdir/files.lst'))

Hoisting the list up increases the likelihood of conflicts and also just leads to huge files. Using a different type of file that's then read with fs.read() doesn't really work, as there often are generated sources or such for a specific binary. It also leads to not having useful error messages and other such fun.

I don't think that would really solve the concern, because you would not know what that additional thing would be.

It'd at least be easier to search for...

Moreover, would this allow you to create the output from anywhere?

Sure. It's probably going to rarely be useful, but at some point restricting things makes them more complicated without a gain in clarity.

@eli-schwartz
Copy link
Member

Sure. It's probably going to rarely be useful, but at some point restricting things makes them more complicated without a gain in clarity.

Let's assume for a moment here that we're talking about a build system that does restrict things, and we're discussing possibilities for compromising and loosening the restrictions a bit.

If we do make that assumption, it seems plausible to say that 6 years of arguing "restricting things is pointless" and getting nowhere isn't going to change today because someone said "restricting things makes them more complicated without a gain in clarity".

But figuring out a way to restrict things that meets the original design goal of the person who enforced a restriction, while still unlocking at least some use cases, feels useful and is, at a minimum, better than before. That's what I, personally, am interested in thinking about.

@eli-schwartz
Copy link
Member

Using a different type of file that's then read with fs.read() doesn't really work, as there often are generated sources or such for a specific binary.

I can't imagine why it "wouldn't work". If there are generated sources for a specific binary, those could be generated alongside the target handling, and offloading the filelist to a dedicated file will still serve the purpose of greatly reducing the complexity, just not totally reducing it. A strict win over the current state of affairs, I would say.

It also leads to not having useful error messages and other such fun.

I'm baffled at why error messages would inherently become not useful as a result of this. In fact, I'm baffled at why error messages would lack usefulness at all. It's the same information either way.

@anarazel
Copy link
Contributor

anarazel commented Aug 1, 2023

Using a different type of file that's then read with fs.read() doesn't really work, as there often are generated sources or such for a specific binary.

I can't imagine why it "wouldn't work". If there are generated sources for a specific binary, those could be generated alongside the target handling, and offloading the filelist to a dedicated file will still serve the purpose of greatly reducing the complexity, just not totally reducing it. A strict win over the current state of affairs, I would say.

Sure, that's possible - but you end up with a lot of buildsystem code in a single file...

It also leads to not having useful error messages and other such fun.

I'm baffled at why error messages would inherently become not useful as a result of this. In fact, I'm baffled at why error messages would lack usefulness at all. It's the same information either way.

If I have a syntax error in meson.build, I am helpfully pointed at the specific line. If there's an error in a file read by fs.read(), that's not typically going to be the case.

You're likely going to have to build a bit of parsing logic in meson (e.g. filtering out comment lines). Not impossible, but also not exactly optimal.

@anarazel
Copy link
Contributor

anarazel commented Aug 1, 2023

Sure. It's probably going to rarely be useful, but at some point restricting things makes them more complicated without a gain in clarity.

Let's assume for a moment here that we're talking about a build system that does restrict things, and we're discussing possibilities for compromising and loosening the restrictions a bit.

I wasn't advocating dropping all restrictions in my comment - I was purely commenting on the parent vs sibling vs nephew aspect.

But figuring out a way to restrict things that meets the original design goal of the person who enforced a restriction, while still unlocking at least some use cases, feels useful and is, at a minimum, better than before. That's what I, personally, am interested in thinking about.

I agree.

@eli-schwartz
Copy link
Member

If I have a syntax error in meson.build, I am helpfully pointed at the specific line. If there's an error in a file read by fs.read(), that's not typically going to be the case.

For lexer errors, yes. Anything else, e.g. invalid kwargs or disallowed values, we just point at the line containing the primary operation (usually the function call itself).

You're likely going to have to build a bit of parsing logic in meson (e.g. filtering out comment lines). Not impossible, but also not exactly optimal.

... and there we have it, I viewed the fs.read() thing as a way to build better lists of strings, not a way to offload a DSL and conditional logic to a new file. :D

@jpakkane
Copy link
Member

jpakkane commented Aug 1, 2023

Indeed, you can use output: 'htmldir' and then just have the documentation builder build into that -- the problem is that ninja can recognize a directory as output, but not recognize much about it other than that it was created at least once. I'm not sure how an "opaque directory" approach solves that problem at all though -- ninja still can't tell much of anything about it.

It can treat it like a file output, because if you write something in the subdir itself, the mtime of the directory changes to that value (I think on all platforms but not really sure). But it requires that whatever command generates the output writes something in the top level dir on incremental builds. Otherwise Ninja will run the target again next time.

@zasdfgbnm
Copy link
Contributor

Upvoting this. Was trying to move away from cmake to meson, but eventually give up due to this.

@jpakkane
Copy link
Member

What is the exact thing that you needed to do but couldn't?

@zasdfgbnm
Copy link
Contributor

What is the exact thing that you needed to do but couldn't?

I want a soft switch from cmake to meson. For our cmake build, we are using a Python script to convert things like runtime/a.cu into resources/a.h. I want meson to do the same so that #include <resources/a.h> works. I do want the resources/ prefix, because this is better code organization than #include <a.h>.

@jpakkane
Copy link
Member

For this specific case you can create a resources subdirectory and have the custom target to do that in that dir's meson.build file. You can either first recurse to the runtime dir and do a files there or just directly specify the source files as '../runtime/a.cu`.

@zasdfgbnm
Copy link
Contributor

For this specific case you can create a resources subdirectory and have the custom target to do that in that dir's meson.build file. You can either first recurse to the runtime dir and do a files there or just directly specify the source files as '../runtime/a.cu`.

This workaround is not perfect, but sounds acceptable. Thanks for the suggestion!

@gabeblack
Copy link

There's been several occasions where I've had to wrap my custom_target with a script that copies the generated output in to the custom_target's working directory so that it meet's the custom_target's requirement of the output field not containing a path or subdirectory.

I've been porting the building of source rpms to building with meson. As you might know, source rpms have a structure of rpmbuild_dir/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}. The final installation of building the source goes in the BUILDROOT folder often with a structure matching what one would see in their filesystem (libs go in BUILDROOT/<pkg>/usr/lib64 and headers in BUILDROOT/<pkg>/usr/include).

However with meson's custom_target, I'm forced to copy/flatten the generated directory structure's include files and libraries in to the directory where my meson.build's custom_target lives because of the 'no subdirectory' requirement.

I guess this just reeks of a code smell -- but I'm getting convinced that it isn't in our meson.build files, but rather meson's design in this area. I like the restrictions that meson does in other areas as I think it helps you re-think your design and structure to make sure you do things correctly and cleanly. However, in respect of having to port third-party code generators and now rpmbuild... I think this restriction is actually a code-smell in meson instead. If we could just simply specify our outputs in the outputs attribute of custom_target, we wouldn't have to add wrapper scripts to code generators (to copy the outputs in the flat directory structure). We wouldn't have to have copies laying of libraries, sources, and header files at all! The code smell is having to do the copies...

I like how meson tries to prevent you from having to 'hack' the build system to get it to work, but I think having to 'hack' rpmbuild and other tools to flatten out all their outputs to the calling custom_target directory is just as bad.

I admit I may not be aware of a meson feature that allows me to do what I'm doing, so I'd love to hear suggestions of meson features that would let me work around having to copy around the output of building source rpms to be able to have those headers and libraries used as part of other build targets.

As you can see from other comments in this thread, the restriction doesn't just apply to rpmbuild, but there are many other code generators and other external build tools that create outputs in a nice, clean subdirectory structure ready for user consumption. Meson should not be requiring that clean structure to be flattened or that directory structure to be pre-existing (so you can put meson.build files in them) in order to be consumed.

@eli-schwartz
Copy link
Member

As you can see from other comments in this thread, the restriction doesn't just apply to rpmbuild, but there are many other code generators and other external build tools that create outputs in a nice, clean subdirectory structure ready for user consumption.

My interest here is for code generators and external build tools that require structures as part of their design intrinsics, such as generated headers which must match the #include <...> structure (solvable with a specific variant of generator() that doesn't apply to non-C-headers). And more generally things like rust or python require specific output structures in order to be usable, even at compile time.

Producing a declare_dependency() which is interchangeable with the installed version is also important, mainly for external wraps where you cannot just arbitrarily re-order public headers into the common include/* & src/ pattern because you don't own that structure.

However I'm not certain I understand the case for rpmbuild. For rpmbuild, you should be invoking meson install --destdir=BUILDROOT/<pkg>/ for example using these rpmbuild macros:

meson/data/macros.meson

Lines 1 to 45 in 5e76e2a

%__meson %{_bindir}/meson
%__meson_wrap_mode nodownload
%__meson_auto_features enabled
%meson \
%set_build_flags \
%{shrink:%{__meson} setup \
--buildtype=plain \
--prefix=%{_prefix} \
--libdir=%{_libdir} \
--libexecdir=%{_libexecdir} \
--bindir=%{_bindir} \
--sbindir=%{_sbindir} \
--includedir=%{_includedir} \
--datadir=%{_datadir} \
--mandir=%{_mandir} \
--infodir=%{_infodir} \
--localedir=%{_datadir}/locale \
--sysconfdir=%{_sysconfdir} \
--localstatedir=%{_localstatedir} \
--sharedstatedir=%{_sharedstatedir} \
--wrap-mode=%{__meson_wrap_mode} \
--auto-features=%{__meson_auto_features} \
%{_vpath_srcdir} %{_vpath_builddir} \
%{nil}}
%meson_build \
%{shrink:%{__meson} compile \
-C %{_vpath_builddir} \
-j %{_smp_build_ncpus} \
--verbose \
%{nil}}
%meson_install \
%{shrink:DESTDIR=%{buildroot} %{__meson} install \
-C %{_vpath_builddir} \
--no-rebuild \
%{nil}}
%meson_test \
%{shrink:%{__meson} test \
-C %{_vpath_builddir} \
--num-processes %{_smp_build_ncpus} \
--print-errorlogs \
%{nil}}

@gabeblack
Copy link

gabeblack commented Jan 25, 2024

However I'm not certain I understand the case for rpmbuild. For rpmbuild, you should be invoking meson install --destdir=BUILDROOT// for example using these rpmbuild macros:

I was unclear, I apologize. I was giving an example of using rpmbuild to build the source code of certain packages, not to use it to generate an rpm. By defining _topdir you can use it to compile a source rpm and stage and install it in a temporary directory. You can then point other projects to those generated includes/libraries -- all within a folder that is temporary (i.e. without installing the rpm in the host environment (often requiring sudo privileges)).

It was probably a bad example, but I'm finding more and more third-party tools that stage built libraries and headers in a directory structure. AFAIK, meson doesn't allow you to consume it without copying the files to either pre-existing folders, or to copy the files to the custom_target folder.

Am I mistaken? Do you know if there any plans to allow output to allow subdirectories?

@eli-schwartz
Copy link
Member

It was probably a bad example, but I'm finding more and more third-party tools that stage built libraries and headers in a directory structure. AFAIK, meson doesn't allow you to consume it without copying the files to either pre-existing folders, or to copy the files to the custom_target folder.

Meson does provide import('pkgconfig').generate(mylib) which generates pkg-config files for installation e.g. in installable rpms. This function also automatically creates "-uninstalled.pc" files which can be used by adding builddir/meson-uninstalled/ to your $PKG_CONFIG_PATH -- pkg-config will pick "uninstalled" projects up first, and emit compile/link flags for the built libraries in the builddir and the headers in the source/build dir. This usually works pretty well.

Am I mistaken? Do you know if there any plans to allow output to allow subdirectories?

I would like to allow it for the reasons, and using the approach, which I outlined in #2320 (comment) and which @jpakkane tweaked in #2320 (comment)

I would like to know from @jpakkane if that is an acceptable approach and worth my investing time to implement it.

@gabeblack
Copy link

Personally, I like what you have outlined in #2320 (comment) much better. It is more intuitive. People on this thread are already naturally looking to add paths to the output argument. IMO, if the API is natural where you do it without looking up docs, then it is a good API.

Having another argument like is_opaque_directory is not very intuitive and adding other accessor methods on top of that I think would require much time for us consumers looking at documentation trying to figure out what that all means.

That is my opinion though, and I respect your decisions as you are the ones maintaining and know better the side-effects of all this.

I wouldn't mind putting in some skin in the game and helping implement the addition.

@intractabilis
Copy link

intractabilis commented Feb 15, 2024

Here is another scenario where I think I need paths in the custom_target's output argument. I am seeking a way to generate C++ bindings for GTK on the fly. The tool, cppgir, generates hundreds of files for a single library. For example, for GTK4, I would want C++ bindings in the build tree's gi directory:

cppgir --output gi --class Gtk-4.0

This command will generate 1536 files. I need to compile the following generated files out of these 1536:

  • gi/cairo/cairo.cpp
  • gi/freetype2/freetype2.cpp
  • gi/gdk/gdk.cpp
  • gi/gdkpixbuf/gdkpixbuf.cpp
  • gi/gio/gio.cpp
  • gi/glib/glib.cpp
  • gi/gmodule/gmodule.cpp
  • gi/gobject/gobject.cpp
  • gi/graphene/graphene.cpp
  • gi/gsk/gsk.cpp
  • gi/gtk/gtk.cpp
  • gi/harfbuzz/harfbuzz.cpp
  • gi/pango/pango.cpp
  • gi/pangocairo/pangocairo.cpp

How do I pass these files as sources to a library function? They don't exist during the setup stage, so library fails if I specify direct paths to these files.

@slavaandrejev
Copy link

Here is my workaround for this issue that generates GTK C++ bindings. It isn't lovely. It involves copying the generated source files into one directory, generating a dummy header file, and using directories as an output of custom_target.

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