#=========================================================================
# Makefile
#=========================================================================
# This generated Makefile chains together the asic flow steps for this
# design according to the design's configuration file. For each asic flow
# step, a make target is generated that contains the commands specified in
# the step's configuration file.
#
# Extra commonly useful targets are also provided:
#
# - all      : Execute all steps
# - clean    : Clean up junk
# - debug-*  : Bring up debug view for many targets
# - graph    : Draws an ASCII dependency graph of steps
# - list     : List all configured steps + short names + other targets
# - print.*  : Print any Makefile variable
# - print    : Print contents of all variables in "print_list"
# - run-*    : Run command with Makefile variables available
# - runtimes : Prints runtimes for each executed step
# - seed     : Creates all build directories and seeds the flow steps
#
# Author : Christopher Torng
# Date   : March 26, 2018

#-------------------------------------------------------------------------
# Front matter
#-------------------------------------------------------------------------

.PHONY: default

default: all

# Disable built-in rules

.SUFFIXES:

#-------------------------------------------------------------------------
# Generated variables from configuration
#-------------------------------------------------------------------------

design    = @design@
flow_path = @flow_path@

include setup-adk.mk
include setup-design.mk
include setup-flow.mk

#-------------------------------------------------------------------------
# Directories
#-------------------------------------------------------------------------

# Base directory relative to the build directory. The base directory has
# the top-level steps directory, the design-flow directory, etc.

export relative_base_dir := ..

# Absolute base directory, which is marked as the base directory because
# it has the base directory marker file in it. This small shell script
# searches up the directory hierarchy until it finds the marker file. Then
# it labels that directory as the absolute base directory.

export base_dir_marker := .TOP
export base_dir        := $(shell \
  x=$${PWD};                      \
  while [ "$$x" != "/" ]; do      \
    x=$$(dirname "$$x");          \
    find "$$x" -maxdepth 1 -name $(base_dir_marker) | xargs -I {} dirname {}; \
  done;)

# Master steps directory in the base directory with all possible steps

export master_steps_dir = steps

# Master directory in the base directory with all assembled flows

export master_flows_dir = assembled-flows

# Build directories

export flow_dir       = flow
export plugins_dir    = plugins

export logs_dir       = logs
export reports_dir    = reports
export results_dir    = results

export collect_dir    = collect
export handoff_dir    = handoff
export timestamps_dir = timestamps

export githashes_dir  = githashes

#-------------------------------------------------------------------------
# Step management
#-------------------------------------------------------------------------
# Set up VPATH
#
# The VPATH variable is a list of directories to be searched for missing
# prerequisites.
#
# VPATH can be cleverly used to abstract the make tool's dependency
# tracking into "steps" that are easier for the user to understand and
# manipulate than the actual detailed Makefile dependencies. The interface
# is a list of flow steps (i.e., PHONY target files) in $(VPATH).

export VPATH = vpath

junk += $(VPATH)

# list
#
# Useful target that lists all configured steps and their short names. The
# abbreviated versions can be defined in each step's configuration file
# and should be defined as a variable called "abbr.name_of_the_step". If a
# variable for "descriptions.name_of_the_step" is provided, the
# description will also be listed.
#
# The output will look something like this:
#
#       (Long Name)       (Short Name)    (Description)
#       ----------------------------------------------------------------
#     - info                              Prints useful design information
#     - dc-synthesis      synth           Synthesize RTL into gates
#     - ...
#

list:
	@echo "--------------------------------------------------------------------------"
	@echo "Available steps targets"
	@echo "--------------------------------------------------------------------------"
	@echo
	@printf                          "%s %-21s   %-15s   %s\n" ' ' "(Long Name)"  "(Short Name)"     "(Description)"
	@echo                            "  ------------------------------------------------------------------------"
	@$(foreach step, $(steps),printf "%s %-21s   %-15s   %s\n" '-' "$(step)"      "$(abbr.$(step))"  $(descriptions.$(step)) ;)
	@echo
	@echo "Other useful targets"
	@echo
	@echo "- all      : Execute all steps"
	@echo "- clean    : Clean up junk"
	@echo "- debug-*  : Bring up debug view for many targets"
	@echo "- graph    : Draws an ASCII dependency graph of steps"
	@echo "- list     : List all configured steps + short names + other targets"
	@echo "- print.*  : Print any Makefile variable"
	@echo "- print    : Print contents of all variables in "print_list""
	@echo "- run-*    : Run command with Makefile variables available"
	@echo "- runtimes : Prints runtimes for each executed step"
	@echo "- seed     : Creates all build directories and seeds the flow steps"
	@echo

# runtimes
#
# Useful target that prints the runtimes for all steps given the
# timestamps for each step (e.g., innovus-place.start, innovus-place.end).
#
# Notes:
#
# - Using "date -f" reads and prints the date in the given file
# - Using "date +%s" formats into seconds since 1970-01-01 00:00:00 UTC
# - The "... 2>/dev/null || echo 0" returns "0" if the file does not exist
# - All bash variables need to be double escaped with $$myvariable
# - This sed command can be added to blank out zero-value printouts
#       sed "s/ 0 hr/     /; s/ 0 min/      /"
# - The total runtime is aggregated across all steps to compute the total.
#   This number might be different compared to subtracting the first/last
#   timestamps because the user might have paused between steps.

# Improvements from Khalid
#
#     * An flag is printed next to runtimes that are still executing
#       (i.e, believed to be still running)
#     * Runtimes for steps that are still executing will be running runtime
#     * If the end time is older than the start, we assume start is invalid
#     * if start time do not exist while end does, we assume step never started

runtimes:
	@echo "--------------------------------------------------------------------------"
	@echo "Runtimes"
	@echo "--------------------------------------------------------------------------"
	@(tot_secs=0; \
    $(foreach step, $(steps), \
      start_e=$(shell [[ -e $(timestamps_dir)/$(step).start ]] && echo "y"); \
      end_e=$(shell [[ -e $(timestamps_dir)/$(step).end ]] && echo "y"); \
      end_1=$(shell date -f $(timestamps_dir)/$(step).end +%s 2>/dev/null || echo 0); \
      start_1=$(shell date -f $(timestamps_dir)/$(step).start +%s 2>/dev/null || echo 0); \
      end_2=$$(if [[ -n "$$start_e" ]]; then echo $$end_1; else echo 0; fi); \
      start_2=$$start_1; \
      flag=; \
      flag=$$([[ "$$end_2" -lt "$$start_2" ]] && echo " <-- in progress"); \
      end=$$(if [[ -n "$$flag" ]]; then date +%s; else echo $$end_2; fi); \
      start=$$start_2; \
      step_secs=$$(( $$end - $$start )); \
      tot_secs=$$(($$tot_secs + $$step_secs)); \
      h=$$(( $$step_secs / 3600      )); \
      m=$$(( $$step_secs % 3600 / 60 )); \
      s=$$(( $$step_secs % 60        )); \
      printf '%-21s -- %2d hr %2d min %2d sec%s\n' $(step) $$h $$m $$s "$$flag"| \
        sed "s/ 0 hr/     /; s/ 0 min/      /";) \
    echo "--------------------------------------------------------------------------"; \
    h=$$(( $$tot_secs / 3600      )); \
    m=$$(( $$tot_secs % 3600 / 60 )); \
    s=$$(( $$tot_secs % 60        )); \
    printf '%-21s -- %2d hr %2d min %2d sec\n' "Total" $$h $$m $$s; )

# graph
#
# Useful target that draws an ASCII dependency graph of all steps

graph:
	@python $(base_dir)/utils/graph.py -f setup-flow.mk

junk += .graph # hidden dir used by utils/graph.py

# git hashes
#
# Each step that runs will mark the $(githashes_dir) with the commit hash
# at the time of running it. This can be useful to see which commit a
# build was on or which branch was being tracked over time.
#
# The following command generates a short hash and the dash-separated
# first line of the commit message for marking the step.

githash_cmd = \
	$(shell git log --oneline -1 | \
	          tr  ' !@\#$$%^&*()[]{}/:;"' '-------------------' | \
	          sed "s|'|-|g" | \
	          cut -c 1-80)

#-------------------------------------------------------------------------
# Seeding the build directory
#-------------------------------------------------------------------------
# The "seed" target prepares the directory structure and assembles the
# "steps" directory with all of the configured asic flow steps for this
# design according to the configuration file. It also pulls all of the
# design-specific plugins into the "plugins" directory.

seed:
# Generate all asic flow directories
	@mkdir -p $(VPATH) $(flow_dir) $(logs_dir)
	@mkdir -p $(reports_dir) $(results_dir)
	@mkdir -p $(collect_dir) $(handoff_dir) $(timestamps_dir)
	@mkdir -p $(githashes_dir)
# Seed links to the master steps into the flow directory
	@$(foreach x, $(steps), \
    (cd $(flow_dir) && \
      cp -rf ../$(relative_base_dir)/$(master_steps_dir)/$x .);)
#      ln -sf ../$(relative_base_dir)/$(master_steps_dir)/$x);)
# Link in the setup make fragments for reference
#  @ln -sf @flow_path@/setup-adk.mk
#  @ln -sf @flow_path@/setup-design.mk
#  @ln -sf @flow_path@/setup-flow.mk
# Link in the plugins
#  @ln -sf @flow_path@/plugins
	@rm -rf ./$(plugins_dir)
	@cp -rf @flow_path@/plugins ./$(plugins_dir)
# Mark this step done
	@touch $(VPATH)/$@

junk += $(flow_dir) $(logs_dir)
junk += $(reports_dir) $(results_dir)
junk += $(collect_dir) $(handoff_dir) $(timestamps_dir)
junk += $(githashes_dir)
#junk += setup-adk.mk setup-design.mk setup-flow.mk
junk += $(plugins_dir)

#-------------------------------------------------------------------------
# Rules template for each step
#-------------------------------------------------------------------------
# This section defines the rules template used to generate the target for
# each asic flow step and "chain together" dependent steps.
#
# The minimum requirement to implement an asic flow step is to create a
# directory called (name of the step) in the master steps directory which
# contains the file "configure.mk", and this file must specify the
# variable "commands.(name of the step)" specifying how to run the step.
#
# For example, if the step name is "myflowstep", then the configure.mk can
# specify:
#
#     commands.myflowstep = echo "Running myflowstep"
#
# The build system uses the following template to include the
# "configure.mk" for the step and then essentially just tells the step to
# run itself by calling the specified command. The template also collects
# handoffs from each of the dependency steps, collects timestamps for
# pretty printing later, and handles misc support functions.

# step_template
#
# - $(1): Name of the step (e.g., "dc-synthesis")
#
# Additional features
#
# - ascii.$(1)
#     - (optional)
#     - This hook can be defined in the step configure.mk to print a
#     pretty banner before executing the step, which is visible as text
#     flies by.
#
# - abbr.$(1)
#     - (optional)
#     - This hook can be used to create a short alias target for the step
#
# - extra_dependencies.$(1)
#     - (optional)
#     - The step configure can define this variable to draw any additional
#     custom dependencies for this step.
#
# - skipvpath.$(1)
#     - (optional)
#     - If defined, the build system will not use VPATH for this step

define step_template

# Generate variables for this step

flow_dir.$(1)    = $(flow_dir)/$(1)
plugins_dir.$(1) = $(plugins_dir)/$(1)

logs_dir.$(1)    = $(logs_dir)/$(1)
reports_dir.$(1) = $(reports_dir)/$(1)
results_dir.$(1) = $(results_dir)/$(1)

collect_dir.$(1) = $(collect_dir)/$(1)
handoff_dir.$(1) = $(handoff_dir)/$(1)

# Include the step makefile fragment

include $$(relative_base_dir)/$$(master_steps_dir)/$(1)/configure.mk

# Primary command target for this step

$(1): $$(dependencies.$(1)) $$(extra_dependencies.$(1))
# Mark the git commit hash used for this step
	@rm -f ./$$(githashes_dir)/[$(1)]-*
	@touch $$(githashes_dir)/[$(1)]-$$(githash_cmd)
# Collect files from handoffs of dependencies
	@rm -rf ./$$(collect_dir.$(1))
	@mkdir -p $$(collect_dir.$(1))
	@$$(foreach dep, $$(dependencies.$(1)), \
     if test x$$(handoff_dir.$$(dep)) != x; then true; \
       $$(foreach file, $$(wildcard $$(handoff_dir.$$(dep))/*), \
         (cd $$(collect_dir.$(1)) && ln -sf ../../$$(file));) \
     fi;)
# Execute the step
	@date > $$(timestamps_dir)/$(1).start
ifdef ascii.$(1)
	$$(ascii.$(1))
endif
	$$(commands.$(1))
	@date > $$(timestamps_dir)/$(1).end
ifndef skipvpath.$(1)
	@touch $(VPATH)/$(1)
endif

# Alias short name target for this step

ifdef abbr.$(1)
$$(abbr.$(1)): $(1)
endif

endef

#-------------------------------------------------------------------------
# Generate targets for all steps
#-------------------------------------------------------------------------

$(foreach step, $(steps), \
  $(eval $(call step_template,$(step))))

.PHONY: all

all: $(dependencies.all)

#-------------------------------------------------------------------------
# Regenerate configure information
#-------------------------------------------------------------------------

configure_prereq = \
  $(base_dir)/configure.ac \
  $(base_dir)/ctx.m4

$(base_dir)/configure : $(configure_prereq)
	cd $(base_dir) && autoconf

config_status_prereq = \
  $(base_dir)/configure

config.status : $(config_status_prereq)
	./config.status --recheck

makefile_prereq = \
  config.status \
  $(base_dir)/Makefile.in

Makefile : $(makefile_prereq)
	./config.status

#-------------------------------------------------------------------------
# Misc
#-------------------------------------------------------------------------

# clean
#
# Try to prefix with "./" to avoid calling "rm -rf /" due to unresolved
# variables. Also remove directories starting with "s.", which is a
# namespace for useful shortcut directories that the user wants quick
# access to.

.PHONY: clean

clean:
	rm -rf $(foreach x, $(junk),./$x)
	rm -rf ./s.*
	rm -rf ./summary-synthesis.txt
	rm -rf ./summary-place-route.txt
	rm -rf ./summary-power.txt
	rm -rf ./activity-scaled.saif ./activity.saif
	rm -rf ./clk-def.tcl ./parasitics_command.log ./pt_shell_command.log
	rm -rf ./power.rpt

# print.%
#
# This target makes it easier to debug Makefile rules by allowing you to
# print any variable. For example, "make print.junk" prints the value of
# the junk variable.

print.% :
	@echo $* = $($*)

# print
#
# Print the values of any variables in the print_list

print:
	@echo "--------------------------------------------------------------------------"
	@echo "Variables in print_list"
	@echo "--------------------------------------------------------------------------"
	@$(foreach var, $(print_list), \
       printf "%30.30s -- %38.38s .\n" "$(var)" "$($(var))";)
	@echo

# run-%
#
# Generic target to execute commands with environment variables set from
# the Makefile.
#
# Note that one particularly useful target is "make run-bash", which gives
# you a new shell with all of the exported environment variables from the
# Makefile available to use.

run-%:
	$*

# Colors for printouts

echo_green   = '\033[92m'
echo_nocolor = '\033[0m'

