Skip to content

Instantly share code, notes, and snippets.

@ichramm
Last active October 13, 2015 03:58
Show Gist options
  • Save ichramm/4136319 to your computer and use it in GitHub Desktop.
Save ichramm/4136319 to your computer and use it in GitHub Desktop.
longclaw: dependency-based personal builder (runs a DFS in the dependency-tree)
#!/bin/bash
#
# Smart builder for inConcert... ... ... ...
#
set -e
function info()
{
[[ -n "$TERM" && "$TERM" != "dumb" ]] && echo -e "$(tput setaf 2)$@$(tput sgr0)" || echo -e "$@";
}
function warn()
{
[[ -n "$TERM" && "$TERM" != "dumb" ]] && echo -e "$(tput bold)$(tput setaf 3)$@$(tput sgr0)" || echo -e "WARN: $@";
}
function error()
{
[[ -n "$TERM" && "$TERM" != "dumb" ]] && echo -e "$(tput bold)$(tput setaf 1)$@$(tput sgr0)" || echo -e "ERROR: $@";
}
# maps a project with its root directory
# base hash for iteration
declare -A map_projects
#map of dependencies by project name
declare -A map_dependencies
declare -A map_descriptions
declare -A map_makedirs
declare -A map_maketargets
declare -A map_cmdbefore
declare -A map_cmdafter
# marks projects as visited (like a DFS)
declare -A map_visited
this_dir=$(dirname `readlink -f $0`);
config_dir=$this_dir/config;
# check if INCONCERT_DEVEL is set
if [ -z "$INCONCERT_DEVEL" ];
then
warn "Environment variable INCONCERT_DEVEL is not set";
TEMP=$(readlink -f "$this_dir/../../");
warn "Setting INCONCERT_DEVEL to '$TEMP'";
export INCONCERT_DEVEL=$TEMP;
fi
# enable parallel compiling
if [[ -z "$MAKEOPTS" || "$MAKEOPTS" != *-j* ]];
then
cpu_cores=$(cat /proc/cpuinfo | grep "cpu cores" | head -1 | cut --delimiter=':' -f2);
if [ -n "$cpu_cores" -a "$cpu_cores" -eq "$cpu_cores" 2> /dev/null ]; then
((cpu_cores=cpu_cores+1));
else
cpu_cores=$(cat /proc/cpuinfo | grep processor | wc -l);
fi
MAKEOPTS="$MAKEOPTS -j$cpu_cores";
warn "Updating variable MAKEOPTS to: [$MAKEOPTS]";
fi
export INCONCERT_SOURCES="$INCONCERT_DEVEL/sources"
export MAKEGTARGET="$MAKE_TARGET"
info "Reading directory $config_dir...";
for file in $config_dir/*.cfg;
do
# cleanup the environment first
unset PROJECT_NAME;
unset PROJECT_DESC;
unset USES;
unset ROOT_DIR;
unset MAKE_DIRS;
unset MAKE_TARGET
unset BEFORE_BUILD;
unset AFTER_BUILD;
info " - Loading build file $file";
file_name=$(basename $file .cfg);
source $file;
# read project name or fallback to filename (without extension)
[[ -z "$PROJECT_NAME" ]] && warn "PROJECT_NAME is not set, falling back to $file_name" && PROJECT_NAME=$file_name;
# read project description (or fallback to project name when compiling)
[[ -z "$PROJECT_DESC" ]] && warn "PROJECT_DESC is not set, will fall back to $PROJECT_NAME" || map_descriptions[$PROJECT_NAME]="$PROJECT_DESC";
# read root directory or fallback to project name, will setup the key on the main map (the one used for transversing)
[[ -z "$ROOT_DIR" ]] && warn "ROOT_DIR is not set, falling back to $PROJECT_NAME" && ROOT_DIR=$PROJECT_NAME;
map_projects[$PROJECT_NAME]="$ROOT_DIR";
# setup make directories, if empty will fallback to '.'
[[ -n "$MAKE_DIRS" ]] && map_makedirs[$PROJECT_NAME]="$MAKE_DIRS";
[[ -n "$MAKE_TARGET" ]] && map_maketargets[$PROJECT_NAME]="$MAKE_TARGET";
# read and set dependencies
if [ -n "$USES" ]; then
info " -- Dependencies for $PROJECT_NAME: $USES";
map_dependencies[$PROJECT_NAME]="$USES";
else
info " -- $PROJECT_NAME has no dependencies";
fi
# read and set before build commands
[[ -n "$BEFORE_BUILD" ]] && map_cmdbefore[$PROJECT_NAME]="$BEFORE_BUILD";
#read and set after build commands
[[ -n "$AFTER_BUILD" ]] && map_cmdafter[$PROJECT_NAME]="$AFTER_BUILD";
done
function build_project_impl()
{
local project_name=$1;
local root_dir=$2
local current_target="";
current_dir=`pwd`;
cd "$INCONCERT_SOURCES/$root_dir";
info " Working on directory: `pwd`";
if [ "$LONGCLAW_RUN_COMMANDS" != "no" -a -n "${map_cmdbefore["$project_name"]}" ]; then
CMDLINE="${map_cmdbefore["$project_name"]}";
warn " Running before-build commands: $CMDLINE";
# run bash -c to avoid problems
bash -c "$CMDLINE";
fi;
[[ -n "${map_makedirs["$project_name"]}" ]] && makedirs=${map_makedirs["$project_name"]} || makedirs=".";
for make_dir in $makedirs;
do
if [ -n "$MAKEGTARGET" ]; then
set +e
current_target=$(make -C $make_dir -pn | grep "$MAKEGTARGET:");
set -e
if [ -z "$current_target" ]; then
warn "Makefile '$root_dir/$make_dir' does not have target $MAKEGTARGET";
else
current_target="$MAKEGTARGET";
fi
fi
if [ -z "$current_target" ]; then
current_target="${map_maketargets["$project_name"]}";
fi
if [ -n "$CONFIGURATION" ]; then
export Target=$CONFIGURATION;
else
export Target=Debug;
info " Running make Debug on directory $make_dir";
make $MAKEOPTS -C $make_dir $current_target;
export Target=Release;
fi
info " Running make $Target on directory $make_dir";
make $MAKEOPTS -C $make_dir $current_target;
done
if [ "$LONGCLAW_RUN_COMMANDS" != "no" -a -n "${map_cmdafter["$project_name"]}" ]; then
CMDLINE="${map_cmdafter["$project_name"]}";
warn " Running post-build commands: $CMDLINE";
# run bash -c to avoid problems
bash -c "$CMDLINE";
fi;
unset Target;
cd $current_dir;
}
function build_project()
{
local project_name=$1;
local root_dir="${map_projects["$project_name"]}"
local project_desc;
[[ -n "${map_descriptions["$project_name"]}" ]] && project_desc=${map_descriptions["$project_name"]} || project_desc=$project_name;
info "Building project '$project_desc'";
# mark this project as visited
map_visited[$project_name]="1";
if [ -n "${map_dependencies["$project_name"]}" ]; then
for depend in ${map_dependencies["$project_name"]}; do
if [ -z "${map_visited["$depend"]}" ]; then
build_project $depend;
fi
done
fi;
info "Really building project '$project_desc'";
build_project_impl "$project_name" "$root_dir"
}
info "Starting build process...";
if [ -n "$1" ]; then
[[ -z "${map_projects["$1"]}" ]] && error "Project $1 not found" && exit 1;
build_project $1;
else
for project in ${!map_projects[@]}; do
if [ -z "${map_visited["$project"]}" ]; then
build_project $project;
fi
done
fi
info "\nDone!";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment