-
-
Save timjstewart/180dbff04d61774e7074 to your computer and use it in GitHub Desktop.
a script that makes creating quick Java projects and experiments easy.
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 | |
| # A script to make it very easy to try things in Java. | |
| # | |
| # Pros: | |
| # | |
| # * Create Java project quickly | |
| # * Create unit test files quickly | |
| # * Create source code files quickly | |
| # * Run unit tests easily | |
| # * Run Java main methods easily | |
| # * Project files compatible with Maven directory structure. | |
| # | |
| # Cons: | |
| # | |
| # * Doesn't download jar dependencies like mvn, gradle | |
| # * No ${EDITOR} integration. | |
| # * Not compatible with IDEs | |
| # * No debugging support | |
| # The path to the pom.xml file (it may not exist) | |
| POM_FILE=./pom.xml | |
| # The directory where source files (not including tests) go. | |
| SRC_DIR=./src/main/java | |
| # The directory where test source code goes | |
| TEST_DIR=./src/test/java | |
| # The directory that contains all Jar files | |
| JARS_DIR=./jars | |
| # The directory that contains all compiled class files (from both the | |
| # src and test directories). | |
| OUTPUT_DIR=./target/classes | |
| # Hidden config file (empty at the moment). | |
| CONFIG_FILE=./.jconfig | |
| # The base URL for downloading Jar files. | |
| MAVEN_REPO="https://repo1.maven.org/maven2" | |
| function usage() | |
| { | |
| if [ "$#" == "1" ] ; then | |
| >&2 echo "command usage: $1" | |
| else | |
| >&2 echo "usage: j <command> [<param>...]" | |
| >&2 echo | |
| >&2 echo " Commands:" | |
| >&2 echo | |
| >&2 echo " build builds all source code (not tests)" | |
| >&2 echo " clean deletes all compiled class files" | |
| >&2 echo " cp print classpath" | |
| >&2 echo " disasm disassemble a class file" | |
| >&2 echo " jars list jars" | |
| >&2 echo " create creates a project" | |
| >&2 echo " get-jar creates a project" | |
| >&2 echo " new creates a new class or test or one of each" | |
| >&2 echo " pom creates a pom.xml file" | |
| >&2 echo " run runs a specific class' main function." | |
| >&2 echo " test runs a specific test" | |
| >&2 echo " tests runs all tests" | |
| >&2 echo | |
| fi | |
| } | |
| function disassemble() | |
| { | |
| in_project_or_die | |
| local FQN=$1 | |
| if [ -z ${FQN} ] ; then | |
| usage "specify a fully qualified class name" | |
| exit 1 | |
| fi | |
| javap -c -cp $(get_class_path) ${FQN} | |
| } | |
| function get_class_name() | |
| { | |
| local fqn=$1 | |
| echo ${fqn##*.} | |
| } | |
| function get_package_name() | |
| { | |
| local fqn=$1 | |
| pkg=${fqn%.*} | |
| if [ "${pkg}" == "${fqn}" ] ; then | |
| echo | |
| else | |
| echo "${pkg}" | |
| fi | |
| } | |
| function get_file_name() | |
| { | |
| local fqn=$1 | |
| echo $(echo "$fqn" | tr '.' '/').java | |
| } | |
| function source_files() | |
| { | |
| find "${SRC_DIR}" -iname '*.java' | |
| } | |
| function test_files() | |
| { | |
| find "${TEST_DIR}" -iname '*Test.java' | |
| } | |
| function test_class_files() | |
| { | |
| find "${OUTPUT_DIR}" -iname '*Test.class' | |
| } | |
| function in_project() | |
| { | |
| if [ -e "${CONFIG_FILE}" ] ; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| function die() | |
| { | |
| if [ ! -z "$1" ] ; then | |
| >&2 echo "error: $1" | |
| else | |
| >&2 echo "error: unknown" | |
| fi | |
| exit 1 | |
| } | |
| function in_project_or_die() | |
| { | |
| in_project || die "must be in project to run that command." | |
| } | |
| function list_test_classes() | |
| { | |
| in_project_or_die | |
| for test in $(find "${TEST_DIR}" -iname '*Test.java') ; do | |
| local test_name=$(echo ${test#${TEST_DIR}/} | tr '/' '.') | |
| local test_name=${test_name%.java} | |
| >&2 echo " - ${test_name}" | |
| done | |
| } | |
| function list_jars() | |
| { | |
| in_project_or_die | |
| for jar in $(find ${JARS_DIR} -iname '*.jar') ; do | |
| >&2 echo " $(basename ${jar%.*})" | |
| done | sort | |
| } | |
| function get_jar() | |
| { | |
| in_project_or_die | |
| if [ "$#" != "3" ] ; then | |
| usage "get-jar GROUP_ID ARTIFACT_ID VERSION" | |
| return 1 | |
| fi | |
| in_project_or_die | |
| local GROUP_ID=$1 | |
| local GROUP_DIR=$(echo $GROUP_ID | tr '.' '/') | |
| local ARTIFACT_ID=$2 | |
| local VERSION=$3 | |
| local JAR_FILE="${JARS_DIR}/${GROUP_ID}/${ARTIFACT_ID}-${VERSION}.jar" | |
| mkdir -p $(dirname $JAR_FILE) | |
| if [ -e "${JAR_FILE}" ] ; then | |
| return 0 | |
| fi | |
| local JAR_URL="${MAVEN_REPO}/${GROUP_DIR}/${ARTIFACT_ID}/${VERSION}/${ARTIFACT_ID}-${VERSION}.jar" | |
| >&2 echo -n "downloading jar: ${GROUP_DIR}:${ARTIFACT_ID}:${VERSION} ..." | |
| if ! wget -q "${JAR_URL}" -P "$(dirname $JAR_FILE)" ; then | |
| >&2 echo | |
| >&2 echo " error: could not download jar:" | |
| >&2 echo " Group: ${GROUP_ID}" | |
| >&2 echo " Artifact: ${ARTIFACT_ID}" | |
| >&2 echo " Version: ${VERSION}" | |
| >&2 echo " From: ${JAR_URL}" | |
| return 1 | |
| fi > /dev/null | |
| >&2 echo " SUCCEEDED." | |
| return 0 | |
| } | |
| function jar_file_exists() | |
| { | |
| in_project_or_die | |
| local ARTIFACT_ID=$1 | |
| local VERSION=$2 | |
| if [ -e "${JARS_DIR}/${ARTIFACT_ID}-${VERSION}.jar" ] ; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| function clean() | |
| { | |
| in_project_or_die | |
| rm -rf "${OUTPUT_DIR}" | |
| } | |
| function ensure_test_jars() | |
| { | |
| in_project_or_die | |
| mkdir -p "${JARS_DIR}" | |
| local HAMCREST_VERSION=1.3 | |
| local JUNIT_VERSION=4.12 | |
| get_jar "org.hamcrest" "hamcrest-core" ${HAMCREST_VERSION} | |
| get_jar "junit" "junit" ${JUNIT_VERSION} | |
| } | |
| function get_class_path() | |
| { | |
| local jar_globs=$(find jars -type d -exec echo "{}/*:" \;) | |
| local jar_globs=$(echo $jar_globs | tr -d ' ') | |
| echo "./:${jar_globs}:${OUTPUT_DIR}" | |
| } | |
| function print_classpath() | |
| { | |
| echo $(get_class_path) | |
| } | |
| function get_target_vm() | |
| { | |
| echo "1.8" | |
| } | |
| function get_source_version() | |
| { | |
| echo "1.8" | |
| } | |
| function ensure_output_dir() | |
| { | |
| in_project_or_die | |
| mkdir -p "${OUTPUT_DIR}" | |
| } | |
| function build() | |
| { | |
| in_project_or_die | |
| >&2 echo "building source..." | |
| if [ ! -d "${SRC_DIR}" ] ; then | |
| >&2 echo "error: no src directory" | |
| exit 2 | |
| fi | |
| ensure_output_dir | |
| if ! javac \ | |
| -source $(get_source_version) \ | |
| -target $(get_target_vm) \ | |
| -cp "$(get_class_path)" \ | |
| -d "${OUTPUT_DIR}" \ | |
| $(source_files) | |
| then | |
| exit 1 | |
| fi | |
| } | |
| function build_tests() | |
| { | |
| in_project_or_die | |
| >&2 echo "building tests..." | |
| ensure_test_jars | |
| ensure_output_dir | |
| if [ ! -d "${TEST_DIR}" ] ; then | |
| >&2 echo "error: no test directory" | |
| exit 2 | |
| fi | |
| if ! javac \ | |
| -source $(get_source_version) \ | |
| -target $(get_target_vm) \ | |
| -cp "$(get_class_path)" \ | |
| -d "${OUTPUT_DIR}" \ | |
| $(test_files) | |
| then | |
| exit 1 | |
| fi | |
| } | |
| function run() | |
| { | |
| in_project_or_die | |
| local MAIN_CLASS=$1 | |
| if [ -z "${MAIN_CLASS}" ] ; then | |
| usage "run CLASS" | |
| return 1 | |
| fi | |
| >&2 echo "running ${MAIN_CLASS} ..." | |
| java -cp "$(get_class_path)" \ | |
| ${MAIN_CLASS} | |
| } | |
| function run_test() | |
| { | |
| in_project_or_die | |
| if [ "$#" == "0" ] ; then | |
| usage "test TEST_CLASS..." | |
| >&2 echo | |
| list_test_classes | |
| >&2 echo | |
| exit 1 | |
| fi | |
| build || exit 1 | |
| ensure_test_jars || exit 1 | |
| build_tests || exit 1 | |
| for test_class in $@ ; do | |
| >&2 echo "running test: ${test_class} ..." | |
| java -cp "${OUTPUT_DIR}:$(get_class_path)" \ | |
| org.junit.runner.JUnitCore \ | |
| ${test_class} | |
| done | |
| } | |
| function run_tests() | |
| { | |
| in_project_or_die | |
| build || exit 1 | |
| ensure_test_jars || exit 1 | |
| build_tests || exit 1 | |
| >&2 echo "running all tests..." | |
| for test in $(test_class_files) | |
| do | |
| local test_name=$(echo ${test#$OUTPUT_DIR/} | tr '/' '.') | |
| local test_name=${test_name%.class} | |
| >&2 echo "running test: ${test_name} ..." | |
| java -cp "${OUTPUT_DIR}:$(get_class_path)" \ | |
| org.junit.runner.JUnitCore \ | |
| ${test_name} | |
| done | |
| } | |
| function create_source_file() | |
| { | |
| in_project_or_die | |
| if [ "$#" == "0" ] ; then | |
| usage "new class CLASS_NAME..." | |
| return 1 | |
| fi | |
| for arg in $@ ; do | |
| local class_name=$(get_class_name "${arg}") | |
| local package_name=$(get_package_name "${arg}") | |
| local file_name="${SRC_DIR}/$(get_file_name ${arg})" | |
| mkdir -p $(dirname "${file_name}") | |
| if [ -e "${file_name}" ] ; then | |
| >&2 echo "warning: file exists: ${file_name}" | |
| continue | |
| fi | |
| if [ ! -z "${package_name}" ] ; then | |
| cat <<EOF > "${file_name}" | |
| package ${package_name}; | |
| EOF | |
| fi | |
| cat <<EOF >> "${file_name}" | |
| import java.io.*; // For file I/O | |
| import java.net.*; // For networking | |
| import java.util.*; // For collections | |
| import java.util.concurrent.*; // For concurrency | |
| import java.util.regex.*; // For matching text | |
| import java.util.stream.*; // For handling streams | |
| public class ${class_name} | |
| { | |
| public static void main(final String[] args) | |
| { | |
| System.out.println("${class_name} says, 'Hello, World!'"); | |
| } | |
| } | |
| EOF | |
| >&2 echo "created: ${file_name}" | |
| done | |
| } | |
| function create_test_file() | |
| { | |
| in_project_or_die | |
| if [ "$#" == "0" ] ; then | |
| usage "new test CLASS_NAME..." | |
| return 1 | |
| fi | |
| mkdir -p "${TEST_DIR}" | |
| for arg in $@ ; do | |
| local class_name=$(get_class_name "${arg}")Test | |
| local package_name=$(get_package_name "${arg}") | |
| local file_name="${TEST_DIR}/$(get_file_name ${arg}Test)" | |
| mkdir -p $(dirname "${file_name}") | |
| if [ -e "${file_name}" ] ; then | |
| >&2 echo "warning: file exists: ${file_name}" | |
| continue | |
| fi | |
| if [ ! -z "${package_name}" ] ; then | |
| cat <<EOF > "${file_name}" | |
| package ${package_name}; | |
| EOF | |
| fi | |
| cat <<EOF >> "${file_name}" | |
| import org.junit.*; | |
| import static org.junit.Assert.*; | |
| import static org.hamcrest.Matcher.*; | |
| import static org.hamcrest.core.Is.*; | |
| public class ${class_name} | |
| { | |
| @Before | |
| public void setUp() | |
| { | |
| } | |
| @After | |
| public void tearDown() | |
| { | |
| } | |
| @Test | |
| public void test() | |
| { | |
| assertTrue(true); | |
| } | |
| @Test(expected=Exception.class) | |
| public void testExpected() throws Exception | |
| { | |
| throw new Exception("Catch me!"); | |
| } | |
| @Test | |
| public void testMatcher() | |
| { | |
| final String name = "Tim"; | |
| assertThat(name, is("Tim")); | |
| } | |
| } | |
| EOF | |
| >&2 echo "created: ${file_name}" | |
| done | |
| } | |
| function create_project() | |
| { | |
| in_project && die "already in a project" | |
| local NAME=$1 | |
| if [ -z "${NAME}" ] ; then | |
| usage "create PROJECT_NAME" | |
| return 1 | |
| fi | |
| if [ -d "./${NAME}" ] ; then | |
| >&2 echo "error: project ${NAME} exists" | |
| return 1 | |
| fi | |
| mkdir -p ./${NAME}/jars | |
| touch "./${NAME}/${CONFIG_FILE}" | |
| pushd "${NAME}" > /dev/null | |
| >&2 echo "in directory: ${NAME}" | |
| >&2 echo | |
| create_source_file Main | |
| create_test_file Main | |
| popd > /dev/null | |
| >&2 echo | |
| >&2 echo "project ${NAME} created." | |
| } | |
| function create_pom() | |
| { | |
| in_project_or_die | |
| if [ -e "${POM_FILE}" ] ; then | |
| die "pom.xml file already exists" | |
| fi | |
| local name=$(pwd) | |
| local name=$(basename $name) | |
| cat <<EOF > "${POM_FILE}" | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| <modelVersion>4.0.0</modelVersion> | |
| <name>${name}</name> | |
| <groupId>${name}</groupId> | |
| <artifactId>${name}</artifactId> | |
| <version>0.1.0</version> | |
| <properties> | |
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| </properties> | |
| <dependencies> | |
| EOF | |
| for jar in $(find "${JARS_DIR}" -iname '*.jar') ; do | |
| jar=${jar#${JARS_DIR}/} | |
| jar=${jar%.jar} | |
| local pair=${jar%-*} | |
| local group=${pair%/*} | |
| local artifact=${pair#*/} | |
| local version=${jar##*-} | |
| cat <<EOF >> "${POM_FILE}" | |
| <dependency> | |
| <groupId>${group}</groupId> | |
| <artifactId>${artifact}</artifactId> | |
| <version>${version}</version> | |
| </dependency> | |
| EOF | |
| done | |
| cat <<EOF >> "${POM_FILE}" | |
| </dependencies> | |
| </project> | |
| EOF | |
| } | |
| function create_new() | |
| { | |
| in_project_or_die | |
| if [ "$#" == "0" ] ; then | |
| usage "new class|test|pair NAME" | |
| exit 1 | |
| else | |
| case "$1" in | |
| class) | |
| shift | |
| create_source_file $@ | |
| ;; | |
| test) | |
| shift | |
| create_test_file $@ | |
| ;; | |
| pair) | |
| shift | |
| create_source_file $@ | |
| create_test_file $@ | |
| ;; | |
| *) | |
| >&2 echo "error: don't know how to create a new '$1'." | |
| ;; | |
| esac | |
| fi | |
| } | |
| function main() | |
| { | |
| if [ "$#" == "0" ] ; then | |
| usage | |
| exit 1 | |
| else | |
| case "$1" in | |
| build) | |
| shift | |
| build $@ | |
| ;; | |
| run) | |
| shift | |
| build | |
| run $@ | |
| ;; | |
| clean) | |
| shift | |
| clean $@ | |
| ;; | |
| test) | |
| shift | |
| run_test $@ | |
| ;; | |
| jars) | |
| shift | |
| list_jars $@ | |
| ;; | |
| get-jar) | |
| shift | |
| get_jar $@ | |
| ;; | |
| tests) | |
| shift | |
| run_tests $@ | |
| ;; | |
| create) | |
| shift | |
| create_project $@ | |
| ;; | |
| cp|classpath) | |
| shift | |
| print_classpath $@ | |
| ;; | |
| pom) | |
| shift | |
| create_pom $@ | |
| ;; | |
| disasm) | |
| shift | |
| disassemble $@ | |
| ;; | |
| new) | |
| shift | |
| create_new $@ | |
| ;; | |
| *|help|--help|-h) | |
| shift | |
| usage $@ | |
| ;; | |
| esac | |
| fi | |
| } | |
| main $@ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment