hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler
Other
5.24k stars 224 forks source link

feat: Add command `-cwd` (change working directory) #1024

Closed helmesjo closed 1 week ago

helmesjo commented 3 months ago

Makes it possible to have cppfront output generated files into a separate directory (unless -output is specified).

Before:

$ ls ..
../hello.cpp2
$ cppfront ../hello.cpp2
$ ls ..
../hello.cpp2
../hello.cpp

After (when using -cwd):

$ ls
./hello.cpp2
subdir/
$ cppfront -cwd ./subdir ./hello.cpp
$ tree
.
├── hello.cpp2
└── subdir
    └── hello.cpp

Side note:

Arguably the output path should always be the current working directory with option to retain relative sub-directory hierarchy, eg. when passing multiple files (this is how most tools operate), and not be derived from input files. Though I assumed that would break tests (and require more special handling/options).

So running cd build && cppfront [options] $(find . -name "*.h2" -o -name "*.cpp2") with this structure:

$ tree
├── one.cpp2
├── one.h2
└── subdir1
    ├── subdir2
    │   ├── three.cpp2
    │   └── three.h2
    ├── two.cpp2
    └── two.h2
$ mkdir build && cd build && cppfront [options] $(find . -name "*.h2" -o -name "*.cpp2")

could yield this result:

$ tree
├── build
│   ├── one.cpp
│   ├── one.h
│   └── subdir1
│       ├── subdir2
│       │   ├── three.cpp
│       │   └── three.h
│       ├── two.cpp
│       └── two.h
├── one.cpp2
├── one.h2
└── subdir1
    ├── subdir2
    │   ├── three.cpp2
    │   └── three.h2
    ├── two.cpp2
    └── two.h2
hsutter commented 3 months ago

Thanks for your pull request! It looks like this may be your first contribution to cppfront. I've emailed you the Contributor License Agreement (CLA), and once it's signed I can look at your pull request. Thanks again for your contribution.

helmesjo commented 3 months ago

Thanks for your pull request! It looks like this may be your first contribution to cppfront. I've emailed you the Contributor License Agreement (CLA), and once it's signed I can look at your pull request. Thanks again for your contribution.

Sure thing! Do you mind re-sending it though? My email got a bit mixed up so you sent it to the wrong one.

hsutter commented 3 months ago

Thanks! This is new to me as I'm only familiar with changing the working directory in shell scripts before invoking the compiler, so I have some questions...

Can you point me to a similar existing flag on another compiler I could take a look at to understand the prior art better? Then I could look at examples and what other compilers do for this feature.

A question about this example:

After (when using -cwd):

$ ls
./hello.cpp2
subdir/
$ cppfront -cwd ./subdir ./hello.cpp
$ tree
.
├── hello.cpp2
└── subdir
    └── hello.cpp

I'm slightly confused... cppfront -cwd ./subdir ./hello.cpp doesn't work for me in this branch.

Is that intended to be cppfront -cwd ./subdir ../hello.cpp2 ? I do get the above result with that.

Today I can write cppfront -o subdir/hello.cpp hello.cpp2... that seems to have the same effect, if I understand right? I think what you're saying is that the -cwd approach lets you avoid repeating part of the filename?

mkdir build && cd build && cppfront [options] $(find . -name "*.h2" -o -name "*.cpp2")

I tried variations of this but wasn't able to get this to work in bash?

Thanks for your patience with all the questions! It's just because it's unfamiliar to me so I want to be sure I understand. Thanks again for the suggestion!

helmesjo commented 3 months ago

Thanks! This is new to me as I'm only familiar with changing the working directory in shell scripts before invoking the compiler, so I have some questions...

Can you point me to a similar existing flag on another compiler I could take a look at to understand the prior art better? Then I could look at examples and what other compilers do for this feature.

Agree (and I do the same), the crux here though is that cppfront will output the file next to the .h2|.cpp2 input file being processed (and ignore my current working directory). And to elaborate on my situation I'm facing, I've packaged cppfront (although not completely finished but all regression-tests pass) for build2 during the "config phase" (in the make world) I first extract which files cppfront will generate and feed this to build2. And preferably I'd like to pass all files in one go (I'll probably try to add a PR to add a "list files that would be generated" so you don't actually have to generate them in the first pass, only during build). For things to work out afterwards when building the generated cpp1 files (with #includes matching the expected structure, eg. #include <subdir/header.hpp>) the output would need to be (or at least it would make sense to be) in the same hierarchy (but potentially in another build directory, out-of-source).

But (as evident from my many edits to the description for this PR), as I was adding it I got a feeling this middle-ground implementation of touching as little as possible but cover my specific use-case probably is redundant and I'll look into changing it to be, as you said, just cd before invoking cppfront while still deadling with any number of input files (which AFAICS is not the case currently, but instead cppfront will output each cpp1 file next to the cpp2 one). Again, assuming I'm passing multiple cpp2 files at once and thus can't use the -output option:

-output filename, -o filename Output to 'filename' (can be 'stdout'). If not set, the default output filename for is the same as the input filename without the 2 > (e.g., compiling hello.cpp2 by default writes its output to hello.cpp, and header.h2 to header.h).

Long story short, let's keep this as "Work In Progress" and I'll ping when I have cooked something up. :)

hsutter commented 3 months ago

Actually, I think you've identified a real issue that should be easy to fix: Compilers generally put their output in the current directory. Cppfront should, too.

When I run:

$ cppfront ../demo.cpp2
$ g++ ../demo.cpp

the first line puts the output file in the parent directory, the second in the current directory.

So I think cppfront should just put files in the current directly by default, like all compilers do.

@JohelEGP / @MaxSagebaum / everyone: Would making that change destabilize any of you, such as this Godbolt environment ?

helmesjo commented 3 months ago

Actually, I think you've identified a real issue that should be easy to fix: Compilers generally put their output in the current directory. Cppfront should, too.

When I run:

$ cppfront ../demo.cpp2
$ g++ ../demo.cpp

the first line puts the output file in the parent directory, the second in the current directory.

So I think cppfront should just put files in the current directly by default, like all compilers do.

@JohelEGP / @MaxSagebaum / everyone: Would making that change destabilize any of you, such as this Godbolt environment ?

Yep, that was my plan as well. Especially useful when you want cppfront to handle all naming of files (but still put them relative to cwd).

MaxSagebaum commented 3 months ago

@JohelEGP / @MaxSagebaum / everyone: Would making that change destabilize any of you, such as this Godbolt environment ?

I usually prefer the -o option. But, I see the merit to align it to the standard behavior. The cwd option seems a good idea.

hsutter commented 2 months ago

Picking this up again after the April madness:

I think we ended up agreeing that ideally cppfront would put files in the current directory by default. Is that right?

My main question before doing that is whether it would destabilize the Godbolt environment linked above, or similar uses?

Thanks again for pointing out this area needs improvement, however we end up improving it!

helmesjo commented 2 months ago

Picking this up again after the April madness:

I think we ended up agreeing that ideally cppfront would put files in the current directory by default. Is that right?

My main question before doing that is whether it would destabilize the Godbolt environment linked above, or similar uses?

Thanks again for pointing out this area needs improvement, however we end up improving it!

My suggestion was to output to the current working directory, though mirror the input tree for the output(s). If relative includes are used that approach wouldn't interfere (maybe this was what you meant?).

jarzec commented 1 month ago

For what it's worth I would suggest to avoid this kind of situation and go for what compilers already do as much as possible. Sticking to what GCC and Clang do would be along the lines of one of the funding ideas behind cppfront: making tooling simpler (or in this case not more complicated). Creating project files, like CMakeLists.txt, will me much easier without special cases for cppfront.

helmesjo commented 3 weeks ago

For what it's worth I would suggest to avoid this kind of situation and go for what compilers already do as much as possible. Sticking to what GCC and Clang do would be along the lines of one of the funding ideas behind cppfront: making tooling simpler (or in this case not more complicated). Creating project files, like CMakeLists.txt, will me much easier without special cases for cppfront.

I agree, though just to highlight the difference here: cppfront generates only an intermediate (source) step which then needs to be compiled, which in turn requires the previous structure to work properly (otherwise only simple "hello.h" kind of includes are supported and not eg. "libhello/utils/hello.h" or my preferred <libhello/utils/hello.h>, unless one manually moves files around afterwards). Compare this to generated object files that don't care about directory hierarchy. Given this (and that we want to stay similar to GCC which disregards the input file(s) hierarchy & just outputs to the cwd), is an option to add a flag -preserve-hierarchy (or similar) to achieve this?

I imagine that a useful and easy to grasp approach could be that:

  1. -preserve-hierarchy: Input file(s) full path is preserved, and cppfront only changes extension (drops the 2).
  2. -output-dir: Specifies the root-dir for all generated output. Default is cwd (if 1. is set and the input file path(s) are absolute paths then this flag naturally has no effect).

This way the behavior should remain the same (all tests are unaffected). But I can still (opt-in) achieve this:

// hello.h2
#include <subdir/world.h>
// ..
$ tree
.
├── hello.cpp2
├── hello.h2
└── subdir
    └── world.h2
$ cppfront -preserve-hierarchy -output-dir /tmp/test ./hello.cpp2 ./hello.h2 ./subdir/world.h2
$ tree /tmp/test
/tmp/test/
├── hello.cpp
├── hello.h
└── subdir
    └── world.h
# compile ...

Basically: The include written in hello.h2 would work as expected (IMO). That is, I wouldn't have to spend cognitive load on figuring out intrinsics about cppfront, the result "just works".

I'll experiment a bit but please chip in with feedback.

@hsutter I noticed that (AFAICS) there are no tests exorcising includes. I guess that would be a good addition regardless of this PR?

hsutter commented 1 week ago

Thanks! @helmesjo when you get a chance would you please check that the commit I pushed onto this PR before merging still keeps the behavior that you wanted?

helmesjo commented 1 week ago

@hsutter Will do, and sorry for the delay!

hsutter commented 1 week ago

No worries, you've been waiting on me for a while! So have others, gradually working my way through the backlog...