ninja-build / ninja

a small build system with a focus on speed
https://ninja-build.org/
Apache License 2.0
11.22k stars 1.6k forks source link

Relative subninja paths don't resolve #977

Open Qix- opened 9 years ago

Qix- commented 9 years ago

I would attempt a PR for this, but I'm not entirely sure this is a bug or if it's intended.

When including a subninja, I get

ninja: error: 'mod.c', needed by 'mod.o', missing and no known rule to make it

although running ninja directly in that directory works just fine. Under the suspicion that relative paths may be to blame, I copied the project structure to a new directory, prepended each input/output with the absolute path, and ran ninja from the parent directory (containing the build.ninja that subninja'd the module). It built just fine.

Is this suggesting we should be generating our ninja configurations with absolute paths? This has a lot of problems, along with the fact not every generator I've seen does this. Further, the documentation doesn't make a distinction, and Ninja runs just fine otherwise.

I want to err on the side of it's a bug, but I feel like a PR that does runtime path canonicalization might introduce a performance hit (though I'm waving my hands here without any benchmarks to back up that suspicion).

nico commented 9 years ago

All paths are relative to the build directory, not to the file containing the subninja line.

Qix- commented 9 years ago

That makes no sense, though. That means generators are going to have to all implement some way to prepend a prefix to the files, which means changing the working directory commands run in (which may change the original intent of the build configuration depending on how the generator/commands run).

What, then, are contrasting use cases for subninja vs include?

A subninja with paths relative to the build file would be useful in that depdencies that are configured to run within their own directory can do so without having to modify their paths, but can still contribute to the dependency graph of the parent ninja configuration.

include functionality would be unmodified, and would act exactly how subninjas act other than the fact rule names would now be in a combined namespace (as per #921). Currently, subninja and include achieve essentially the same thing other than scoping variables and rule names...

nico commented 9 years ago

The idea is that the generator generates all .ninja files, so they can write paths relative to the build directory. It's an interesting idea to combine ninja files generated by different generators (it sounds like that's what you want to do?), but that's not something that's supported at the moment.

Correct, the difference between subninja and include is that the former adds a scope and the latter doesn't. The use case for this is that the toplevel ninja can define common build rules such as cc that reference variables such as cflags, and each subninja can set cflags to what's appropriate for that target, and there can be one-off actions in there. To see an example, you can run python misc/write_fake_manifests.py /tmp/foo` to write a bunch of .ninja files to /tmp/foo that use this pattern.

Qix- commented 9 years ago

That makes sense, though I still don't see much of a benefit (other than scope).

It's an interesting idea to combine ninja files generated by different generators (it sounds like that's what you want to do?)

Exactly. Being able to include Ninja itself as a submodule for my generator, and then another CMake project (which is configured to output Ninja build files), and then generating the Ninja config files for both and including them as subninjas in my project's build.ninja file in order to be able to build them as if they were on their own, but still allow my project to use their outputs (and thus their dependency graphs) in order to build the whole project at once.

If that makes sense. Immediate use-case that I see with my generator in particular is that it's borrowing a few concepts from Tup (which IIRC influenced some design decisions within Ninja itself) in that I can include N subprojects, all with their own build.ninja files, and then tap into their graphs to allow me to automatically construct a much larger dependency graph.

I personally think that'd make subninja a whole lot more useful, though I could see it being a potentially breaking change. However, I don't see a way around this unless 1) I modify the dependencies' build.ninja files with a patch or 2) sacrifice the ability to harness the dependencies' dependency graphs.

Thoughts?

Qix- commented 9 years ago

How about:

subninja path/to/build.ninja relative path/to

and an absent relative defaults to ./. That would make it non-breaking but still give the functionality if a generator so desires.

Or, following in the steps of other Ninja constructs, maybe

subninja path/to/build.ninja
  relative = path/to
evmar commented 9 years ago

Suppose you have a project at .../foo and it has a subdirectory bar, and that Ninja had the relative-path logic you suggest.

If your build system wants to write all build outputs to /foo/obj, a subninja in /foo/bar that used directory-relative paths would need to know to write its output into ../obj/bar, as that's the path to the file from that subdirectory. So whatever is generating your build.ninja files must already be aware of the global path hierarchy, in which case making all paths relative is effectively the same problem as prepending a bar/ to the paths in the bar/ directory.

Maybe there are enough people who write build output in their source directories that the above doesn't matter, though. I mostly hear from people who want even stronger separation -- like the people who sent patches to Ninja to make it so they can build Ninja with the build output in a totally unrelated directory.

Qix- commented 9 years ago

I'm not sure I understand. Subninjas, in the use-case I've described in particular, don't normally know about the parent ninja's structure (at least, don't need to know). I'm sure someone out there could find a case where they do, though.

The problem I'm facing right now is dependencies (third party libraries, etc.) that don't use my generator (which is 100% of them) currently need to be built using a bootstrapper, and have to be bootstrapped every time they're updated or changed, etc. Most generators I work with have the options to output to a ninja configuration, but they're all configured for that directory alone (i.e. relative to that directory).

Being able to perform selective rebuilds using their configurations and graphs would be huge. Currently, I cannot do this due to the fact subninja assumes paths relative to the build directory.

nico commented 9 years ago

Evan: The way I understood this is that you'd create a tree like this:

builddir
  subbuild1
  subbuild2

and since generators usually support putting the build dir in arbitrary places, building project 1 in subbuild1 and project 2 in subbuild 2 should work. Then there's a toplevel ninja file in builddir that Qix- wants to drive building the subprojects.

Unrelated: this relative feature also has to change the cwd to its argument if any rules depend on the current directory being equal to their build dir (say, if a rule strips built artifacts or something).

crazydef commented 1 year ago

I'm hitting this problem now with Ninja and Premake. All of my libraries exist as their own projects in entirely unrelated directories which I pull into which ever application is being built at the time.

So an example directory structure for my libraries looks something like this:

Core
  Gwee
    Build
      gwee.lua
    <gwee sources>
  Carbon
    Build
      carbon.lua
    <carbon sources>
  etc...

And a project which uses some of those libraries would have something like this:

Project
  Build
    premake5.lua
    build.py
  Engine
    Build
      engine.lua
    <engine sources>
  App
    Build
      app.lua
    <app sources>

So premake5.lua includes engine.lua, app.lua, and whichever libraries it needs, such as gwee.lua.

The build script build.py runs Premake in the Build directory, and (on Linux) calls Ninja. Or rather, attempts to call Ninja. (On other platforms building is performed in the relevant IDE - Xcode, Visual Studio, etc.)

The Premake Ninja generator correctly produces .ninja files next to each lua project file in their respective directories, and the build.ninja file in Project/Build correctly references them with relative paths.

But none of them actually build because subninja doesn't change to the directory containing the actual ninja file.

So how is this supposed to work?

evmar commented 1 year ago

All relative paths in Ninja are interpreted as relative to the directory you run Ninja from (which you can set with the -C flag).

On Sun, Jul 9, 2023 at 7:46 AM James @.***> wrote:

I'm hitting this problem now with Ninja and Premake. All of my libraries exist as their own projects in entirely unrelated directories which I pull into which ever application is being built at the time.

So an example directory structure for my libraries looks something like this:

Core Gwee Build gwee.lua

Carbon Build carbon.lua etc... And a project which uses some of those libraries would have something like this: Project Build premake5.lua Engine Build engine.lua App Build app.lua So premake5.lua includes engine.lua, app.lua, and whichever libraries it needs, such as gwee.lua. The Premake Ninja generator correctly produces .ninja files next to each lua project file in their respective directories, and the build.ninja file in Project/Build correctly references them with relative paths. But none of them actually build because subninja doesn't change to the directory containing the actual ninja file. So how is this supposed to work? — Reply to this email directly, view it on GitHub , or unsubscribe . You are receiving this because you commented.Message ID: ***@***.***>
crazydef commented 1 year ago

I'm running Ninja from the Build directory of the main project. But that ninja file just runs SubNinja for a bunch of other generated ninja files. The individual ninja files for each library are built with their internal paths relative to themselves. Hence the above compile error of not being able to find the source files being compiled.

Each project that I have uses Premake and pulls in the libraries it needs as other Premake lua files so that the generated files for any given project are self contained in a single build script. (Or in the case of Xcode/Visual Studio a single workspace/solution that has all libraries needed for that project.) But crucially, the project files (and ninja files) for libraries are located within each library's Build directory.

Basically, I end up with:

Core
  Gwee
    Build
      gwee.lua
      build_Gwee_Debug_x64.ninja
      build_Gwee_Release_x64.ninja
  Carbon
    Build
      carbon.lua
      build_Carbon_Debug_x64.ninja
      build_Carbon_Release_x64.ninja
  etc...

Project
  Build
    premake5.lua
    build.ninja
    build.py
  Engine
    Build
      engine.lua
      build_Engine_Debug_x64.ninja
      build_Engine_Release_x64.ninja
  App
    Build
      app.lua
      build_App_Debug_x64.ninja
      build_App_Release_x64.ninja

Where build.ninja just consists of:

# solution build file
# generated with premake ninja

# build projects
subninja ../../Core/Carbon/Build/build_Carbon_Debug_x64.ninja
subninja ../../Core/Carbon/Build/build_Carbon_Release_x64.ninja
subninja ../../Core/Gwee/Build/build_Gwee_Debug_x64.ninja
subninja ../../Core/Gwee/Build/build_Gwee_Release_x64.ninja
subninja ../Engine/Build/build_Engine_Debug_x64.ninja
subninja ../Engine/Build/build_Engine_Release_x64.ninja
subninja ../App/Build/build_App_Debug_x64.ninja
subninja ../App/Build/build_App_Release_x64.ninja

# targets
build Debug: phony Carbon_Debug_x64 Gwee_Debug_x64 Engine_Debug_x64 App_Debug_x64
build Release: phony Carbon_Release_x64 Gwee_Release_x64 Engine_Release_x64 App_Release_x64

# default target
default App_Debug_x64
crazydef commented 1 year ago

Is running subninja from the directory that contains the referenced ninja file all that is needed?

evmar commented 1 year ago

On Sun, Jul 9, 2023 at 10:50 AM James @.***> wrote:

I'm running Ninja from the Build directory of the main project. But that ninja file just runs SubNinja for a bunch of other generated ninja files. The individual ninja files for each library are built with their internal paths relative to themselves. Hence the above compile error of not being able to find the source files being compiled.

Ninja doesn't support the idea of composing multiple different projects with different notions of the source root. It assumes all your build.ninja files are generated under the same global view of paths.

Message ID: @.***>

evmar commented 1 year ago

On Sun, Jul 9, 2023 at 11:12 AM Evan Martin @.***> wrote:

On Sun, Jul 9, 2023 at 10:50 AM James @.***> wrote:

I'm running Ninja from the Build directory of the main project. But that ninja file just runs SubNinja for a bunch of other generated ninja files. The individual ninja files for each library are built with their internal paths relative to themselves. Hence the above compile error of not being able to find the source files being compiled.

Ninja doesn't support the idea of composing multiple different projects with different notions of the source root. It assumes all your build.ninja files are generated under the same global view of paths.

...er sorry, I misunderstood. It looks like your project does this already. Yes, I think you just need to invoke Ninja from the right initial directory (or use -C).

Message ID: @.***>

crazydef commented 1 year ago

I'm running Ninja from the Build directory that contains the build.ninja file. Looking at the individual ninja files, it does look like their paths are correct, but things just aren't working. (And having looked inside the individual ninja files, them having paths relative to the current project isn't what I want or need anyway because that won't work for other projects.)

So rewriting my entire build system from scratch so it works on Linux isn't something I'm prepared to do, so maybe Ninja isn't going to work for me.

evmar commented 1 year ago

In general, the Ninja behavior here is fairly straightforward ("all paths in all files must be relative to the same directory"), which means any complexity/configuration here is managed by premake. You might have more success asking a premake list.

On Sun, Jul 9, 2023 at 12:03 PM James @.***> wrote:

I'm running Ninja from the Build directory that contains the build.ninja file. Looking at the individual ninja files, it does look like their paths are correct, but things just aren't working. (And having looked inside the individual ninja files, them having paths relative to the current project isn't what I want or need anyway because that won't work for other projects.)

So rewriting my entire build system from scratch so it works on Linux isn't something I'm prepared to do, so maybe Ninja isn't going to work for me.

— Reply to this email directly, view it on GitHub https://github.com/ninja-build/ninja/issues/977#issuecomment-1627798086, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAA6BYALRC7AQE3EOGWFFDXPL57VANCNFSM4BJAOQYA . You are receiving this because you commented.Message ID: @.***>

crazydef commented 1 year ago

Yeah, it's always tricky to know exactly where the responsibility falls when you're dealing with issues like these. :)

tritao commented 1 month ago

@crazydef In case you are still looking for a fix for this: https://github.com/jimon/premake-ninja/pull/33