Last active
January 19, 2026 13:33
-
-
Save mcgivrer/3fabaa2cd1a1e1fc934c7b5da6a6e8ca to your computer and use it in GitHub Desktop.
java project micro build with libs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| #---- project parameters | |
| project_name=[[DEFAULT_APP_NAME]] | |
| project_version=[[DEFAULT_APP_VERSION]] | |
| main_class=[[DEFAULT_MAIN_CLASS]] | |
| vendor_name="[[DEFAULT_VENDOR_NAME]]" | |
| author_name=[[DEFAULT_AUTHOR_NAME]] | |
| JARS= | |
| SOURCE_VERSION="25" | |
| JAR_OPTS="-Xmx512m" | |
| # | |
| #--- DO NOT CHANGE THE FOLLOWING LINES --- | |
| # | |
| main_class_array=($main_class) | |
| num_main_classes=${#main_class_array[@]} | |
| SRC=./src | |
| LIBS=./libs | |
| TARGET=./target | |
| BUILD=${TARGET}/build | |
| CLASSES=${TARGET}/classes | |
| RESOURCES=${SRC}/main/resources | |
| SOURCE_ENCODING="UTF-8" | |
| JARS=libs/dependencies/ | |
| COMPILATION_OPTS="-Xlint:unchecked -Xlint:deprecation -parameters" | |
| # add your test execution commands here | |
| TEST_CLASSES=${TARGET}/test-classes | |
| TEST_RESOURCES=${SRC}/test/resources | |
| LIB_TEST=$LIBS/junit-platform-console-standalone-6.0.0.jar | |
| # detect OS type to set the classpath separator | |
| if [[ "$OSTYPE" == "linux"* ]]; then | |
| FS=":" | |
| else | |
| FS=";" | |
| fi | |
| # define colors | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' | |
| #---- process buid | |
| GIT_COMMIT_ID=$(git rev-parse HEAD) | |
| JAVA_BUILD=$(java --version | head -1 | cut -f2 -d' ') | |
| #---- check SDKMAN | |
| REQUIRED_JAVA_VERSION=$SOURCE_VERSION | |
| JAVA_BIN=$(command -v java 2>/dev/null) | |
| JAVA_VERSION_DETECTED=$($JAVA_BIN -version 2>&1 | awk -F '[\"_]' '/version/ {print $2}' | cut -d'.' -f1) | |
| if [ -z "$JAVA_BIN" ] || [ "$JAVA_VERSION_DETECTED" != "$REQUIRED_JAVA_VERSION" ]; then | |
| echo -e "${RED}Java n'est pas installé. Tentative d'installation de Java $REQUIRED_JAVA_VERSION avec SDKMAN.${NC}" | |
| # Ensure the `sdk` command is available by sourcing SDKMAN init if installed | |
| if [ -z "$(command -v sdk 2>/dev/null)" ] && [ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]; then | |
| # shellcheck source=/dev/null | |
| source "$HOME/.sdkman/bin/sdkman-init.sh" | |
| if [ -f .sdkmanrc ]; then | |
| echo "Install SDKMAN environment" | |
| sdk env install | |
| sdk env use | |
| echo "done." | |
| fi | |
| fi | |
| else | |
| echo -e "${GREEN}Java détecté: version $JAVA_VERSION_DETECTED${NC}" | |
| fi | |
| # Prepare Build | |
| rm -vrf target/ | |
| find $SRC/main/java $RESOURCES -name "*.java" | |
| mkdir -vp ${TARGET}/{classes,build/libs} | |
| # Compile sources | |
| javac ${COMPILATION_OPTS} -cp libs/ $(find $SRC/main/java $RESOURCES -name "*.java") -d ${CLASSES} | |
| # create MANIFEST file | |
| cp -vr $RESOURCES/* $CLASSES | |
| echo "build jar..." | |
| for app in "${main_class_array[@]}"; do | |
| if [ $num_main_classes -eq 1 ]; then | |
| jar_name="${project_name}-${project_version}.jar" | |
| else | |
| jar_name="${project_name}-$app-${project_version}.jar" | |
| fi | |
| mkdir -p ${TARGET}/META-INF | |
| echo ">> for ${project_name}.$app..." | |
| echo """ | |
| Manifest-Version: ${project_name} | |
| Main-Class: ${app} | |
| Class-Path: ${JARS} | |
| Created-By: ${JAVA_BUILD} | |
| Implementation-Title: ${project_name} | |
| Implementation-Version: ${project_version}-build_${GIT_COMMIT_ID:0:12} | |
| Implementation-Vendor: ${vendor_name} | |
| Implementation-Author: ${author_name} | |
| """ >>${TARGET}/META-INF/MANIFEST.MF | |
| jar cvfe ${TARGET}/build/${jar_name} $app -C ${CLASSES} . | |
| # create run script | |
| echo "create run script ${project_name}.sh ..." | |
| rm -f ${project_name}.sh | |
| echo """#!/bin/bash | |
| java -jar ${TARGET}/build/${jar_name} \$@ | |
| """ >${project_name}.sh | |
| chmod +x ${project_name}.sh | |
| echo "done." | |
| done | |
| if ([ "$1" == "test" ]); then | |
| echo "Run tests..." | |
| echo -e "|_ ${BLUE}6. Execute tests${NC}..." | |
| echo "> from : ${SRC}/test" | |
| echo "> to : ${TARGET}/test-classes" | |
| mkdir -p ${TARGET}/test-classes | |
| echo "copy test resources" | |
| cp -r ./$RESOURCES/* $TEST_CLASSES | |
| cp -r ./$TEST_RESOURCES/* $TEST_CLASSES | |
| echo "compile test classes" | |
| #list test sources | |
| find ${SRC}/main -name '*.java' >${TARGET}/sources.lst | |
| find ${SRC}/test -name '*.java' >${TARGET}/test-sources.lst | |
| javac -source $SOURCE_VERSION -encoding $SOURCE_ENCODING $COMPILATION_OPTS -cp ".${FS}$LIB_TEST${FS}${EXTERNAL_JARS}" -d $TEST_CLASSES @${TARGET}/sources.lst @${TARGET}/test-sources.lst | |
| echo "execute tests through JUnit" | |
| java $JAR_OPTS -jar $LIB_TEST execute -cp "${EXTERNAL_JARS}${FS}${CLASSES}${FS}${TEST_CLASSES}${FS}." --scan-class-path | |
| echo -e " |_ ${GREEN}done$NC" | |
| echo "- execute tests through JUnit ${SRC}/test." >>${TARGET}/build.log | |
| fi | |
| if ([ "$1" == "run" ]); then | |
| echo "Run the generated JAR(s)..." | |
| for app in "${main_class_array[@]}"; do | |
| if [ $num_main_classes -eq 1 ]; then | |
| jar_name="${project_name}-${project_version}.jar" | |
| else | |
| jar_name="${project_name}-$app-${project_version}.jar" | |
| fi | |
| echo ">> run JAR ${jar_name} ..." | |
| shift 1 | |
| java $JAR_OPTS -jar ${TARGET}/build/${jar_name} $@ | |
| echo "done." | |
| done | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package core; | |
| import java.io.IOException; | |
| import java.util.Properties; | |
| public class App { | |
| private Properties config = new Properties(); | |
| public static int debug = 0; | |
| public App() { | |
| System.out.println("Start App class..."); | |
| } | |
| public void run(String[] args) { | |
| init(args); | |
| process(); | |
| dispose(); | |
| } | |
| private void init(String[] args) { | |
| try { | |
| config.load(App.class.getResourceAsStream("/config.properties")); | |
| for (String key : config.stringPropertyNames()) { | |
| String value = config.getProperty(key); | |
| parseArg(key + "=" + value); | |
| } | |
| } catch (IOException e) { | |
| System.err.printf(" failed to load config file: %s%n", e.getMessage()); | |
| } | |
| if (args.length > 0) { | |
| System.out.println("parse args:"); | |
| int i = 0; | |
| for (String arg : args) { | |
| System.out.printf("- arg[%d]: %s%n", i++, arg); | |
| parseArg(arg); | |
| } | |
| } else { | |
| System.out.println("- no argument..."); | |
| } | |
| } | |
| public void parseArg(String arg) { | |
| if (arg.contains("=")) { | |
| String[] key = arg.split("=", 2); | |
| switch (key[0].toLowerCase().trim()) { | |
| case "d", "-d", "debug", "--debug" -> { | |
| debug = Integer.parseInt(key[1]); | |
| System.out.printf(" set debug level to %d%n", debug); | |
| } | |
| default -> { | |
| System.out.printf(" unknown argument: %s%n", arg); | |
| } | |
| } | |
| } else { | |
| System.out.printf(" unknown argument: %s%n Respect the format key=value%n", arg); | |
| } | |
| } | |
| private void process() { | |
| System.out.println("Processing..."); | |
| } | |
| private void dispose() { | |
| System.out.println("End App class."); | |
| } | |
| public static void main(String[] args) { | |
| App app = new App(); | |
| app.run(args); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| if [ "$#" -ne 1 ]; then | |
| echo "Usage: $0 <project_name>" | |
| echo "Description: create a java project structure into <project_name> with build script" | |
| echo "NOTE: if git user.name and user.email configuration are not set, git initialization will fail" | |
| echo " Make sure to set them using:" | |
| echo " git config --global user.name \"Your Name\"" | |
| echo " git config --global user.email \"your.email@example.com\"" | |
| echo " After setting these, rerun the script." | |
| exit 1 | |
| fi | |
| JUNIT_VERSION=6.0.0 | |
| GIT_USERNAME="$(git config --get user.name)<$(git config --get user.email)>" | |
| # create project structure | |
| mkdir -p $1/{src/{{main,test}/{java/core,resources},docs},libs/dependencies,.vscode} | |
| # add readme | |
| echo """# README | |
| ## Project $1 version 0.0.1 | |
| ### Build JAR with sdkman and javac | |
| \`\`\`bash | |
| sdk env use | |
| chmod +x ./build | |
| ./build | |
| \`\`\` | |
| ### Run tests | |
| \`\`\`bash | |
| ./build test | |
| \`\`\` | |
| ### Execute project | |
| \`\`\`bash | |
| ./build run debug=2 | |
| \`\`\` | |
| >**Note** | |
| >You can pass as many argument after run as you need: | |
| >e.g.: | |
| > - \`debug\`=[1 to 5], | |
| > - \`mode\`=[DEVELOPMENT,TESTING,PRODUCTION] | |
| > - \`timeout\`=[milliseconds] | |
| #### help on program | |
| \`\`\`bash | |
| ./build run help | |
| \`\`\` | |
| #### run from java | |
| \`\`\`bash | |
| java -jar target/build/$1-0.0.1.jar debug=2 | |
| \`\`\` | |
| Thanks, | |
| $GIT_USERNAME. | |
| """ >$1/README.md | |
| # add gitignore | |
| echo "target/" >$1/.gitignore | |
| # add sdkman config | |
| echo "java=25-zulu" >$1/.sdkmanrc | |
| ## download JUnit | |
| curl -sL https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/$JUNIT_VERSION/junit-platform-console-standalone-$JUNIT_VERSION.jar >$1/libs/junit-platform-console-standalone-$JUNIT_VERSION.jar | |
| ## build script | |
| curl -sL https://gist.githubusercontent.com/mcgivrer/3fabaa2cd1a1e1fc934c7b5da6a6e8ca/raw/build >$1/build | |
| sed -i \ | |
| -e "s/\[\[DEFAULT_APP_NAME\]\]/$1/g" \ | |
| -e "s/\[\[DEFAULT_APP_VERSION\]\]/0.0.1/g" \ | |
| -e "s/\[\[DEFAULT_MAIN_CLASS\]\]/core.App/g" \ | |
| -e "s/\[\[DEFAULT_AUTHOR_NAME\]\]/\"$GIT_USERNAME\"/g" \ | |
| -e "s/\[\[DEFAULT_VENDOR_NAME\]\]/VENDORNAME/g" \ | |
| "$1/build" | |
| chmod +x $1/build | |
| ## create VSCode support | |
| echo """ | |
| { | |
| \"java.project.sourcePaths\": [ | |
| \"src/main/java\", | |
| \"src/main/resources\", | |
| \"src/test/java\", | |
| \"src/test/resources\" | |
| ], | |
| \"java.project.referencedLibraries\": [ | |
| \"libs/junit-platform-console-standalone-6.0.0.jar\" | |
| ], | |
| \"java.project.outputPath\": \"target/classes\" | |
| } | |
| """ >$1/.vscode/settings.json | |
| ## Create Main app class source | |
| echo """package core; | |
| import java.io.IOException; | |
| import java.time.ZonedDateTime; | |
| import java.util.Properties; | |
| public class App { | |
| public enum DebugLevel { | |
| DEBUG, | |
| INFO, | |
| WARN, | |
| ERROR, | |
| FATAL; | |
| } | |
| public enum AppMode { | |
| DEVELOPMENT, | |
| TESTING, | |
| PRODUCTION; | |
| } | |
| private Properties config = new Properties(); | |
| public static int debug = 0; | |
| private static AppMode mode = AppMode.DEVELOPMENT; | |
| private static int timeout = 1000; // in milliseconds | |
| public App() { | |
| log(getClass(), DebugLevel.INFO, \"Start App class...\"); | |
| } | |
| public void run(String[] args) { | |
| initialize(args); | |
| try { | |
| log(getClass(), DebugLevel.INFO, \"Sleeping for %d ms...\", timeout); | |
| Thread.sleep(timeout); | |
| log(getClass(), DebugLevel.INFO, \"Execution completed after timeout.\"); | |
| } catch (InterruptedException e) { | |
| log(getClass(), DebugLevel.WARN, \"Execution interrupted.\"); | |
| } | |
| log(getClass(), DebugLevel.INFO, \"End App class.\"); | |
| } | |
| private void initialize(String[] args) { | |
| // set default values | |
| log(getClass(), DebugLevel.INFO, \"Set default configuration\"); | |
| config.setProperty(\"debug\", \"0\"); | |
| config.setProperty(\"mode\", \"DEVELOPMENT\"); | |
| config.setProperty(\"timeout\", \"1000\"); | |
| parseConfiguration(); | |
| log(App.class, DebugLevel.INFO, \" -> default configuration initialized\"); | |
| // parse configuration file | |
| try { | |
| log(getClass(), DebugLevel.INFO, \"Load configuration from file\"); | |
| config.load(App.class.getResourceAsStream(\"/config.properties\")); | |
| log(App.class, DebugLevel.INFO, \" loaded config.properties\"); | |
| parseConfiguration(); | |
| log(App.class, DebugLevel.INFO, \" -> configuration loaded\"); | |
| } catch (IOException e) { | |
| log(App.class, DebugLevel.ERROR, \" cannot load config.properties: %s\", e.getMessage()); | |
| } | |
| // parse command line arguments | |
| parseCliArgs(args); | |
| parseConfiguration(); | |
| } | |
| private void parseConfiguration() { | |
| for (String key : config.stringPropertyNames()) { | |
| parseConfig(key, config.getProperty(key)); | |
| } | |
| } | |
| private void parseCliArgs(String[] args) { | |
| if (args.length > 0) { | |
| log(getClass(), DebugLevel.INFO, \"parse args:\"); | |
| int i = 0; | |
| for (String arg : args) { | |
| log(getClass(), DebugLevel.INFO, \"- arg[%d]: %s\", i++, arg); | |
| parseArg(arg); | |
| } | |
| } else { | |
| log(getClass(), DebugLevel.INFO, \"- no argument...\"); | |
| } | |
| } | |
| private void parseArg(String arg) { | |
| if (arg.contains(\"=\")) { | |
| String[] key = arg.split(\"=\", 2); | |
| config.setProperty(key[0], key[1]); | |
| } else { | |
| config.setProperty(arg, \"true\"); | |
| } | |
| } | |
| private void parseConfig(String key, String value) { | |
| // first is config key.value, then last possible cli key args. | |
| switch (key.toLowerCase().trim()) { | |
| // set debug level from config file or CLI key-value pair | |
| case \"debug\" -> { | |
| debug = Integer.parseInt(value); | |
| log(getClass(), DebugLevel.INFO, \" set debug level to %d\", debug); | |
| } | |
| case \"mode\" -> { | |
| mode = AppMode.valueOf(value.toUpperCase()); | |
| log(getClass(), DebugLevel.INFO, \" set app mode to %s\", value); | |
| } | |
| case \"timeout\" -> { | |
| timeout = Integer.parseInt(value); | |
| log(getClass(), DebugLevel.INFO, \" set app timeout to %d ms\", timeout); | |
| } | |
| case \"h\", \"-h\", \"help\", \"-help\" -> { | |
| log(getClass(), DebugLevel.INFO, \" help requested, exiting...\"); | |
| System.out.println(\"Usage: java -jar app.jar [key=value]...\n\" + | |
| \"Available options:\n\" + | |
| \" debug=<level> Set debug level (0=none, 1=some, 2=verbose)\n\" + | |
| \" mode=<mode> Set application mode (DEVELOPMENT, TESTING, PRODUCTION)\n\" + | |
| \" timeout=<ms> Set application timeout in milliseconds\n\" + | |
| \" help Show this help message\"); | |
| System.exit(0); | |
| } | |
| default -> { | |
| log(getClass(), DebugLevel.WARN, \" unknown argument: %s=%s\", key, value); | |
| } | |
| } | |
| } | |
| public static void main(String[] args) { | |
| App app = new App(); | |
| app.run(args); | |
| } | |
| public static void log(Class<?> cls, DebugLevel level, String message, Object... args) { | |
| System.out.printf(\"%s;%s;[%s];%s%n\", | |
| ZonedDateTime.now(), | |
| cls.getCanonicalName(), | |
| level.name(), | |
| message.formatted(args)); | |
| } | |
| } | |
| """ >$1/src/main/java/core/App.java | |
| # add config file | |
| echo """debug=2 | |
| mode=DEVELOPMENT | |
| timeout=3000 | |
| """ >$1/src/main/resources/config.properties | |
| git init -b main --quiet $1/ | |
| cd $1 | |
| git add . | |
| git commit -m "Create Project $1" | |
| echo "==> Project $1 created." |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
Use the
createscripts to create a simple Java project inspired by the Maven project structure :Create the project
Project Structure :
buildis the bash script to build your project.You need to define the following variables in the script:
Defining the jar output :
List jar dependencies for build and run :
libs/]Define entries for MANIFEST: