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

Replace generated include folder with src-gen/include #7

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions 0000-user-facing-include-path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
- Start Date: 2024-02-29
- RFC PR: [lf-lang/rfcs#7](https://github.com/lf-lang/rfcs/pull/7)
- Tracking Issue(s): [lf-lang/lingua-franca#2203](https://github.com/lf-lang/lingua-franca/issues/2203)

# Abstract
[abstract]: #abstract

Do not put generated headers in the project root. Instead, put them in the src-gen directory, where all the other generated files are.

# Motivation
[motivation]: #motivation

Generating files outside of the src-gen directory is a surprising behavior because users may expect that files generated by the LF code generator will all be placed in the `src-gen` and `bin` directories. In particular, if the user is already maintaining a directory called `include` that contains source files, then the current behavior will cause that directory to be overwritten or even deleted, which in the worst case can result in lost work.

# Proposed Implementation
[proposed-implementation]: #proposed-implementation

The proposed solution is to generate the headers in `src-gen/include` only. Because user-provided C files will be included directly in `${LF_MAIN_TARGET}`, this directory will be on the include path for user-provided C files. Here is an example of how an `#include` directive might look in a user-provided C file given the proposed new behavior:

```c
#include "Count/Count.h"
```

For completeness, this is for a C file that is included in the LF project using the following target specification:

```
target C {
cmake-include: ["../../c/count.cmake"],
files: ["../../c"]
}
```

And the following contents of `count.cmake`:

```cmake
target_sources(${LF_MAIN_TARGET} PRIVATE c/count.c)
```

Note the lack of any `target_include_directories` statements in `count.cmake`. This shows how the generated CMakeLists.txt takes care of putting the generated headers on the include path.

# Drawbacks
[drawbacks]: #drawbacks

One potential drawback of this proposal is that users may not be fully aware of the user-facing include directory, or it may not be as clear that the interfaces defined there are considered stable and safe to depend on. Putting both user-facing and non-user-facing files in `src-gen` could blur the lines between the two kinds of files. This can be addressed by carefully documenting which files are safe to depend on.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

One alternative is to put these headers in a sibling directory of the `src-gen` directory called `include-gen`, and to either add that directory to the include path or copy it into src-gen and put the copied directory on the include path. The disadvantage of this alternative is that it violates the principle that generated files will go in src-gen; this principle is simple and easy to explain, so it is worth keeping.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another take is that a directory that ends with -gen (such as src-gen but also fed-gen) contains generated code. If that is the principle, then this alternative solution is not a departure from it. I kind of like the idea of keeping things that are user-facing in separate directory.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we actually have fed-gen? Sure, we could have multiple -gen directories, but why not place everything in a single directory where we have full control. As Peter says, it's easy to describe to users and also it makes cleaning much simpler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of like the idea of keeping things that are user-facing in separate directory.

This is taken into account in #8.

it makes cleaning much simpler

This is taken into account in #8.

it's easy to describe to users

Unfortunately, #8 would be complex to describe to users.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have fed-gen because it contains generated Lingua Franca source code, which src-gen doesn't. Moreover, fed-gen has a src-gen directory with in it with generated target code, just like the root-level src-gen directory. This sounds like a pretty reasonable explanation to me. I understand that all if this could be changed, but it will cost us time, so we should only do this if there is something to gain from it. What advantage would it yield?

Copy link
Contributor

@cmnrd cmnrd Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, what we place in fed-gen is also generated code, the only difference is the language. Why does that difference matter?

How exactly is the include (or potential include-gen) actually user facing? Currently, I don't see the difference compared to the other generated code and in particular to reactor-c, all of which we place in src-gen. The user only interacts with this code by writing reactions, right? If that is the case, why should we treat it differently from the other generated code?

What advantage would it yield?

By using nested gen, build, and install directories, instead of many different ones on the top-level we gain parity between targets. We can identify a small set of directories that we create. This simplifies communication to the user, as we can state precisely which directories we create and which we potentially delete on a clean. In fact, I would advocate for introducing only a single directory, which we could call target or something similar. Inside this directory, we can then more freely define the structure we want, and it would be ok to also use different structures for different targets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion about this, but to answer the question:

How exactly is the include (or potential include-gen) actually user facing?

It includes type definitions and function prototypes that the user has to implement. The user has to #include the header file in their C code. Although the user could in principle read the LF code and figure out what the header file is going to contain, in practice I expect that the easiest thing will be for the user to just read the generated header file because they are already familiar the process of writing source files that implement things that are defined in header files. The header files may also be hooked into whatever editor support the user has for their C source files.

We could try to liberate the user from having to work directly with the header files but I consider that to be an unnecessary disruption to whatever their usual workflow probably is for writing C code.


# Unresolved questions
[unresolved-questions]: #unresolved-questions

# Future possibilities
[future-possibilities]: #future-possibilities