#!/bin/bash
set -e

# Generates and compiles the random architecture.
RPG_LANG="cpp"
. $(dirname ${0})/internal-generate-shared.sh

# The first command line argument is the profile name.
check_profile ${1}

create_data_dir ${2}
echo Info: Generating into ${DATA_DIR:?}.

# Use the standard preprocessor to generate the
# configuration file in the target directory.

CONFIG_FILE=${DATA_DIR:?}/generator.conf

cpp ${PROFILE_FILE:?} ${CONFIG_FILE:?}

# Also prepare the input files from all modules.

# An optional third parameter can specify a directory with modules
# already prepared, which is just symlinked in that case. This is
# done in order to conserve disk space in batch runs.

INPUT_DIR=${3}
if [[ -z ${INPUT_DIR} ]]
then
	# Only one directory level is copied to avoid
	# conflicts with versioning subdirectories.
	
	mkdir "${DATA_DIR:?}/input"
	for MODULE in ${ROOT_DIR:?}/src/modules/cpp/*
	do
	  if [[ -d "${MODULE:?}/input" ]]
	  then
	    cp ${MODULE:?}/input/* "${DATA_DIR:?}/input"
	  fi 
	done
else
	ln -s "${INPUT_DIR}" "${DATA_DIR:?}/input"
fi

# Prepare the template file that the generator uses.

cp ${ROOT_DIR:?}/src/templates/cpp/main.cpp ${DATA_DIR:?}/main.tpl

# Generate the application :-) ...

"${ROOT_DIR:?}/bin/rpg" "${CONFIG_FILE:?}" "${DATA_DIR:?}"
echo
echo "Info: Generation done, compiling and linking..."

# One drawback of the generator is that the libraries
# to be included with the application are all
# specified here, regardless of the
# modules actually used.

# Create a separate script that compiles the generated application.
# This is to facilitate compiling with different flags or debugging.
BUILD_FILE=${DATA_DIR:?}/build
echo "#!/bin/bash" > ${BUILD_FILE:?}
echo "g++ ${BUILD_CC_OPTS--O3} -Wall -I${ROOT_DIR:?}/src/include -I${ROOT_DIR:?}/src/modules/cpp/ -I. -c main.cpp" >> ${BUILD_FILE:?}
chmod +x ${BUILD_FILE:?}
# Right now, the build script can create an object file, which is what we need.
pushd ${DATA_DIR:?} >/dev/null
./build
popd >/dev/null

############################################################################
# Symbol and section renaming
############################################################################

# Prepare the directory and common files for renaming.
RENAME_PATH=$(mktemp -d)
INPUT=${ROOT_DIR:?}/bin/modules.a

# Dump candidates for renaming from the module library.
# A symbol is a candidate for renaming if one of this holds:
#  - it is weak and defined
#  - it is global and defined
#  - it is in the common section
  
SYMBOLS_FILE=${RENAME_PATH:?}/symbols
objdump --syms --wide ${INPUT:?} > ${SYMBOLS_FILE:?}

# The seemingly unnecessary cat commands ending the line are to prevent the script from bailing out when grep does not match.   
cat ${SYMBOLS_FILE:?} | egrep "^[0-9a-f]+ g...... " | cat > ${SYMBOLS_FILE:?}-global
cat ${SYMBOLS_FILE:?} | egrep "^[0-9a-f]+ .w..... " | cat > ${SYMBOLS_FILE:?}-weak
cat ${SYMBOLS_FILE:?}-global ${SYMBOLS_FILE:?}-weak | egrep --invert-match "^[0-9a-f]+ ....... \*UND\* " | cat > ${SYMBOLS_FILE:?}-defined
cat ${SYMBOLS_FILE:?} | egrep "^[0-9a-f]+ ....... \*COM\*" | cat > ${SYMBOLS_FILE:?}-common

# Create list of common sections.
  
HEADERS_FILE=${RENAME_PATH:?}/headers
objdump --headers --wide ${INPUT:?} > ${HEADERS_FILE:?}
 
SECTIONS_FILE=${RENAME_PATH:?}/sections
cat ${HEADERS_FILE:?} | sed -rn "/LINK_ONCE_DISCARD/s/^ *[0-9]+ ([^.][^ ]*) .*$/\1/p" > ${SECTIONS_FILE:?}

# Create a list of instantiated module classes from the object file.
objdump --syms ${DATA_DIR:?}/main.o \
| sed -nr "s/^[0-9a-f]+ +\*UND\*\t[0-9a-f]+ _ZN[0-9]+([^0-9].+)_instantiated_([^0-9].+)D[0-9]+Ev$/\1 \2/p" \
| while read CLASS NAME
do
  MANGLED=${CLASS:?}_instantiated_${NAME:?}
  OUTPUT=${DATA_DIR:?}/modules_${NAME:?}.a
  
  RENAME_FILE=${RENAME_PATH:?}/rename_${NAME}
  cat ${SYMBOLS_FILE:?}-defined ${SYMBOLS_FILE:?}-common \
  | sed -nr "s/^[0-9a-f]+ ....... [^\t]+\t[0-9a-f]+ (.*)$/\1/p" \
  | sort -u \
  > ${RENAME_FILE:?}

  # Rename the external module methods using class names.
  
  # Type information is renamed. 
  sed -ri "s/^((_ZT[VIS])${#CLASS}${CLASS:?})$/\1 \2${#MANGLED}${MANGLED:?}/" ${RENAME_FILE:?}
  # Constructors and destructors are renamed.
  sed -ri "s/^(_ZN${#CLASS}${CLASS:?}([CD][0-9]+[A-Za-z]+))$/\1 _ZN${#MANGLED}${MANGLED:?}\2/" ${RENAME_FILE:?}
  # Virtual methods are renamed.
  sed -ri "s/^(_ZN${#CLASS}${CLASS:?}(4initEiz))$/\1 _ZN${#MANGLED}${MANGLED:?}\2/" ${RENAME_FILE:?}   
  sed -ri "s/^(_ZN${#CLASS}${CLASS:?}(6deinitEv))$/\1 _ZN${#MANGLED}${MANGLED:?}\2/" ${RENAME_FILE:?}
  sed -ri "s/^(_ZN${#CLASS}${CLASS:?}(13internal_workEv))$/\1 _ZN${#MANGLED}${MANGLED:?}\2/" ${RENAME_FILE:?}   
  # All the other symbols are just prefixed to avoid clashes.
  sed -ri "s/^([^ ]+)$/\1 ${MANGLED:?}_\1/" ${RENAME_FILE:?}

  # Do all the renaming in one invocation.
  
  objcopy --redefine-syms=${RENAME_FILE:?} $(cat ${SECTIONS_FILE:?} | while read SECTION ; do echo --rename-section ${SECTION:?}=${MANGLED:?}_${SECTION:?} ; done) ${INPUT:?} ${OUTPUT:?}
  
done
# cleanup
rm -rf ${RENAME_PATH:?}

############################################################################

# Now continue with the build script.
echo "g++ ${BUILD_CC_OPTS--O3} -Wall main.o modules_*.a ${ROOT_DIR:?}/bin/shared.a -lpthread -lfftw3 -lrt -o main" >> ${BUILD_FILE:?}
pushd ${DATA_DIR:?} >/dev/null
./build
popd >/dev/null

html_tidy "${DATA_DIR}/main.xml"

parse_modules "${DATA_DIR}/main.xml" "${DATA_DIR}/module-classes.out"

echo Info: Done.
echo ${DATA_DIR}
