Files
Mārtiņš Možeiko fdab8bd6b2 add github actions
2021-11-14 18:13:28 -08:00

656 lines
19 KiB
Bash
Executable File

#!/bin/bash
set -eo pipefail
###### Usage ##################################################################
# ./bld_core.sh <command> [arguments ...]
#
# command: compile
# ./bld_core.sh compile <source-file> [opts ...]
#
# Creates object files from a single source file and options with a compiler.
# Scans the source file for '//$' and '//' and pulls options out of the tokens
# in between, combines options from the source file with the passed options.
#
#
# command: link
# ./bld_core.sh link <out-name> [input-file ...] -- [opts ...]
#
# Creates binaries (executables and dynamic libraries) from objects,
# libraries, and options with a linker.
# First automatically compiles source files into objects.
# Object files can be specified as '<name>.o' or '<name>.obj', and will be
# automatically adjusted according to the selected compiler.
# Library files should be specified with '<name>.lib', and will be
# automatically adjusted according to the selected linker.
# If the option 'dll' appears in the list the linker will produce a dynamic
# library (or "shared object").
#
#
# command: lib
# ./bld_core.sh lib <out-name> [input-file ...] -- [opts ...]
#
# Creates library archives (.lib or .a) from objects.
# First automatically compiles source files into objects.
# Object files can be specified as '<name>.o' or '<name>.obj', and will be
# automatically adjusted according to the selected compiler.
###### Has Opt Function #######################################################
function bld_has_opt {
###### parse arguments ####################################################
local key_opt=$1
local opts=()
for ((i=2; i<=$#; i+=1)); do
opts+=(${!i})
done
###### scan ###############################################################
local has_key=0
for ((i=0;i<${#opts[@]};i+=1)); do
local opt=${opts[i]}
if [[ "$opt" == "$key_opt" ]]; then
has_key=1
break
fi
done
echo $has_key
}
###### Flags From Opts Function ###############################################
function bld_flags_from_opts {
###### parse arguments ####################################################
local in_file=$1
local opts=()
for ((i=2; i<=$#; i+=1)); do
opts+=(${!i})
done
###### load file ##########################################################
local flags_raw=()
IFS=$'\r\n' GLOBIGNORE='*' command eval 'flags_raw=($(cat $in_file))'
###### filter #############################################################
local flags=()
for ((i=0;i<${#flags_raw[@]};i+=1)); do
local flag=${flags_raw[i]}
###### skip blanks and comments #######################################
if [[ -z "${flag// }" ]]; then
continue
fi
if [[ "${flag:0:1}" == "#" ]]; then
continue
fi
###### parse line filters #############################################
local line_filters=()
while [[ $flag = *">"* ]]; do
line_filters+=("${flag%%>*}")
flag="${flag#*>}"
done
###### check filters ##################################################
local can_include=1
for ((j=0;j<${#line_filters[@]};j+=1)); do
can_include=0
for ((k=0;k<${#opts[@]};k+=1)); do
if [[ ${opts[k]} = ${line_filters[j]} ]]; then
can_include=1
break
fi
done
if [[ "$can_include" = "0" ]]; then
break
fi
done
if [[ "$can_include" = "1" ]]; then
flags+=("${flag}")
fi
done
echo "${flags[@]}"
}
###### Opts From Src Function #################################################
function bld_opts_from_src {
###### Split File Into Lines ##############################################
local in_file=$1
local tokens=($(grep "//\\$" $in_file))
###### Parse ##############################################################
local in_params_range="0"
local params=()
for ((i=0; i<${#tokens[@]}; i+=1)); do
local string="${tokens[i]}"
if [[ "$in_params_range" == "0" ]]; then
if [[ "$string" == "//$" ]]; then
in_params_range="1"
fi
elif [[ "$in_params_range" == "1" ]]; then
if [[ "${string:0:2}" == "//" ]]; then
break
fi
params+=($string)
fi
done
echo "${params[@]}"
}
###### Dedup Function #########################################################
function bld_dedup {
###### parse arguments ####################################################
local in=()
for ((i=1; i<=$#; i+=1)); do
in+=(${!i})
done
###### dedup ##############################################################
local out=()
for ((i=0; i<${#in[@]}; i+=1)); do
local string=${in[i]}
local is_dup="0"
for ((j=0; j<${#out[@]}; j+=1)); do
if [[ "$string" == "${out[j]}" ]]; then
is_dup="1"
break
fi
done
if [[ "$is_dup" == "0" ]]; then
out+=($string)
fi
done
echo "${out[@]}"
}
###### Compile Function #######################################################
function bld_compile {
###### parse arguments ####################################################
local in_file=$1
local opts=()
for ((i=2; i<=$#; i+=1)); do
opts+=(${!i})
done
if [ "$in_file" == "" ]; then
echo "compile: missing input file"
return 1
fi
###### finith in file #####################################################
local final_in_file=$root_path/$in_file
###### out file name ######################################################
local file_base=${final_in_file##*/}
local file_base_no_ext=${file_base%.*}
local out_file="$file_base_no_ext$dot_ext_obj"
###### finish options #####################################################
local src_opts=($(bld_opts_from_src $final_in_file))
local all_opts=($(bld_dedup $file_base ${opts[@]} ${src_opts[@]}))
###### diagnostics ########################################################
local diagnostics=$(bld_has_opt diagnostics ${all_opts[@]})
###### get real flags #####################################################
local flags_file=$bin_path/compiler_flags.txt
local flags=($(bld_flags_from_opts $flags_file ${all_opts[@]}))
###### get inc paths ######################################################
local paths_file=$bin_path/compiler_inc_paths.txt
local paths=($(bld_flags_from_opts $paths_file ${all_opts[@]}))
local incs=()
for ((i=0; i<${#paths[@]}; i+=1)); do
incs+=("-I$root_path/${paths[i]}")
done
###### move to output folder ##############################################
mkdir -p "$build_path"
cd $build_path
###### delete existing object file ########################################
rm -f "$out_file_base.o"
rm -f "$out_file_base.obj"
###### get flags ##########################################################
local all_flags="-c -I$src_path ${incs[@]} ${flags[@]}"
###### diagnostic output ##################################################
if [ "$diagnostics" == "1" ]; then
echo "cmp $final_in_file -- ${all_opts[@]}"
echo $compiler "$final_in_file" $all_flags
fi
###### print source file (if the compiler doesn't do it automatically) ####
if [ "$manually_print_target" == "1" ]; then
echo "$file_base"
fi
###### compile ############################################################
$compiler "$final_in_file" $all_flags
}
###### Link Function ##########################################################
function bld_link {
###### parse arguments ####################################################
local out_name=$1
local in_files=()
for ((i=2; i<=$#; i+=1)); do
if [ "${!i}" == "--" ]; then
break
fi
in_files+=(${!i})
done
local opts=()
for ((i+=1; i<=$#; i+=1)); do
opts+=(${!i})
done
if [ "$out_name" == "" ]; then
echo "link: missing output name"
return 1
fi
if [ "${#in_files}" == "0" ]; then
echo "link: missing input file(s)"
return 1
fi
###### finish options #####################################################
local all_opts=($(bld_dedup ${opts[@]}))
###### diagnostics ########################################################
local diagnostics=$(bld_has_opt diagnostics ${all_opts[@]})
###### sort in files ######################################################
local in_src=()
local in_obj=()
local in_lib=()
for ((i=0; i<${#in_files[@]}; i+=1)); do
local file="${in_files[i]}"
local ext="${file##*.}"
if [[ "$ext" == "c" || "$ext" == "cpp" ]]; then
in_src+=($file)
elif [[ "$ext" == "o" || "$ext" == "obj" ]]; then
in_obj+=($file)
elif [[ "$ext" == "lib" ]]; then
in_lib+=($file)
else
echo "WARNING: ignoring unrecognized file type $file"
fi
done
###### auto correct object files ##########################################
for ((i=0; i<${#in_obj[@]}; i+=1)); do
local file_name="${in_obj[i]}"
local base_name="${file_name%.*}"
in_obj[$i]="$base_name$dot_ext_obj"
done
###### compile source files ###############################################
for ((i=0; i<${#in_src[@]}; i+=1)); do
bld_compile "${in_src[i]}" ${all_opts[@]}
status=$?
if [ $status -ne 0 ]; then
exit $status
fi
done
###### intermediate object files ##########################################
local interm_obj=()
for ((i=0; i<${#in_src[@]}; i+=1)); do
local file_name="${in_src[i]}"
local base_name="${file_name##*/}"
local base_name_no_ext="${base_name%.*}"
interm_obj+=($base_name_no_ext$dot_ext_obj)
done
###### get real flags #####################################################
local flags_file=$bin_path/linker_flags.txt
local flags=$(bld_flags_from_opts $flags_file ${all_opts[@]})
###### link ###############################################################
local linker_kind="link"
if [ $linker == "clang" ]; then
linker_kind="clang"
fi
###### out file name ######################################################
local bin_kind="exe"
for ((i=0; i<${#all_opts[@]}; i+=1)); do
if [[ "${all_opts[i]}" == "dll" ]]; then
bin_kind="dll"
break
fi
done
dot_ext_out="$dot_ext_exe"
if [ "$bin_kind" == "dll" ]; then
dot_ext_out="$dot_ext_dll"
fi
out_file="$out_name$dot_ext_out"
###### move to output folder ##############################################
mkdir -p "$build_path"
cd $build_path
###### diagnostic output ##################################################
local final_in_files="${interm_obj[@]} ${in_obj[@]} ${in_lib[@]}"
if [ "$diagnostics" == "1" ]; then
echo "lnk $final_in_files -- ${all_opts[@]}"
fi
###### link ###############################################################
if [ "$linker_kind" == "link" ]; then
if [ "$diagnostics" == "1" ]; then
echo $linker -OUT:"$out_file" $flags $final_in_files
fi
echo "$out_file"
$linker -OUT:"$out_file" $flags $final_in_files
elif [ "$linker_kind" == "clang" ]; then
if [ "$diagnostics" == "1" ]; then
echo $linker -o "$out_file" $flags $final_in_files
fi
echo "$out_file"
$linker -o "$out_file" $flags $final_in_files
else
echo "ERROR: invokation not defined for this linker"
false
fi
status=$?
return $status
}
###### Library Function #######################################################
function bld_lib {
###### parse arguments ####################################################
local out_name=$1
local in_files=()
for ((i=2; i<=$#; i+=1)); do
if [ "${!i}" == "--" ]; then
break
fi
in_files+=(${!i})
done
local opts=()
for ((i+=1; i<=$#; i+=1)); do
opts+=(${!i})
done
if [ "$out_name" == "" ]; then
echo "lib: missing output name"
return 1
fi
if [ "${#in_files}" == "0" ]; then
echo "lib: missing input file(s)"
return 1
fi
###### finish options #####################################################
local all_opts=($(bld_dedup ${opts[@]}))
###### diagnostics ########################################################
local diagnostics=$(bld_has_opt diagnostics ${all_opts[@]})
###### sort in files ######################################################
local in_src=()
local in_obj=()
for ((i=0; i<${#in_files[@]}; i+=1)); do
local file="${in_files[i]}"
local ext="${file##*.}"
if [[ "$ext" == "c" || "$ext" == "cpp" ]]; then
in_src+=($file)
elif [[ "$ext" == "o" || "$ext" == "obj" ]]; then
in_obj+=($file)
else
echo "WARNING: ingnoring unrecgonized file type $file"
fi
done
###### auto correct object files ##########################################
for ((i=0; i<${#in_obj[@]}; i+=1)); do
local file_name="${in_obj[i]}"
local base_name="${file_name%.*}"
in_obj[$i]=$base_name$dot_ext_obj
done
###### compile source files ###############################################
for ((i=0; i<${#in_src[@]}; i+=1)); do
compile "${in_src[i]}" ${all_opts[@]}
local status=$?
if [ $status -ne 0 ]; then
return $status
fi
done
###### intermediate object files ##########################################
local interm_obj=()
for ((i=0; i<${#in_src[@]}; i+=1)); do
local file_name="${in_src[i]}"
local base_name="${file_name##*/}"
local base_name_no_ext="${base_name%.*}"
interm_obj+=($base_name_no_ext$dot_ext_obj)
done
###### out file name ######################################################
local out_file=""
if [ "$os" == "windows" ]; then
out_file="$out_name.lib"
elif [ "$os" == "linux" || "$os" == "mac" ]; then
out_file="lib$out_name.a"
else
echo "ERROR: static library output not defined for OS: $os"
fi
###### diagnostic output ##################################################
local final_in_files="${interm_obj[@]} ${in_obj[@]}"
if [ "$diagnostics" == "1" ]; then
echo "lib $final_in_files -- ${all_opts[@]}"
fi
###### print output file ##################################################
echo "$out_file"
###### move to output folder ##############################################
mkdir -p "$build_path"
cd $build_path
###### build library ######################################################
if [ "$os" == "windows" ]; then
lib -nologo -OUT:"$out_file" $final_in_files
elif [ "$os" == "linux" || "$os" == "mac" ]; then
# TODO(allen): invoke ar here - make sure to delete the original .a first
# because ar does not (seem) to replace the output file, just append
echo "TODO: implement ar path in bld_core.sh:bld_lib"
false
else
echo "ERROR: static library invokation not defined for OS: $os"
false
fi
status=$?
return $status
}
###### Unit Function ##########################################################
function bld_unit {
###### parse arguments ####################################################
local out_name=$1
local main_file=$2
in_files=()
for ((i=3; i<=$#; i+=1)); do
if [ "${!i}" == "--" ]; then
break
fi
in_files+=(${!i})
done
if [ "$out_name" == "" ]; then
echo "unit: missing output name"
return 1
fi
if [ "$main_file" == "" ]; then
echo "unit: missing main file"
return 1
fi
###### finish options #####################################################
local src_opts=$(bld_opts_from_src $root_path/$main_file)
local all_opts=($(bld_dedup $out_name ${src_opts[@]} ${implicit_opts[@]}))
###### link ###############################################################
bld_link $out_name $main_file ${in_files[@]} -- ${all_opts[@]}
status=$?
return $status
}
###### Show Ctx Function ######################################################
function bld_show_ctx {
local mod_opts=()
for ((i=0;i<${#implicit_opts[@]};i+=1)); do
local mod_opt="[${implicit_opts[i]}]"
mod_opts+=($mod_opt)
done
echo "${mod_opts[@]}"
}
###### Get Paths ##############################################################
og_path=$PWD
cd "$(dirname "$0")"
cd ..
root_path=$PWD
bin_path="$root_path/bin"
local_path="$root_path/local"
build_path="$root_path/build"
src_path="$root_path/source"
###### Crack Operating System From Environment ################################
os="undefined"
if [ "$OSTYPE" == "win32" ] ||
[ "$OSTYPE" == "msys" ]; then
os="windows"
elif [ "$OSTYPE" == "linux-gnu" ]; then
os="linux"
elif [[ "$OSTYPE" == "darwin"* ]]; then
os="mac"
fi
###### Implicit Options #######################################################
compiler=$($local_path/compiler.sh)
linker=$($local_path/linker.sh)
compile_mode=$($local_path/compile_mode.sh)
arch=$($local_path/arch.sh)
ctx=$($local_path/ctx.sh)
###### Apply Override #########################################################
parse_i="1"
override_list=()
if [ "${!parse_i}" == "override" ]; then
for ((parse_i+=1; parse_i<=$#; parse_i+=1)); do
arg="${!parse_i}"
if [ "$arg" == "--" ]; then
((parse_i+=1))
break
fi
if [[ "$arg" == *":"* ]]; then
arg_key="${arg%%:*}"
arg_val="${arg#*:}"
declare "${arg_key}=$arg_val"
override_list+=("[$arg]")
fi
done
fi
###### Finish Implicit Options ################################################
implicit_opts=($out_name $compiler $linker $compile_mode $os $arch $ctx)
###### Object File Extension ##################################################
dot_ext_obj=".o"
if [[ "$compiler" == "cl" ]]; then
dot_ext_obj=".obj"
fi
###### Binary File Extension ##################################################
dot_ext_exe=".exe"
dot_ext_dll=""
if [ "$os" == "windows" ]; then
dot_ext_dll=".dll"
elif [ "$os" == "linux" ] || [ "$os" == "mac" ]; then
dot_ext_dll=".so"
else
echo "ERROR: binary extension not defined for OS: $os"
fi
###### Binary File Extension ##################################################
manually_print_target="0"
if [ "$compiler" == "clang" ]; then
manually_print_target="1"
fi
###### Diagnostics ############################################################
top_diagnostics=$(bld_has_opt diagnostics ${implicit_opts[@]})
###### Overrides Diagnostics ##################################################
if [[ "$top_diagnostics" == "1" && "${#override_list}" != "0" ]]; then
echo "${override_list[@]} {"
fi
###### Control ################################################################
command=${!parse_i}
args=()
for ((parse_i+=1; parse_i<=$#; parse_i+=1)); do
args+=(${!parse_i})
done
if [ "$command" == "compile" ]; then
bld_compile ${args[@]}
elif [ "$command" == "link" ]; then
bld_link ${args[@]}
elif [ "$command" == "lib" ]; then
bld_lib ${args[@]}
elif [ "$command" == "unit" ]; then
bld_unit ${args[@]}
elif [ "$command" == "show_ctx" ]; then
bld_show_ctx
else
echo "'$command' not a recognized command"
fi
###### Overrides Diagnostics ##################################################
if [[ "$top_diagnostics" == "1" && "${#override_list}" != "0" ]]; then
echo "}"
fi
###### Restore Path ###########################################################
cd $og_path