lf-lang / lingua-franca

Intuitive concurrent programming in any language
https://www.lf-lang.org
Other
238 stars 63 forks source link

Unify the output behavior of code generators #149

Closed cmnrd closed 3 years ago

cmnrd commented 4 years ago

I have noticed that the file generation across targets uses different approaches which also leads to generated files being placed in different locations. I want to start a discussion about this and hope that we can decide for a common approach.

Consider the source file <project-root>/foo/Bar.lf where <project-root> refers to a project directory in the eclipse workspace. When compiling the file Bar.lf, this is what currently happens in the different targets:

I think in any case, we should stick to the usage of src-gen. The TS target can create subdirectories as needed within (src-gen/Bar). This is also what the C++ target does.

But where should the src-gen directory be placed? I personally prefer the Xtext default which is used in the C++ target. When running lfc, I would expect it to operate in the CWD and not to create new files next to the specified input file. When compiling from Eclipse, it creates src-gen in the project root. I am not a regular Eclipse user, but I think that is also what most Eclipse users would expect. It acknowledges that Eclipse has the notion of a project to group related things. If the generated code of two LF files should not be placed in the same src-gen directory, then those two files should actually not be part of the same project.

What is your opinion on this matter?

Independent of what the default behavior should be, I think it would make sense for lfc to accept an optional command line argument that allows the user to specify the output directory.

lhstrh commented 4 years ago

I agree that the behavior should be the same across targets, and I think your suggestion to put generated code in <project-root>/src-gen when running in INTEGRATED mode, and in <cwd/src-gen> when running in STANDALONE mode indeed is the most sensible solution. Unless anyone disagrees, I'll make the change as soon as I get to it. Also, :+1: for the suggestion to add an output path to lfc :-)

cmnrd commented 4 years ago

I think the best solution (TM), would be for all targets to use the IFileSystemAccess2 interface that xtext passed to the doGenerate method. This already defaults to the behavior suggested above. I already added a method getAbsolutePath(IFileSystemAccess2 fsa, String file) to the generator base, that converts a / separated path relative to the src-gen directory, to an absolute path in the file system. This is very handy when it comes to copying files from the lib dir or for executing commands in a certain directory.

It is also possible to modify the configuration of the file system interface. I didn't look into this yet, but this should allow us to override the default location. Thus we can take a target output directly given to lfc and update the configuration once in generator base.

edwardalee commented 4 years ago

sounds good to me.

cmnrd commented 4 years ago

While we are at it: I think we should also add an option to both lfc and the GUI to clean up the generated directories. I am just not sure how this would work in the CLI version. For the GUI version I think it is simple: delete all the generated files for the entire project. But for the CLI version this is not safe to do. Simply deleting the src-gen directory (and others) in the CWD seems dangerous to me. Maybe we need some kind of manifest file that keeps track over all the generated files and then clean only deletes files listed in the manifest.

edwardalee commented 4 years ago

Why can’t the user just do rm -rf src-gen?

cmnrd commented 4 years ago

Because it is a bit more complicated than that. For the C target, for instance, one needs to run rm -rf src-gen bin to clean everything. This is even more complicated for the C++ target (rm -rf bin build include lib share src-gen) because it creates various artifacts. I am not sure how cleaning would work in TS.

Probably we should also talk about these other directories. I think src-gen should really just contain the generated sources. But where should we place all the build artifacts? If we can find a common directory structure, we could stick to the rm -rf approach.

lhstrh commented 4 years ago

@edwardalee pointed out the issue of generated source and binaries for .lf files with the same name but different targets (obviously located in different directories, but within the same project) overwriting one another. To avoid this, we could feature the target in the output path. I.e., we'd put things in <project-root>/src-gen/<target>/ when running in INTEGRATED mode, and in <cwd>/src-gen/<target>/ when running STANDALONE, but this seems a bit odd as well.

There's also another solution: if we introduce packages (which has been suggested we do as part of an effort to revamp the import mechanism), we'd get an error if there were two classes with the same name in the same package, and we could use the package name rather than the target name in the src-gen directory to avoid clashes in the file system. If no package is specified, we'd simply search the entire resource/classpath for name clashes and report them...

lhstrh commented 4 years ago

Also, @cmnrd mentioned:

We could easily avoid the name conflicts, by placing the generated files in a directory tree that resembles the original source tree. For example, if we compile foo/bar.lf the generated source code could be placed in src-gen/foo/bar/ and the binary in bin/foo/bar. This is also how xtend handles this issue. We should only take care to use the same convention across targets.

cmnrd commented 3 years ago

Closing this issue as it was addressed in #319