-
Notifications
You must be signed in to change notification settings - Fork 63
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
Redesign of the repository layout and gradle configuration #1779
Conversation
they are included in gradle build
@petervdonovan I added a the shadow jar plugin back to the lsp supbroject. Now |
@erlingrj Do you have any idea why the zephyr tests are failing? |
I think the problem is that my |
If they are based on RuntimeTest, they will inherit all the standard test methods as well.
Ok, that gave me the right pointer. I fixed it in 75a0578 (hopefully). The problem was that the new CI setups runs all test of the |
Because Christian added back a way to construct a fat jar for the language server, it is trivial to update |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am far from expert in Gradle, but I think this is probably ready to merge.
Thanks Christian!
.replace(lfFile.toString(), "%%%PATH.lf%%%") | ||
.replace("%%%PATH.lf%%%", lfFile.toString()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happened here? It seems like the original intent was to replace the value of lfFile.toString()
with "%%%PATH.lf%%%"
and not the other way around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not entirely sure either. The test used to fail and worked fine after switching this.
In order to get this to work in IntelliJ, I needed to go to
We should make sure people know about this to save them some trouble. |
@cmnrd, do you think there is a way to avoid this extra step? |
I am a bit confused by this. What exactly did not work for you? Actually Normally, the way to configure the Or was your problem that IntelliJ would throw errors when the tasks are loaded initially? If that was the case for you, it should be fixed by b2c70d2. This ensures that the errors are only thrown when the task actually executes, not when it is loaded. |
You are right, the problem seems to be solved now! |
Overview and Motivation
This PR presents a major refactoring of the source layout in our repository as well as the gradle configuration that we use for building. Our current layout was created by Xtext and does not follow the Gradle/Maven layout conventions that are typical for Java projects (#1801). Moreover, our build configuration (gradle) grew over time, without ever considering the idiomatic gradle way of solving certain problems. This lead to a situation where we don't support gradle's standard tasks like
build
andassemble
(#1514). Moreover, there were several issues with the setup like Kotlin classes missing on a rebuild (#1284) and whenever we tried to modify something in the configuration something else broke. For this reason, this PR rewrites the gradle scripts from scratch using an idiomatic approach.Goals
assemble
task #1514)Changes
Project structure
The new project structure looks like this:
In the root of the repository (the root project) we have one sub-project
org.lflang
that is divided into multiple nested sub-projects. This is in contrast to the single project that we had before. Gradle doesn't like if certain plugins are combined (e.g. library and application) and instead one should be explicit about which sub-projects are libraries (with public API or internal) and which are executable applications.In the new setup, we have the
core
projects which is a library and holds the largest part of our code base. Thelsp
project contains is application and holds sources that are specific to the language server. Thecli/lfc
andcli/lfc
are both applications and of course correspond to our cli tools.cli/base
is a library containing logic that is shared between the two.Source Layout
Each of the
src/
directories follows the Gradle/Maven source tree layout and looks something like this:Note that we divide the source tree by function (
main
i.e main sources,test
,testFixtures
andintegrationTest
) and then by language (java
,kotlin
,resources
).resources
contains all additional resources that should be on the classpath like our runtime submodules.Testing
Instead of having a separate test project (the
org.lflang.tests
from before) we now make the tests part of each project (i.e. we place the tests close to the code they are testing). In the source layout from above, thetest
subdirectory contains all unit tests. We make use or gradle's test suite feature and also define an additional test suiteintegrationTest
. These are our runtime tests that compile and execute each test LF program. Separating unit and integration tests gives us more control about which types of tests we run. Thetest
task will only run all unit tests and not the slow integration tests.The integration tests can be run using
./gradlew integrationTest
. We can also specify the precise test as before. For instance, the following command will run all concurrency tests for the C target:Note that we qualify the
integrationTest
task with the project, so that we only search for matching tests in this project.The root project defines a convenient helper tasks that allows to run all tests for a specific target. For instance, you can use the following command to run all Rust tests:
You can specify any valid target. If you run the task without specifying the target property
./gradlew tagetTest
it will produce an error message and list all available targets.Another root task allows running only a single specific test:
Note that this replaces the
runSingleTest
task.To invoke only the C tests in the
concurrent
category, for example, do this:Note that the description above can also be found in the updated
test/README.md
.Building and running with Gradle
To build everything, simply run
./gradlew build
. Note that the build task in Gradle also includes running all checks. Sobuild
will also execute thetest
andcheckFormat
tasks. If you don't want the checks to run,./gradelw assemble
can be used instead. However, I think it is great to always make sure that the checks pass when building and Gradle is quite smart in figuring out which checks to run. It will only rerun those affected by a change. Overall, build should be quite fast.Both the
build
and theassemble
task will create executablelfc
andlff
scripts inbuild/install/lf-cli/bin
.As before, we also define custom tasks
runLfc
and runrunLff
that can be used to directly execute the cli applications:Packaging
Packaging is trivial now. Gradle's distribution plugin takes care of it. It creates zip and tar archives in
build/distributions
that are ready to be uploaded as artifacts. In fact, the installed scripts from above are the unpacked version of these archives.For nightly builds, the
nightly
project property can be specified. For instance:This will create an archive called something like
lf-cli-0.4.1-SNAPSHOT-nightly-20230531150822.zip
. Note that the name now also includes the snapshot version, as before it was namedlf-cli-nightly-20230531150822.zip
. This shouldn't be an issue, though.Scripts in bin
We have collected several bash scripts in
bin/
, many of which have rarely been used or are redundant.measure-lf-time
andrun-multiple-times
are simply not needed as this functionality is covered by the benchmark runner.build-lf-cli
andrun-lf-tests
are also not needed as we can instead invoke the corresponding gradle task directly.bump_versions
we do need, but I moved it toutil/scripts/bump-versions
. It should only be used rarely and should probably not be located in the prominent bin directory.lfc
andlff
see belowbin/lfc
andbin/lff
scriptsI decided to make the
bin/lfc
andbin/lff
scripts as simple as possible. They simply call the corresponding gradle run tasks. Running./bin/lfc <arg1> <arg2> <...>
is equivalent to running./gradlew runLfc --args="<arg1> <arg2> <...>"
.Advantages:
Disadvantages:
Note that users and developers can still use the lfc/lff scripts in
build/install/lf-cli/bin
if they prefer. Those invoke the commands directly (not through gradle).CI Flows
Since the
build
task includes running unit tests and formatter checks, we don't need separate CI jobs for the latter two anymore.Idea Plugin
I also added the idea plugin to our root project. This allows to run
./gradlew openIdea
to open the project in IntelliJ IDEA.Open questions
Should we eliminate theorg.lflang
directory and havecli
,core
,lsp
directly as sub-projects of the root project?Is changing the behavior ofbin/lfc
andbin/lff
controversial or is it Ok for everyone?TODO
tmp/fed-gen
is created in project root after running unit tests.util/scripts/bump-versions
to epock repo (still works and fix if needed. It should also consider the versions inThe release tooling needs to be updated( after merging this)org.lflang/todo/log4j.xml
where it is unclear what it was needed for (the current location in master isorg.lflang/todo/log4j.xml
). Was this needed for the LSP or for the CLI tools? Either way, it should be added as a resource to the corresponding application projects.buildAll
,runSingleTest
) with an informative error message indicating what to do instead.Integrate changes in epoch repo.-> do laterFixed issues
assemble
task #1514LFFormatter
is slow #1342