You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sh...@apache.org on 2021/12/21 17:33:34 UTC

[daffodil-vscode] branch main updated: Daffodil Debugger Classpaths:

This is an automated email from the ASF dual-hosted git repository.

shanedell pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git


The following commit(s) were added to refs/heads/main by this push:
     new d5f75ae  Daffodil Debugger Classpaths:
d5f75ae is described below

commit d5f75ae724876cd422968641632f5b09451c290b
Author: Shane Dell <sh...@gmail.com>
AuthorDate: Tue Dec 14 18:49:05 2021 -0500

    Daffodil Debugger Classpaths:
    
    - Create launch.json variable for daffodilDebugClasspath.
    - Add daffodilDebugClasspath to getConfig and default config
    - Create bash and bat templates for the scripts to run the debugger.
      - Created templates from the default of what we used.
      - Updated the templates to appened the value of the ENV DAFFODIL_DEBUG_CLASSPATH if it is set.
    - Update launch wizard to have an option for class paths.
      - Created two different check boxes that allows the input box to be replaced or appened with the files selected.
      - If folder selected the extension creates a string with all the full paths to all jars inside of the folder to it.
      - If multiple files are selected they will all be added.
    - Update issue with replace method, moved to replaceAll.
    - Fix issue with extension installed from VSIX not being able to load the style sheet or script for the launch wizard
    - Fix bad replacement of characters from the daffodilClasspath
    
    Closes #54
    Closes #57
---
 .vscodeignore                           |   2 +
 package.json                            |  16 +-
 server/core/src/templates/bash-template | 378 ++++++++++++++++++++++++++++++++
 server/core/src/templates/bat-template  | 204 +++++++++++++++++
 src/daffodilDebugger.ts                 |  26 ++-
 src/launchWizard/launchWizard.js        |  48 +++-
 src/launchWizard/launchWizard.ts        |  66 +++++-
 src/utils.ts                            |  13 +-
 tsconfig.json                           |   3 +-
 9 files changed, 742 insertions(+), 14 deletions(-)

diff --git a/.vscodeignore b/.vscodeignore
index 48c552a..4d00f3f 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -23,3 +23,5 @@
 !README.md
 !server/core/target/universal/daffodil-debugger-*.zip
 !snippets/*
+!src/launchWizard/launchWizard.js
+!src/launchWizard/styles.css
diff --git a/package.json b/package.json
index 046111a..f9378ec 100644
--- a/package.json
+++ b/package.json
@@ -265,6 +265,11 @@
                 "type": "boolean",
                 "description": "Open hexview on debug start",
                 "default": false
+              },
+              "daffodilDebugClasspath": {
+                "type": "string",
+                "description": "Additional classpaths to be exported to the debugger",
+                "default": ""
               }
             }
           }
@@ -284,7 +289,8 @@
             "debugServer": 4711,
             "openHexView": false,
             "openInfosetView": false,
-            "openInfosetDiffView": false
+            "openInfosetDiffView": false,
+            "daffodilDebugClasspath": ""
           }
         ],
         "configurationSnippets": [
@@ -305,7 +311,8 @@
               "debugServer": 4711,
               "openHexView": false,
               "openInfosetView": false,
-              "openInfosetDiffView": false
+              "openInfosetDiffView": false,
+              "daffodilDebugClasspath": ""
             }
           }
         ],
@@ -382,6 +389,11 @@
             "type": "boolean",
             "description": "Open hexview on debug start",
             "default": false
+          },
+          "daffodilDebugClasspath": {
+            "type": "string",
+            "description": "Additional classpaths to be exported to the debugger",
+            "default": ""
           }
         }
       }
diff --git a/server/core/src/templates/bash-template b/server/core/src/templates/bash-template
new file mode 100644
index 0000000..e150be7
--- /dev/null
+++ b/server/core/src/templates/bash-template
@@ -0,0 +1,378 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+###  ------------------------------- ###
+###  Helper methods for BASH scripts ###
+###  ------------------------------- ###
+
+die() {
+  echo "$@" 1>&2
+  exit 1
+}
+
+realpath () {
+(
+  TARGET_FILE="$1"
+  CHECK_CYGWIN="$2"
+
+  cd "$(dirname "$TARGET_FILE")"
+  TARGET_FILE=$(basename "$TARGET_FILE")
+
+  COUNT=0
+  while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
+  do
+      TARGET_FILE=$(readlink "$TARGET_FILE")
+      cd "$(dirname "$TARGET_FILE")"
+      TARGET_FILE=$(basename "$TARGET_FILE")
+      COUNT=$(($COUNT + 1))
+  done
+
+  if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
+    cd "$TARGET_FILE"
+    TARGET_FILEPATH=
+  else
+    TARGET_FILEPATH=/$TARGET_FILE
+  fi
+
+  # make sure we grab the actual windows path, instead of cygwin's path.
+  if [[ "x$CHECK_CYGWIN" == "x" ]]; then
+    echo "$(pwd -P)/$TARGET_FILE"
+  else
+    echo $(cygwinpath "$(pwd -P)/$TARGET_FILE")
+  fi
+)
+}
+
+# TODO - Do we need to detect msys?
+
+# Uses uname to detect if we're in the odd cygwin environment.
+is_cygwin() {
+  local os=$(uname -s)
+  case "$os" in
+    CYGWIN*) return 0 ;;
+    *)  return 1 ;;
+  esac
+}
+
+# This can fix cygwin style /cygdrive paths so we get the
+# windows style paths.
+cygwinpath() {
+  local file="$1"
+  if is_cygwin; then
+    echo $(cygpath -w $file)
+  else
+    echo $file
+  fi
+}
+
+# Make something URI friendly
+make_url() {
+  url="$1"
+  local nospaces=${url// /%20}
+  if is_cygwin; then
+    echo "/${nospaces//\\//}"
+  else
+    echo "$nospaces"
+  fi
+}
+
+# This crazy function reads in a vanilla "linux" classpath string (only : are separators, and all /),
+# and returns a classpath with windows style paths, and ; separators.
+fixCygwinClasspath() {
+  OLDIFS=$IFS
+  IFS=":"
+  read -a classpath_members <<< "$1"
+  declare -a fixed_members
+  IFS=$OLDIFS
+  for i in "${!classpath_members[@]}"
+  do
+    fixed_members[i]=$(realpath "${classpath_members[i]}" "fix")
+  done
+  IFS=";"
+  echo "${fixed_members[*]}"
+  IFS=$OLDIFS
+}
+
+# Fix the classpath we use for cygwin.
+fix_classpath() {
+  cp="$1"
+  if is_cygwin; then
+    echo "$(fixCygwinClasspath "$cp")"
+  else
+    echo "$cp"
+  fi
+}
+# Detect if we should use JAVA_HOME or just try PATH.
+get_java_cmd() {
+  # High-priority override for Jlink images
+  if [[ -n "$bundled_jvm" ]];  then
+    echo "$bundled_jvm/bin/java"
+  elif [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
+    echo "$JAVA_HOME/bin/java"
+  else
+    echo "java"
+  fi
+}
+
+echoerr () {
+  echo 1>&2 "$@"
+}
+vlog () {
+  [[ $verbose || $debug ]] && echoerr "$@"
+}
+dlog () {
+  [[ $debug ]] && echoerr "$@"
+}
+execRunner () {
+  # print the arguments one to a line, quoting any containing spaces
+  [[ $verbose || $debug ]] && echo "# Executing command line:" && {
+    for arg; do
+      if printf "%s\n" "$arg" | grep -q ' '; then
+        printf "\"%s\"\n" "$arg"
+      else
+        printf "%s\n" "$arg"
+      fi
+    done
+    echo ""
+  }
+
+  # we use "exec" here for our pids to be accurate.
+  exec "$@"
+}
+addJava () {
+  dlog "[addJava] arg = '$1'"
+  java_args+=( "$1" )
+}
+addApp () {
+  dlog "[addApp] arg = '$1'"
+  app_commands+=( "$1" )
+}
+addResidual () {
+  dlog "[residual] arg = '$1'"
+  residual_args+=( "$1" )
+}
+addDebugger () {
+  addJava "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$1"
+}
+
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+is_function_defined() {
+  declare -f "$1" > /dev/null
+}
+
+# Attempt to detect if the script is running via a GUI or not
+# TODO - Determine where/how we use this generically
+detect_terminal_for_ui() {
+  [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+  # SPECIAL TEST FOR MAC
+  [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+}
+
+# Processes incoming arguments and places them in appropriate global variables.  called by the run method.
+process_args () {
+  local no_more_snp_opts=0
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+             --) shift && no_more_snp_opts=1 && break ;;
+       -h|-help) usage; exit 1 ;;
+    -v|-verbose) verbose=1 && shift ;;
+      -d|-debug) debug=1 && shift ;;
+
+    -no-version-check) no_version_check=1 && shift ;;
+
+           -mem) echo "!! WARNING !! -mem option is ignored. Please use -J-Xmx and -J-Xms" && shift 2 ;;
+     -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
+
+          -main) custom_mainclass="$2" && shift 2 ;;
+
+     -java-home) require_arg path "$1" "$2" && jre=`eval echo $2` && java_cmd="$jre/bin/java" && shift 2 ;;
+
+ -D*|-agentlib*|-XX*) addJava "$1" && shift ;;
+                 -J*) addJava "${1:2}" && shift ;;
+                   *) addResidual "$1" && shift ;;
+    esac
+  done
+
+  if [[ no_more_snp_opts ]]; then
+    while [[ $# -gt 0 ]]; do
+      addResidual "$1" && shift
+    done
+  fi
+
+  is_function_defined process_my_args && {
+    myargs=("${residual_args[@]}")
+    residual_args=()
+    process_my_args "${myargs[@]}"
+  }
+}
+
+# Actually runs the script.
+run() {
+  # TODO - check for sane environment
+
+  # process the combined args, then reset "$@" to the residuals
+  process_args "$@"
+  set -- "${residual_args[@]}"
+  argumentCount=$#
+
+  #check for jline terminal fixes on cygwin
+  if is_cygwin; then
+    stty -icanon min 1 -echo > /dev/null 2>&1
+    addJava "-Djline.terminal=jline.UnixTerminal"
+    addJava "-Dsbt.cygwin=true"
+  fi
+
+  # check java version
+  if [[ ! $no_version_check ]]; then
+    java_version_check
+  fi
+
+  if [ -n "$custom_mainclass" ]; then
+    mainclass=("$custom_mainclass")
+  else
+    mainclass=("${app_mainclass[@]}")
+  fi
+
+  # Now we check to see if there are any java opts on the environment. These get listed first, with the script able to override them.
+  if [[ "$JAVA_OPTS" != "" ]]; then
+    java_opts="${JAVA_OPTS}"
+  fi
+
+  if [[ "$DAFFODIL_DEBUG_CLASSPATH" != "" ]]; then
+    app_classpath="$app_classpath:$DAFFODIL_DEBUG_CLASSPATH"
+  fi
+
+  # run sbt
+  execRunner "$java_cmd" \
+    ${java_opts[@]} \
+    "${java_args[@]}" \
+    -cp "$(fix_classpath "$app_classpath")" \
+    "${mainclass[@]}" \
+    "${app_commands[@]}" \
+    "${residual_args[@]}"
+
+  local exit_code=$?
+  if is_cygwin; then
+    stty icanon echo > /dev/null 2>&1
+  fi
+  exit $exit_code
+}
+
+# Loads a configuration file full of default command line options for this script.
+loadConfigFile() {
+  cat "$1" | sed $'/^\#/d;s/\r$//'
+}
+
+# Now check to see if it's a good enough version
+# TODO - Check to see if we have a configured default java version, otherwise use 1.6
+java_version_check() {
+  readonly java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}')
+  if [[ "$java_version" == "" ]]; then
+    echo
+    echo No java installations was detected.
+    echo Please go to http://www.java.com/getjava/ and download
+    echo
+    exit 1
+  else
+    local major=$(echo "$java_version" | cut -d'.' -f1)
+    if [[ "$major" -eq "1" ]]; then
+     local major=$(echo "$java_version" | cut -d'.' -f2)
+    fi
+    if [[ "$major" -lt "6" ]]; then
+      echo
+      echo The java installation you have is not up to date
+      echo $app_name requires at least version 1.6+, you have
+      echo version $java_version
+      echo
+      echo Please go to http://www.java.com/getjava/ and download
+      echo a valid Java Runtime and install before running $app_name.
+      echo
+      exit 1
+    fi
+  fi
+}
+
+###  ------------------------------- ###
+###  Start of customized settings    ###
+###  ------------------------------- ###
+usage() {
+ cat <<EOM
+Usage: $script_name [options]
+
+  -h | -help         print this message
+  -v | -verbose      this runner is chattier
+  -d | -debug        enable debug output for the launcher script
+  -no-version-check  Don't run the java version check.
+  -main <classname>  Define a custom main class
+  -jvm-debug <port>  Turn on JVM debugging, open at the given port.
+
+  # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
+  -java-home <path>         alternate JAVA_HOME
+
+  # jvm options and output control
+  JAVA_OPTS          environment variable, if unset uses "$java_opts"
+  -Dkey=val          pass -Dkey=val directly to the java runtime
+  -J-X               pass option -X directly to the java runtime
+                     (-J is stripped)
+
+  # special option
+  --                 To stop parsing built-in commands from the rest of the command-line.
+                     e.g.) enabling debug and sending -d as app argument
+                     \$ ./start-script -d -- -d
+
+In the case of duplicated or conflicting options, basically the order above
+shows precedence: JAVA_OPTS lowest, command line options highest except "--".
+Available main classes:
+	org.apache.daffodil.debugger.dap.DAPodil
+EOM
+}
+
+###  ------------------------------- ###
+###  Main script                     ###
+###  ------------------------------- ###
+
+declare -a residual_args
+declare -a java_args
+declare -a app_commands
+declare -r real_script_path="$(realpath "$0")"
+declare -r app_home="$(realpath "$(dirname "$real_script_path")")"
+# TODO - Check whether this is ok in cygwin...
+declare -r lib_dir="$(realpath "${app_home}/../lib")"
+declare -a app_mainclass=(org.apache.daffodil.debugger.dap.DAPodil)
+
+declare -r script_conf_file="${app_home}/../conf/application.ini"
+declare -a app_classpath="$lib_dir/org.apache.daffodil.daffodil-debugger-1.0.0-SNAPSHOT.jar:$lib_dir/org.scala-lang.scala-library-2.12.13.jar:$lib_dir/org.apache.daffodil.daffodil-sapi_2.12-3.1.0.jar:$lib_dir/org.apache.daffodil.daffodil-runtime1_2.12-3.1.0.jar:$lib_dir/ch.qos.logback.logback-classic-1.2.3.jar:$lib_dir/com.microsoft.java.com.microsoft.java.debug.core-0.31.1.jar:$lib_dir/co.fs2.fs2-io_2.12-3.0.4.jar:$lib_dir/com.monovore.decline-effect_2.12-2.1.0.jar:$lib_dir/org.typeleve [...]
+
+# java_cmd is overrode in process_args when -java-home is used
+declare java_cmd=$(get_java_cmd)
+
+# if configuration files exist, prepend their contents to $@ so it can be processed by this runner
+[[ -f "$script_conf_file" ]] && set -- $(loadConfigFile "$script_conf_file") "$@"
+
+run "$@"
+stupid thing
diff --git a/server/core/src/templates/bat-template b/server/core/src/templates/bat-template
new file mode 100644
index 0000000..35b06f8
--- /dev/null
+++ b/server/core/src/templates/bat-template
@@ -0,0 +1,204 @@
+@REM Licensed to the Apache Software Foundation (ASF) under one or more
+@REM contributor license agreements.  See the NOTICE file distributed with
+@REM this work for additional information regarding copyright ownership.
+@REM The ASF licenses this file to You under the Apache License, Version 2.0
+@REM (the "License"); you may not use this file except in compliance with
+@REM the License.  You may obtain a copy of the License at
+@REM
+@REM     http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+
+@REM daffodil-debugger launcher script
+@REM
+@REM Environment:
+@REM JAVA_HOME - location of a JDK home dir (optional if java on path)
+@REM CFG_OPTS  - JVM options (optional)
+@REM Configuration:
+@REM DAFFODIL_DEBUGGER_config.txt found in the DAFFODIL_DEBUGGER_HOME.
+@setlocal enabledelayedexpansion
+@setlocal enableextensions
+
+@echo off
+
+
+if "%DAFFODIL_DEBUGGER_HOME%"=="" (
+  set "APP_HOME=%~dp0\\.."
+
+  rem Also set the old env name for backwards compatibility
+  set "DAFFODIL_DEBUGGER_HOME=%~dp0\\.."
+) else (
+  set "APP_HOME=%DAFFODIL_DEBUGGER_HOME%"
+)
+
+set "APP_LIB_DIR=%APP_HOME%\lib\"
+
+rem Detect if we were double clicked, although theoretically A user could
+rem manually run cmd /c
+for %%x in (!cmdcmdline!) do if %%~x==/c set DOUBLECLICKED=1
+
+rem FIRST we load the config file of extra options.
+set "CFG_FILE=%APP_HOME%\DAFFODIL_DEBUGGER_config.txt"
+set CFG_OPTS=
+call :parse_config "%CFG_FILE%" CFG_OPTS
+
+rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config.
+set _JAVA_OPTS=%JAVA_OPTS%
+if "!_JAVA_OPTS!"=="" set _JAVA_OPTS=!CFG_OPTS!
+
+rem We keep in _JAVA_PARAMS all -J-prefixed and -D-prefixed arguments
+rem "-J" is stripped, "-D" is left as is, and everything is appended to JAVA_OPTS
+set _JAVA_PARAMS=
+set _APP_ARGS=
+
+set "APP_CLASSPATH=%APP_LIB_DIR%\org.apache.daffodil.daffodil-debugger-1.0.0-SNAPSHOT.jar;%APP_LIB_DIR%\org.scala-lang.scala-library-2.12.13.jar;%APP_LIB_DIR%\org.apache.daffodil.daffodil-sapi_2.12-3.1.0.jar;%APP_LIB_DIR%\org.apache.daffodil.daffodil-runtime1_2.12-3.1.0.jar;%APP_LIB_DIR%\ch.qos.logback.logback-classic-1.2.3.jar;%APP_LIB_DIR%\com.microsoft.java.com.microsoft.java.debug.core-0.31.1.jar;%APP_LIB_DIR%\co.fs2.fs2-io_2.12-3.0.4.jar;%APP_LIB_DIR%\com.monovore.decline-effect_2.1 [...]
+set "APP_MAIN_CLASS=org.apache.daffodil.debugger.dap.DAPodil"
+set "SCRIPT_CONF_FILE=%APP_HOME%\conf\application.ini"
+
+rem Bundled JRE has priority over standard environment variables
+if defined BUNDLED_JVM (
+  set "_JAVACMD=%BUNDLED_JVM%\bin\java.exe"
+) else (
+  if "%JAVACMD%" neq "" (
+    set "_JAVACMD=%JAVACMD%"
+  ) else (
+    if "%JAVA_HOME%" neq "" (
+      if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe"
+    )
+  )
+)
+
+if "%_JAVACMD%"=="" set _JAVACMD=java
+
+rem Detect if this java is ok to use.
+for /F %%j in ('"%_JAVACMD%" -version  2^>^&1') do (
+  if %%~j==java set JAVAINSTALLED=1
+  if %%~j==openjdk set JAVAINSTALLED=1
+)
+
+rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style
+set JAVAOK=true
+if not defined JAVAINSTALLED set JAVAOK=false
+
+if "%JAVAOK%"=="false" (
+  echo.
+  echo A Java JDK is not installed or can't be found.
+  if not "%JAVA_HOME%"=="" (
+    echo JAVA_HOME = "%JAVA_HOME%"
+  )
+  echo.
+  echo Please go to
+  echo   http://www.oracle.com/technetwork/java/javase/downloads/index.html
+  echo and download a valid Java JDK and install before running daffodil-debugger.
+  echo.
+  echo If you think this message is in error, please check
+  echo your environment variables to see if "java.exe" and "javac.exe" are
+  echo available via JAVA_HOME or PATH.
+  echo.
+  if defined DOUBLECLICKED pause
+  exit /B 1
+)
+
+rem if configuration files exist, prepend their contents to the script arguments so it can be processed by this runner
+call :parse_config "%SCRIPT_CONF_FILE%" SCRIPT_CONF_ARGS
+
+call :process_args %SCRIPT_CONF_ARGS% %%*
+
+set _JAVA_OPTS=!_JAVA_OPTS! !_JAVA_PARAMS!
+
+if defined CUSTOM_MAIN_CLASS (
+    set MAIN_CLASS=!CUSTOM_MAIN_CLASS!
+) else (
+    set MAIN_CLASS=!APP_MAIN_CLASS!
+)
+
+if defined DAFFODIL_DEBUG_CLASSPATH (
+    set APP_CLASSPATH="%APP_CLASSPATH%;%DAFFODIL_DEBUG_CLASSPATH%"
+)
+
+
+rem Call the application and pass all arguments unchanged.
+"%_JAVACMD%" !_JAVA_OPTS! !DAFFODIL_DEBUGGER_OPTS! -cp "%APP_CLASSPATH%" %MAIN_CLASS% !_APP_ARGS!
+
+@endlocal
+
+exit /B %ERRORLEVEL%
+
+
+rem Loads a configuration file full of default command line options for this script.
+rem First argument is the path to the config file.
+rem Second argument is the name of the environment variable to write to.
+:parse_config
+  set _PARSE_FILE=%~1
+  set _PARSE_OUT=
+  if exist "%_PARSE_FILE%" (
+    FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%_PARSE_FILE%") DO (
+      set _PARSE_OUT=!_PARSE_OUT! %%i
+    )
+  )
+  set %2=!_PARSE_OUT!
+exit /B 0
+
+
+:add_java
+  set _JAVA_PARAMS=!_JAVA_PARAMS! %*
+exit /B 0
+
+
+:add_app
+  set _APP_ARGS=!_APP_ARGS! %*
+exit /B 0
+
+
+rem Processes incoming arguments and places them in appropriate global variables
+:process_args
+  :param_loop
+  call set _PARAM1=%%1
+  set "_TEST_PARAM=%~1"
+
+  if ["!_PARAM1!"]==[""] goto param_afterloop
+
+
+  rem ignore arguments that do not start with '-'
+  if "%_TEST_PARAM:~0,1%"=="-" goto param_java_check
+  set _APP_ARGS=!_APP_ARGS! !_PARAM1!
+  shift
+  goto param_loop
+
+  :param_java_check
+  if "!_TEST_PARAM:~0,2!"=="-J" (
+    rem strip -J prefix
+    set _JAVA_PARAMS=!_JAVA_PARAMS! !_TEST_PARAM:~2!
+    shift
+    goto param_loop
+  )
+
+  if "!_TEST_PARAM:~0,2!"=="-D" (
+    rem test if this was double-quoted property "-Dprop=42"
+    for /F "delims== tokens=1,*" %%G in ("!_TEST_PARAM!") DO (
+      if not ["%%H"] == [""] (
+        set _JAVA_PARAMS=!_JAVA_PARAMS! !_PARAM1!
+      ) else if [%2] neq [] (
+        rem it was a normal property: -Dprop=42 or -Drop="42"
+        call set _PARAM1=%%1=%%2
+        set _JAVA_PARAMS=!_JAVA_PARAMS! !_PARAM1!
+        shift
+      )
+    )
+  ) else (
+    if "!_TEST_PARAM!"=="-main" (
+      call set CUSTOM_MAIN_CLASS=%%2
+      shift
+    ) else (
+      set _APP_ARGS=!_APP_ARGS! !_PARAM1!
+    )
+  )
+  shift
+  goto param_loop
+  :param_afterloop
+
+exit /B 0
diff --git a/src/daffodilDebugger.ts b/src/daffodilDebugger.ts
index b849725..9929392 100644
--- a/src/daffodilDebugger.ts
+++ b/src/daffodilDebugger.ts
@@ -24,6 +24,7 @@ import { deactivate } from './adapter/extension'
 import { LIB_VERSION } from './version'
 import XDGAppPaths from 'xdg-app-paths'
 import * as path from 'path'
+import { regexp } from './utils'
 
 const xdgAppPaths = XDGAppPaths({ name: 'daffodil-dap' })
 
@@ -113,7 +114,7 @@ export async function getDebugger(
         // If debugging the extension without vsix installed make sure debugger is created
         if (!filePath.includes('.vscode/extension')) {
           if (!fs.existsSync(filePath)) {
-            let baseFolder = context.asAbsolutePath('./')
+            let baseFolder = context.asAbsolutePath('.')
             child_process.execSync(
               `cd ${baseFolder} && sbt universal:packageBin`
             )
@@ -147,9 +148,9 @@ export async function getDebugger(
           "kill -9 $(ps -ef | grep 'daffodil' | grep 'jar' | awk '{ print $2 }') || return 0"
         ) // ensure debugger server not running and
         child_process.execSync(
-          `chmod +x ${rootPath.replace(' ', '\\ ')}/${artifact.name}/bin/${
-            artifact.scriptName
-          }`
+          `chmod +x ${rootPath.replace(regexp['space'], '\\ ')}/${
+            artifact.name
+          }/bin/${artifact.scriptName}`
         ) // make sure debugger is executable
       }
 
@@ -180,12 +181,29 @@ export async function getDebugger(
         return stopDebugging()
       }
 
+      let workspaceFolder = vscode.workspace.workspaceFolders
+        ? vscode.workspace.workspaceFolders[0].uri.fsPath
+        : vscode.Uri.parse('').fsPath
+
+      // Get daffodilDebugger class paths to be added to the debugger
+      let daffodilDebugClasspath = config.daffodilDebugClasspath.includes(
+        '${workspaceFolder}'
+      )
+        ? config.daffodilDebugClasspath.replace(
+            regexp['workspace'],
+            workspaceFolder
+          )
+        : config.daffodilDebugClasspath
+
       // Start debugger in terminal based on scriptName
       let terminal = vscode.window.createTerminal({
         name: artifact.scriptName,
         cwd: `${rootPath}/daffodil-debugger-${daffodilVersion}-${LIB_VERSION}/bin/`,
         hideFromUser: false,
         shellPath: artifact.scriptName,
+        env: {
+          DAFFODIL_DEBUG_CLASSPATH: daffodilDebugClasspath,
+        },
       })
       terminal.show()
 
diff --git a/src/launchWizard/launchWizard.js b/src/launchWizard/launchWizard.js
index 547a3fd..b743320 100644
--- a/src/launchWizard/launchWizard.js
+++ b/src/launchWizard/launchWizard.js
@@ -18,6 +18,20 @@
 // Retrieve vscode api - Doing this multiple times causes issues with the scripts
 const vscode = acquireVsCodeApi();
 
+// Function to update which checkbox is checked for the classpath, replace/action
+function daffodilDebugClassAction(action) {
+    switch(action) {
+        case 'replace':
+            document.getElementById("daffodilDebugClasspathReplace").checked = true
+            document.getElementById("daffodilDebugClasspathAppend").checked = false
+            break
+        case 'append':
+            document.getElementById("daffodilDebugClasspathReplace").checked = false
+            document.getElementById("daffodilDebugClasspathAppend").checked = true
+            break
+    }
+}
+
 // Function to call extension to open file picker
 function filePicker(id, description) {
     vscode.postMessage({
@@ -67,6 +81,7 @@ function save() {
     var configSelectedValue = configSelectionBox.options[configSelectionBox.selectedIndex].value
     var updateOrCreate = configSelectedValue === 'New Config' ? 'create' : 'update'
     const name = configSelectedValue === 'New Config' ? document.getElementById('name').value : configSelectedValue
+    const daffodilDebugClasspath = document.getElementById('daffodilDebugClasspath').value
     const data = document.getElementById('data').value
     const debugServer = parseInt(document.getElementById('debugServer').value)
     const infosetOutputFilePath = document.getElementById('infosetOutputFilePath').value
@@ -98,7 +113,8 @@ function save() {
                 useExistingServer: useExistingServer,
                 openHexView: openHexView,
                 openInfosetView: openInfosetView,
-                openInfosetDiffView: openInfosetDiffView
+                openInfosetDiffView: openInfosetDiffView,
+                daffodilDebugClasspath: daffodilDebugClasspath
             }
         ]
     }
@@ -112,6 +128,7 @@ function save() {
 function updateConfigValues(config) {
     document.getElementById('name').value = config.name
     document.getElementById('data').value = config.data
+    document.getElementById('daffodilDebugClasspath').value = config.daffodilDebugClasspath
     document.getElementById('debugServer').value = parseInt(config.debugServer)
     document.getElementById('infosetOutputFilePath').value = config.infosetOutput['path'] ? config.infosetOutput['path'] : config.infosetOutputFilePath
     document.getElementById('infosetOutputType').value = config.infosetOutput['type'] ? config.infosetOutput['type'] : config.infosetOutputType
@@ -125,6 +142,32 @@ function updateConfigValues(config) {
     updateInfosetOutputType()
 }
 
+// Function for updating the classpath input box
+function updateClasspath(message) {
+    let action = ''
+
+    if (document.getElementById('daffodilDebugClasspathAppend').checked && !document.getElementById('daffodilDebugClasspathReplace').checked) {
+        action = 'append'
+    } else {
+        action = 'replace'
+    }
+    
+    if (action === 'append') {
+        let newValue = ''
+        var filesToAdd = message.value.split(":")
+
+        for(var i = 0; i < filesToAdd.length; i++) {
+            if (!document.getElementById('daffodilDebugClasspath').value.includes(filesToAdd[i])) {
+                newValue += filesToAdd[i] + ':'
+            }
+        }
+
+        document.getElementById('daffodilDebugClasspath').value = newValue + document.getElementById('daffodilDebugClasspath').value
+    } else {
+        document.getElementById('daffodilDebugClasspath').value = message.value
+    }
+}
+
 // Function that gets called by default to create and update the hex web view
 (function main() {
     // Listener for getting messages/data from the extension
@@ -141,6 +184,9 @@ function updateConfigValues(config) {
             case 'programUpdate':
                 document.getElementById('program').value = message.value
                 break
+            case 'daffodilDebugClasspathUpdate':
+                updateClasspath(message)
+                break
         }
     })
 } ());
diff --git a/src/launchWizard/launchWizard.ts b/src/launchWizard/launchWizard.ts
index e81d09f..d58fc7a 100644
--- a/src/launchWizard/launchWizard.ts
+++ b/src/launchWizard/launchWizard.ts
@@ -139,24 +139,55 @@ async function openFilePicker(description) {
     ? vscode.workspace.workspaceFolders[0].uri.fsPath
     : vscode.Uri.parse('').fsPath
 
+  let canSelectMany = description.includes('jar files/folder') ? true : false
+  let canSelectFolders = description.includes('jar files/folder') ? true : false
+
   let chosenFile = await vscode.window
     .showOpenDialog({
-      canSelectMany: false,
+      canSelectMany: canSelectMany,
       openLabel: description,
       canSelectFiles: true,
-      canSelectFolders: false,
+      canSelectFolders: canSelectFolders,
       title: description,
     })
-    .then((fileUri) => {
+    .then(async (fileUri) => {
       if (fileUri && fileUri[0]) {
-        return fileUri[0].fsPath
+        let stats = fs.statSync(fileUri[0].fsPath)
+
+        if (stats.isDirectory()) {
+          let basePath = fileUri[0].fsPath
+          let fileString = ''
+
+          fs.readdirSync(basePath).forEach((file) => {
+            if (file.includes('.jar')) {
+              fileString += `${basePath}/${file},`
+            }
+          })
+
+          return fileString.substring(0, fileString.length - 1) // make sure to remove comma at the end of the string
+        }
+
+        if (fileUri.length === 1) {
+          return fileUri[0].fsPath
+        } else {
+          let files = ''
+
+          fileUri.forEach((file) => {
+            if (file.fsPath.includes('.jar')) {
+              files += `${file.fsPath},`
+            }
+          })
+
+          return files.substring(0, files.length - 1) // make sure to remove comma at the end of the string
+        }
       }
 
       return ''
     })
 
   if (chosenFile.includes(rootPath)) {
-    chosenFile = '${workspaceFolder}/' + chosenFile.split('/').pop()
+    let regExp = new RegExp(rootPath, 'g')
+    chosenFile = chosenFile.replace(regExp, '${workspaceFolder}')
   }
 
   return chosenFile
@@ -290,6 +321,13 @@ class LaunchWizard {
     let stopOnEntry = defaultValues.stopOnEntry ? 'checked' : ''
     let trace = defaultValues.trace ? 'checked' : ''
     let useExistingServer = defaultValues.useExistingServer ? 'checked' : ''
+    let daffodilDebugClasspathAction =
+      defaultValues.daffodilDebugClasspathAction === 'append'
+        ? defaultConf.daffodilDebugClasspath
+        : 'replace'
+    let replaceCheck =
+      daffodilDebugClasspathAction !== 'append' ? 'checked' : ''
+    let appendCheck = daffodilDebugClasspathAction === 'append' ? 'checked' : ''
 
     let infosetOutputTypeSelect = ''
     let infosetOutputTypes = ['none', 'console', 'file']
@@ -337,6 +375,24 @@ class LaunchWizard {
         </p>
       </div>
 
+      <div id="daffodilDebugClasspathDiv" class="setting-div">
+        <p>Daffodil Debugger Classpath:</p>
+        <p class="setting-description">Additional classpaths to be added to the debugger.</p>
+
+        <label class="container" style="margin-top: 10px; margin-bottom: 0px;">Replace value in input box with selected files.
+          <input type="checkbox" id="daffodilDebugClasspathReplace" ${replaceCheck} onclick="daffodilDebugClassAction('replace')">
+          <span class="checkmark"></span>
+        </label>
+
+        <label class="container" style="margin-top: 10px; margin-bottom: 10px;">Append selected files to value in input box.
+          <input type="checkbox" id="daffodilDebugClasspathAppend" ${appendCheck} onclick="daffodilDebugClassAction('append')">
+          <span class="checkmark"></span>
+        </label>
+
+        <input class="file-input" value="${defaultValues.daffodilDebugClasspath}" id="daffodilDebugClasspath"/>
+        <button id="daffodilDebugClasspathBrowse" class="browse-button" type="button" onclick="filePicker('daffodilDebugClasspath', 'Select jar files/folder with desired jars')">Browse</button>
+      </div>
+
       <div id="dataDiv" class="setting-div">
         <p>Data:</p>
         <p class="setting-description">Absolute path to the input data file.</p>
diff --git a/src/utils.ts b/src/utils.ts
index dc235c0..2dc333e 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -20,6 +20,13 @@ import * as vscode from 'vscode'
 const defaultConf = vscode.workspace.getConfiguration()
 let currentConfig: vscode.ProviderResult<vscode.DebugConfiguration>
 
+export const regexp = {
+  comma: new RegExp(',', 'g'),
+  slash: new RegExp('/', 'g'),
+  space: new RegExp(' ', 'g'),
+  workspace: new RegExp('${workspaceFolder}', 'g'),
+}
+
 // Function to retrieve to the current debug config
 export function getCurrentConfg() {
   return currentConfig
@@ -78,7 +85,8 @@ export function getConfig(
   trace = false,
   openHexView = false,
   openInfosetView = false,
-  openInfosetDiffView = false
+  openInfosetDiffView = false,
+  daffodilDebugClasspath: string = ''
 ) {
   return {
     name: name,
@@ -116,5 +124,8 @@ export function getConfig(
     openInfosetDiffView: openInfosetDiffView
       ? openInfosetDiffView
       : defaultConf.get('openInfosetDiffView', false),
+    daffodilDebugClasspath: daffodilDebugClasspath
+      ? daffodilDebugClasspath
+      : defaultConf.get('daffodilDebugClasspath', ''),
   }
 }
diff --git a/tsconfig.json b/tsconfig.json
index d9555f3..ba4e1c7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -21,7 +21,8 @@
 		"target": "es6",
 		"outDir": "out",
 		"lib": [
-			"es6"
+			"es6",
+			"es2021"
 		],
 		"sourceMap": true,
 		"rootDir": "src",