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

chore: update Gradle build documentation #17408

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
71 changes: 9 additions & 62 deletions docs/gradle-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ This documents explains how to use Gradle directly from the command line via the
command. All Gradle tasks can also be invoked from the Gradle view in
[IntelliJ IDEA](intellij-quickstart.md).

For more information, also refer to the
[documentation of the Hiero Gradle Conventions](https://github.com/hiero-ledger/hiero-gradle-conventions#build)
which this project uses.

There are several Gradle tasks you can use. Most notably:

- `./gradlew assemble` compile all code and create all Jar files
Expand All @@ -38,68 +42,11 @@ You may run `./gradlew` (without arguments) for a detailed overview

## Using Gradle during Development

### Changing or adding Modules of Hedera (aka Gradle Subprojects)

All modules are listed in [settings.gradle.kts](../settings.gradle.kts) using
`include(":<module-name>", "<module-folder-path>")`. The `module-folder-path` should be a folder in
a subdirectory like [platform-sdk](../platform-sdk) or [hedera-node](../hedera-node). In the folder,
the following files are expected:

- `build.gradle.kts` specifies to which group of modules the module belongs, e.g.
`id("com.hedera.gradle.services")` or `id("com.hedera.gradle.platform")` and may contain
[dependency definitions](#changing-or-adding-dependencies) for tests.
- `src/main/java/module-info.java` is the Java Module specification that is also used to determine
the [dependencies of the module](#changing-or-adding-dependencies) by Gradle. Note that the last
segment of the module name defined in the `module-info.java` file needs to correspond to the
name of the module defined in [settings.gradle.kts](../settings.gradle.kts).

### Changing or Adding Dependencies

This project use of the _Java Module System (JPMS)_. With this, dependencies between modules are
defined in the `src/main/java/module-info.java` files that each module contains. Other modules are
identified by their _Module Name_ there. For example, a dependency to the `swirlds-logging` module
is expressed by `requires com.swirlds.logging`. A dependency to the 3rd party library
`com.fasterxml.jackson.core` is expressed by `requires com.fasterxml.jackson.core`. Note: This
project utilizes the
[org.gradlex.java-module-dependencies](https://github.com/gradlex-org/java-module-dependencies)
plugin to achieve this integration between Gradle and the Java Module System.

Each dependency definition contains a scope – e.g. `requires` or `requires transitive`. If you are
unsure about a scope, use `requires` when adding a dependency. Then execute `./gradlew qualityGate`
which runs a dependency scope check that analysis the code to determine which Java types are visible
(and should be visible) to which modules. If the check fails, it will advise you how to change the
scope.

### Adding or Changing the Version of a 3rd party dependency

If you use a 3rd party module lke `com.fasterxml.jackson.core`, a version for that module needs to
be selected. For this, the
[hedera-dependency-versions/build.gradle.kts](../hedera-dependency-versions/build.gradle.kts)
defines a so-called _Gradle platform_ (also called BOM) that contains the versions of all 3rd party
modules used. If you want to upgrade the version of a module, do this here. Remember to run
`./gradlew qualityGate` after the change. If you need to use a new 3rd party module in a
`src/main/java/module-info.java` file, you need to add the version here. (If the new module is not
completely Java Module System compatible, you may also need to add
[patching rules](#patching-3rd-party-modules)).

### Patching 3rd Party Modules

Some 3rd party libraries we use are not yet fully Java Module System compatible. And some modules
pull in other dependencies that we can neglect. Situations like this are treated as wrong/incomplete
metadata in our Gradle setup and the file
[com.hedera.gradle.jpms-modules.gradle.kts](../gradle/plugins/src/main/kotlin/com.hedera.gradle.jpms-modules.gradle.kts)
contains the rules to adjust or extend the metadata of 3rd party libraries to address such problems.

Note: This project utilizes the
[org.gradlex.extra-java-module-info](https://github.com/gradlex-org/extra-java-module-info) and
[org.gradlex.jvm-dependency-conflict-resolution](https://gradlex.org/jvm-dependency-conflict-resolution/#resolution-plugin)
plugins to ease the definition of patching rules.

### Incrementing the Version of Hedera itself

Our Gradle build has a single version number for all modules. It is defined in
[version.txt](../version.txt). Changing this version number will automatically apply to every
module.
### Defining modules and dependencies

Please refer to the section about _modules and dependencies_ in the
[documentation of the Hiero Gradle Conventions](https://github.com/hiero-ledger/hiero-gradle-conventions#modules)
which this project uses.

### Testing

Expand Down
4 changes: 4 additions & 0 deletions docs/intellij-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ git clone https://github.com/hashgraph/hedera-services.git

From IntelliJ, choose `File -> Open` the _hedera-services/_ directory you just cloned.

For more information, also refer to the
[documentation of the Hiero Gradle Conventions](https://github.com/hiero-ledger/hiero-gradle-conventions#build)
which this project uses.

### (optional) IntelliJ plugins

The following plugins add comfort features for working with certain parts of the code base:
Expand Down
119 changes: 62 additions & 57 deletions hedera-node/docs/design/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,25 @@ Java modules.

## Gradle module description

Each module needs a `build.gradle.kts` file that describes the module.
Please also refer to the
[documentation of the Hiero Gradle Conventions](https://github.com/hiero-ledger/hiero-gradle-conventions#modules)
which describes a common approach for structuring projects and modules in Hiero and Hedera repositories.

General best practices for all our (Java) modules are defined in custom plugins that can be found
under `gradle/plugins/src/main/kotlin`. For a Java module the `com.hedera.gradle.java` plugin should be used.
Next to this each module should have a description. Since nothing else is needed for a minimal module the most simple
`build.gradle.kts` looks like this:
Each module needs a `build.gradle.kts` file that describes the module.
In the `build.gradle.kts` file, you define the type of the module by using one of the
[_Module_ convention plugins](https://github.com/hiero-ledger/hiero-gradle-conventions#plugins).
provided by the Hiero Gradle Conventions.

```
plugins {
id("com.hedera.gradle.java")
}
plugins { id("org.hiero.gradle.module.library") }

description = "A minimal module without any dependecies"
```

The `group` and `version` of the module should not be added here. The `group` is the same for all modules on the
repository (`com.hedera.hashgraph`) and its definition can be found in the `com.hedera.gradle.conventions` plugin.
For the `version` we use a global definition, too. The current `version` is defined in the `gradle.properties` file in
Note: the `group` and `version` of the module should not be added here. The `group` is the same for all modules that
belong to a _product_, and it is
[defined in `settings.gradle.kts`](https://github.com/hiero-ledger/hiero-gradle-conventions#modules).
For the `version` we use a global definition, too. The current `version` is defined in the `version.txt` file in
the root folder of the project. The `name` of a module is simple created based on the folder name of the module.

## Project sources set
Expand All @@ -45,36 +46,44 @@ specific path below the `src/main/resources/com/hedera/node/app/services/foo` fo

## Tests

All modules can have different types of tests. The `com.hedera.gradle.java` plugin provides direct support
for unit test, integration tests, and end-to-end tests. Next to this the test fixtures functionality of Gradle is
supported.
All modules can have different types of tests. The `org.hiero.gradle.module.library` plugin provides direct support
for unit tests. Other test sets can be added by applying additional feature plugins:

- `id("org.hiero.gradle.feature.test-hammer")`
- `id("org.hiero.gradle.feature.test-integration")`
- `id("org.hiero.gradle.feature.test-time-consuming")`
- `id("org.hiero.gradle.feature.test-timing-sensitive")`

Next to this, the test fixtures functionality of Gradle is supported.

### Test fixtures

To create clean and readable unit tests it is best practice to provide common functionality for tests in the test
fixtures of a module. Based on the `com.hedera.gradle.java` plugin test fixtures are supported for all
Java modules.
fixtures of a module. To add test fixture support to a module, apply the `id("org.hiero.gradle.feature.test-fixtures")`
plugin.

All Java sources for the test fixtures must be placed under `src/testFixtures/java`. Additional resources that should be
shared for tests can be placed under `src/testFixtures/resources`.

Like for the source set of a project the test fixtures sets are defined as full JPMS modules, too. Based on that
a `module-info.java` file must be placed directly under `src/testFixtures/java` if at least one Java file is present.
The name of the test fixtures module should be based on the name of the source module and add a `testfixtures` suffix.
The name of the test fixtures module should be based on the name of the source module and add a `test.fixtures` suffix.
For the given sample of the foo service the name and the base package for the test fixtures set would
be `com.hedera.node.app.services.foo.testfixtures`. Since the test fixtures JPMS module will only be used in tests it
be `com.hedera.node.app.services.foo.test.fixtures`. Since the test fixtures JPMS module will only be used in tests it
should be fully opened. This makes the content of the `module-info.java` much more readable since no individual `opens`
statements need to be added. The complete module will be opened by adding the `open` keyword directly to the module
definition:

```
open module com.hedera.node.app.services.foo.testfixtures {
open module com.hedera.node.app.services.foo.test.fixtures {
//...
}
```

**Hint:** Similar to any other module the test fixtures set of a module can be added as a dependendency by using the
correct Gradle syntax like in this sample: `testImplementation(testFixtures(project(":foo-service"))) `
**Hint:** Similar to any other module the test fixtures set of a module can be added as a dependency by using the
corresponding `requires`. In this sample: `requires com.hedera.node.app.services.foo.test.fixtures`
(in module-info.java) or `testModuleInfo { requires("com.hedera.node.app.services.foo.test.fixtures") }"`
(in build.gradle.kts for unit tests that have no module-info.java).

### Unit tests

Expand All @@ -84,25 +93,21 @@ same base package as the module sources. By doing so unit tests can access packa

### Integration tests

For the integration test set the `src/itest/java` and `src/itest/resources` folders must be used. All integration tests
will be executed on the module path to be as near to the real usage as possible. Based on that a `module-info.java` file
is needed. Like for test fixtures the name for the module and the base package is based on the name of the source module
plus the 'itest' suffix. For the given sample the JPMS module name for the integration tests would
be `com.hedera.node.app.services.foo.itest`. Since the test fixtures JPMS module will only be used in tests it should be
fully opened. This makes the content of the `module-info.java` much more readable since no individual `opens`
statements need to be added. The complete module will be opened by adding the `open` keyword directly to the module
definition:
For the integration test set the `src/testIntegration/java` and `src/testIntegration/resources` folders must be used.
All integration tests will be executed on the module path to be as near to the real usage as possible. Based on that a
`module-info.java` file is needed. Like for test fixtures the name for the module and the base package is based on the
name of the source module plus the 'test.integration' suffix. For the given sample the JPMS module name for the
integration tests would be `com.hedera.node.app.services.foo.test.integration`. Since the test fixtures JPMS module
will only be used in tests it should be fully opened. This makes the content of the `module-info.java` much more
readable since no individual `opens` statements need to be added. The complete module will be opened by adding the
`open` keyword directly to the module definition:

```
open com.hedera.node.app.services.foo.itest {
open com.hedera.node.app.services.foo.test.integration {
//...
}
```

### End-to-end tests

For the end-to-end test set the `src/eet/java` and `src/eet/resources` folders must be used.

## JMH benchmarks

Next to tests a module can have several benchmarks to test the performance of critical components within the module. All
Expand All @@ -113,29 +118,29 @@ benchmarks must be based on JMH and the `src/jmh/java` and `src/jmh/resources` f
Based on the given definitions a module folder in the project looks like this:

```
foo-service/
├── src/main/java/
│ ├── com.hedera.node.app.service.foo
│ │ ├── FooService.java
│ │ └── package-info.java
│ └── module-info.java
├── src/main/resources/
│ ├── com.hedera.node.app.service.foo
│ │ └── some_data.json
│ └── logging.properties
├── src/testFixtures/java/
│ ├── com.hedera.node.app.service.foo.testfixtures
│ │ └── FooServiceTestConfig.java
│ └── module-info.java
├── src/test/java/
│ └── com.hedera.node.app.service.foo
│ └── FooServiceTest.java
├── src/itest/java/
│ ├── com.hedera.node.app.service.foo.itest
│ │ └── FooServiceITest.java
│ └── module-info.java
└── build.gradle.kts
```
foo-service/
├── src/main/java/
│ ├── com.hedera.node.app.service.foo
│ │ ├── FooService.java
│ │ └── package-info.java
│ └── module-info.java
├── src/main/resources/
│ ├── com.hedera.node.app.service.foo
│ │ └── some_data.json
│ └── logging.properties
├── src/testFixtures/java/
│ ├── com.hedera.node.app.service.foo.testfixtures
│ │ └── FooServiceTestConfig.java
│ └── module-info.java
├── src/test/java/
│ └── com.hedera.node.app.service.foo
│ └── FooServiceTest.java
├── src/testIntegration/java/
│ ├── com.hedera.node.app.service.foo.itest
│ │ └── FooServiceITest.java
│ └── module-info.java
└── build.gradle.kts
```

### Open questions:

Expand Down
14 changes: 5 additions & 9 deletions hedera-node/docs/design/services/service-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ hedera-foo-service/
```

The api modules should depend on the `com.hedera.node.app.spi` module and provide it as a transitive dependency. To do
so `api(project(":hedera-node:hedera-app-spi"))` must be a dependency in the gradle file. The module should only contain
the public api of services. The complete content of the module should be exported. Since the service interface that are
defined in the api modules will be loaded by the Java SPI the interfaces must be defined in the module info by using
the `uses` keyword.
so `requires transitive com.hedera.node.app.spi` must be a dependency in the `module-info.java` file. The module should
only contain the public api of services. The complete content of the module should be exported. Since the service
interface that are defined in the api modules will be loaded by the Java SPI the interfaces must be defined in the
module info by using the `uses` keyword.

Based on this given constraints the `module-info.java` of an api module looks like this:

Expand All @@ -49,14 +49,10 @@ plugins {
}

description = "Hedera Foo Service API"

dependencies {
api(project(":hedera-node:hedera-app-spi"))
}
```

The public service api can depend on additional libraries. Such libraries will be defined as **public api** and must be
added as `api(...)` dependencies to gradle and defined as `requires transitive` in the `module-info.java`. No api module
added as `requires transitive` in the `module-info.java`. No api module
must ever depend on any service implementation or the `hedera-mono-service` module. The `@NonNull`/`@Nullable`
annotations can be used in the service definitions. Since the annotations are already defined as transitive dependencies
at compile time by the `hedera-app-spi` module no extra dependency needs to be added. Unit tests and integration tests
Expand Down
6 changes: 4 additions & 2 deletions platform-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
- **Adoptium OpenJDK 17**
- Available from
[https://adoptium.net/temurin/releases/](https://adoptium.net/temurin/releases/)
- **Gradle 7.5.1**
- Available via the provided Gradle wrapper included in this repository.

### Standard Build Command

Expand All @@ -23,6 +21,10 @@ cd hedera-services/platform-sdk
./gradlew build
```

For more information, also refer to the
[documentation of the Hiero Gradle Conventions](https://github.com/hiero-ledger/hiero-gradle-conventions#build)
which this project uses.

## Support

If you have a question on how to use the product, please see our
Expand Down
Loading