Building Projects¶
Here, We use JeKa script and kbean capabilities to build a Java project.
Scaffold new project¶
Execute: jeka project: scaffold
to create a typical project structure, from where
you can directly start coding.
Execute: jeka intellij: iml
to sync the project with Intellij.
If the project seems not being reflected in IntelliJ, go to the Intellij project root dir, then execute jeka intellij: initProject
.
Tip
A README.md file is generated at the root of the project, mentioning the usual commands to run.
Configure project with properties¶
We can configure basic aspects of a project using properties configuration only.
Add dependencies¶
Add org.junit.jupiter:junit-jupiter:5.8.1
to the == TEST ==
section in dependencies.txt, then execute jeka intelllij: iml
If you want to add a compile-only dependency, add it to the == COMPILE ==
section then explicitly substract it from
the == RUNTIME ==
section using -
symbol.
Example:
== COMPILE ==
org.projectlombok:lombok:1.18.32
== RUNTIME ==
-org.projectlombok:lombok # indicate to not include lombok in runtime classpath.
== TEST ==
org.junit.jupiter:junit-jupiter:5.8.1
Configure jar type¶
By default, JeKa produce regular jar files. Nevertheless it can be configured to produce fat jar (jar including all dependencies), or shade jar (same with relocated packages to avoid classpath collision).
Edit jeka.properties and add :
@project.pack.jarType=FAT
If you bundle a library, you may prefer to create an additional shade jar, that embeds dependency jars gracefully.
For this, edit jeka.properties and add :
# Create an extra jar suffixed with 'all-deps*
@project.pack.shadeJarClassifier=all-deps
# Instruct Maven plugin to publish it as well.
@maven.publication.extraArtifacts=all-deps
Configure Layout¶
By default, project structure adopt Maven one, meaning sources in src/main/java ans so on.
You can modify it by adding the following properties in jeka.properties.
# sources in src dir and tests in test dir
@project.layout.style=SIMPLE
# sources and resources sharing the same dir
@project.layout.mixSourcesAndResources=true
Tip
List available options by executing: jeka project: --help
.
Use common plugins¶
Handle versioning with git¶
You can use Git to automatically handle versioning for your project. The version will value [TAG] if the workspace is currently on a tag, or [BRANCH]-SNAPSHOT if it is not.
The Manifest file will also be augmented with Git info (commit, dirty, branch, ...)
@git=
@git.handleVersioning=true
Perform static analysis using Sonarqube and Jacoco¶
We need to import the Sonarqube and Jacoco project. We also set a shorthand for easy invoking.
We can also select the exact version for each tool.
SonarQube can be configured directly using native configuration settings present in the jeka.properties file. Properties related to the SonarQube project/version are automatically picked up from the project information.
jeka.inject.classpath=dev.jeka:sonarqube-plugin dev.jeka:jacoco-plugin
jeka.cmd.pack_quality=project: pack jacoco: sonarqube: run logOutput=true
@jacoco.jacocoVersion=0.8.11
@sonarqube.scannerVersion=5.0.1.3006
sonar.host.url=http://localhost:9000
Execute build actions¶
This is a list of common commands used when building projects. This commands may be combined
and use in conjunction on --clean (-c)
option for cleaning the output dir prior running.
Generates source code
jeka project: generateSources
Compile sources
jeka project: compile --clean
Compile (if needed) and execute tests
jeka project: test
Compile and test (if needed) and create artifacts as jars.
jeka project: pack
Same, but skipping the test execution
jeka poject: pack -Djeka.skip.tests=true
Display project info on console
jeka project: info
Display the tree of transitive dependencies
jeka project: depTree
Execute the main method from compiled classes
jeka project: runMain programArgs="myArg1 myArg2"
Execute the main method from the packed jar.
jeka project: runJar programArgs="myArg1 myArg2"
Docker commands¶
Create a Docker image of the application
jeka docker: build
Create a Docker image of the application compiled to native.
jeka docker: buildNative
Maven publish commands¶
Publish on Maven repository
jeka maven: publish
Publish on local Maven repository
jeka maven: publishLocal
Configure programmatically¶
Project can also be configured programmatically. This is often needed when we want to include extra build actions or adopt distinct behaviors on specific conditions.
When we have scaffolded the project, a Build.java class was created on purpose for customizing the project.
class Build extends KBean {
final JkProject project = load(ProjectKBean.class).project;
/*
* Configures KBean project
* When this method is called, option fields have already been injected from command line.
*/
@Override
protected void init() {
// configure project instance here
}
}
The init()
is the designed place for configuring the JkProject
instance.
The JkProject
class models all what is needed to build a JVM project. This is a huge object model that
contains both configuration and methods that actually 'build'.
The API is too large and deep to be detailed here, we can just mention some useful links :
JkProject.flatFacade()
propose a simplified access to the API for most frequent cases.
Example with Flat Facade¶
class Build extends KBean {
boolean runIntegrationTests;
final JkProject project = load(ProjectKBean.class).project;
@Override
protected void init() {
project.flatFacade()
// change dependency ordering : guava must be before commons-lang
.customizeRuntimeDeps(deps -> deps
.withMoving("com.google.guava:guava", before("commons-lang:commons-lang")))
// Set how tests execution are displayed on console
.setTestProgressStyle(JkTestProcessor.JkProgressOutputStyle.BAR)
// Run test ending with 'IT' only on a specific condition
.addTestIncludeFilterSuffixedBy("IT", runIntegrationTests);
}
}
Example with JkProject¶
import dev.jeka.core.api.depmanagement.JkTransitivity;
import dev.jeka.core.api.depmanagement.artifact.JkArtifactId;
import dev.jeka.core.api.depmanagement.publication.JkMavenPublication;
import dev.jeka.core.api.project.JkProject;
import dev.jeka.core.tool.KBean;
import dev.jeka.core.tool.builtins.project.ProjectKBean;
import dev.jeka.core.tool.builtins.tooling.maven.MavenKBean;
class Build extends KBean {
final JkProject project = load(ProjectKBean.class).project;
final JkMavenPublication mavenPublication = load(MavenKBean.class).getMavenPublication();
@Override
protected void init() {
// Add extra actions
project.compilation.preCompileActions.append("Resources generation", this::generateSomeResources);
project.packActions.append("Create bin distrib", this::createBinDistrib);
// Customize Maven publication
// -- customize published transitive dependencies
mavenPublication.customizeDependencies(dep -> dep
.withTransitivity("com.google.guava:guava", JkTransitivity.COMPILE)
);
// -- add a new artifact file to publish, along the method to create it
mavenPublication.putArtifact(JkArtifactId.of("fat", "jar"), project.packaging::createFatJar);
// -- describe the POM metadata
mavenPublication.pomMetadata
.addMitLicense()
.addGithubDeveloper("John Doe", "Doe@djoe.com")
.setProjectDescription("......");
}
private void generateSomeResources() {
// .. do something
}
private void createBinDistrib() {
// .. do something
}
}