sheclang/sheclang

178 lines
5.3 KiB
Bash
Executable file

#!/bin/bash
# sheclang - shebang (#!) meets automatic clang.
# 2021-09-06 (Public Domain)
# _____________________________________________________________________________
# I got tired of the turnaround time when debugging my code, and I don't want
# to make a full build system or frankly even maintain a ./build.sh script for
# each project. I'm well on the road to re-inventing another build system, but
# if we can make python a simple ./<my code>.py, why can't we do the same for
# C and C++?
#
# See hello-world.cpp for the source code. Install this guy wherever your bins
# are stored, try it, weep, then rejoice that you have one less buffer open.
#
# _____________________________________________________________________________
# Version History:
# 2023-07-24 - Updated to accept !# arguments seperated by --
# 2021-09-06 - Initial Release
# _____________________________________________________________________________
# LICENSE:
# Public Domain
function __sheclang_version() {
SHECLANG_VERSION="2023-07-24"
SV_LEN___="$SHECLANG_VERSION"
printf ""
printf "\e[4msheclang\e[0m: a she-bang (#!) for your C/C++ files.\n"
printf "\e[4mversion\e[0m: $SV_LEN___\n"
tput sgr0
}
function __sheclang_usage() {
SHECLANG_VERSION="2023-07-24"
cat << EOF
usage:
1. add "#!/usr/bin/env shebang" to your main C/C++ file,
2. mark the file as executable
3. try to execute it.
4. Profit.
For advanced usage information, see the project README file.
EOF
}
set -e
#O_IFS=${IFS}
export IFS=$'\n'
COMPILER="clang++"
SHECLANG_VERBOSE=${SHECLANG_VERBOSE:-false}
###################
## Phase 1: Input Collection & Argument Checking
if [[ $# -lt 1 ]]; then
printf "sheclang requires at least the build file arugment.\n" >&2
__sheclang_usage
exit
fi
case "$1" in
-v|-V|--version)
__sheclang_version
exit
;;
--help|-h)
__sheclang_version
__sheclang_usage
exit
;;
*)
;;
esac
# Load a global configuartion for a user
if [[ -e $HOME/.config/sheclang/config ]]; then
source $HOME/.config/sheclang/config
elif [[ -e $HOME/.sheclang ]]; then
source $HOME/.sheclang
fi
# Load a project specific config
if [[ -e $PWD/sheclang.conf ]]; then
source $PWD/sheclang.conf
elif [[ -e $PWD/.sheclang ]]; then
source $PWD/.sheclang
fi
# Collect arguments from command line
# First check to see if we have a -- indicating a split between arguments for clang and arguments for the tool itself
CLI_CC_ARGS=()
CLI_BIN_ARGS=()
FOUND_SPLIT=false
for ARG in $@; do
if [[ "$ARG" == "--" ]]; then
FOUND_SPLIT=true
continue
fi
if [[ "$FOUND_SPLIT" == "true" ]]; then
CLI_BIN_ARGS+=("$ARG")
else
CLI_CC_ARGS+=("$ARG")
fi
done
# If we never found the -- seperator, then all of the arguments are assumed to have been
# input arguments
if [[ "$FOUND_SPLIT" == "false" ]]; then
CLI_BIN_ARGS+=(${CLI_CC_ARGS[@]})
unset CLI_CC_ARGS
fi
# Next, remove the source code file as the root executable
file_input="${CLI_BIN_ARGS[0]}"
# and validate that the file is indeed a source code file, with a sane extension
if [[ ! -f "$file_input" ]]; then
printf "ERROR: provided input file could not be found.\n"
printf " INFO: sheclang effective invocation:\n "
printf "%s " $0 ${CLI_CC_ARGS[@]} -- "${CLI_BIN_ARGS[@]}"
printf "\n"
exit 1
elif [[ "$SHECLANG_VERBOSE" == true ]]; then
printf " INFO: sheclang effective invocation:\n "
printf "%s " $0 ${CLI_CC_ARGS[@]} -- "${CLI_BIN_ARGS[@]}"
printf "\n"
fi
target_dir="$(dirname "${file_input}")"
# TODO: Validate the extension of the output file before fucking with it.
file_input_name="$(basename "${file_input}")"
file_output="${file_input_name%.*}"
file_extension="${file_input_name##*.}"
if [[ "$file_input_name" == "$file_output" ]]; then
(
printf "ERROR! Input and output file names match.\n"
printf " file input: %s\n" "$file_input_name"
printf " file output: %s\n" "$file_output"
) >&2
exit;
fi
###################
## Phase 2: File cleanup
# Build the copy the main source file without a hashbang line
file_input_corrected="${target_dir}/.sheclang.${file_output}.${file_extension}"
cp "${file_input}" "${file_input_corrected}"
# Convert the shebang into a comment line so the compiler doesn't see it
printf "//" | dd conv=notrunc of="${file_input_corrected}" \
count=2 bs=1 status=none
file_output="${target_dir}/${file_output}"
###################
## Phase 3: Build the file, and execute it with the remaining arguments.
unset 'CLI_BIN_ARGS[0]' # remove the source file from the BASH argument tree.
# Finally, merge the CLI build arguments with the pre-defined arguments
PRE_BUILD_FLAGS+=(${CLI_CC_ARGS[@]})
#POST_BUILD_FLAGS are defined in scripts only
RUNTIME_ARGS+=(${CLI_BIN_ARGS[@]})
# Run my prefered build script. Customize to your tastes
#$COMPILER ${PRE_BUILD_FLAGS[@]} -o "$file_output" "$file_input_corrected"
if [[ "$SHECLANG_VERBOSE" == true ]]; then
printf " INFO: sheclang effective compiler invocation:\n "
printf "%s " $COMPILER ${PRE_BUILD_FLAGS[@]} -o "$file_output" "$file_input_corrected" ${POST_BUILD_FLAGS[@]}
printf "\n"
fi
$COMPILER ${PRE_BUILD_FLAGS[@]} -o "$file_output" "$file_input_corrected" ${POST_BUILD_FLAGS[@]}
# And execute with the remaining arguments
if [[ "$SHECLANG_VERBOSE" == true ]]; then
printf " INFO: sheclang effective binary invocation:\n "
printf "%s " "$file_output" ${RUNTIME_ARGS[@]}
printf "\n"
fi
"$file_output" ${RUNTIME_ARGS[@]}