# -*-Python-*-

##
## SCons script for building Leap dictionaries
##

import os
import re
import string
import sys

defs = {
    'BSC'                : '@BSC@',
    'BSC_FLAGS_VERILOG'  : '@BSC_FLAGS_VERILOG@',
    'DICT_SRCS'          : '@DICT_SRCS@',
    'ROOT_DIR_SW_INC'    : '@ROOT_DIR_SW_INC@',
    'RRR_SRCS'           : '@RRR_SRCS@',
    'TMP_BSC_DIR'        : '@TMP_BSC_DIR@',
}

# Propagate environment from external state
env = Environment(ENV = os.environ, DEFS = defs)
env['ENV']['SHELL'] = '/bin/sh'

BSC = env['DEFS']['BSC']
BSC_FLAGS_VERILOG = env['DEFS']['BSC_FLAGS_VERILOG']
TMP_BSC_DIR = env['DEFS']['TMP_BSC_DIR']

inc_tgt = 'build/include'
dict_inc_tgt = inc_tgt + '/awb/dict'
rrr_inc_tgt = inc_tgt + '/awb/rrr'
hw_tgt = 'build/hw'

if env.GetOption('clean'):
    os.system('rm -rf build')
else:
    if not os.path.isdir(dict_inc_tgt):
        os.makedirs(dict_inc_tgt)
    if not os.path.isdir(rrr_inc_tgt):
        os.makedirs(rrr_inc_tgt)
    if not os.path.isdir(hw_tgt):
        os.makedirs(hw_tgt + '/' + TMP_BSC_DIR)

tgt = []


#
# What is the Bluespec compiler version?
#
bsc_version = 0
bsc_ostream = os.popen('bsc -verbose')
ver_regexp = re.compile('^Bluespec Compiler, version.*\(build ([0-9]+),')
for ln in bsc_ostream.readlines():
    m = ver_regexp.match(ln)
    if (m):
        bsc_version = int(m.group(1))
bsc_ostream.close()

if bsc_version == 0:
    print "Failed to get Bluespec compiler version"
    sys.exit(1)


inc_dirs = env['DEFS']['ROOT_DIR_SW_INC']

# Compile dictionary
#  NOTE: this must run even if there are no dictionary files.  It always
#  builds an init.h, even if it is empty.  That way any model can safely
#  include init.h in a standard place.

# First define an emitter that describes all the files generated by dictionaries
all_gen_bsv = []
def dict_emitter(target, source, env):
    global all_gen_bsv
    # Output file names are a function of the contents of dictionary files,
    # not the names of dictionary files.
    src_names = ''
    for s in source:
        src_names += ' ' + str(s)
    # Ask leap-dict for the output names based on the input files
    for d in os.popen('leap-dict --querymodules --src-inc ' + inc_dirs + ' ' + src_names).readlines():
        #
        # Querymodules describes both targets and dependence info.  Targets
        # are the first element of each line followed by a colon and a
        # space separated list of other dictionaries on which the target
        # depends, e.g.:
        #     STREAMS: STREAMS_A STREAMS_B
        #
        # Start by breaking the line into a one or two element array.  The
        # first element is a dictionary name.  The second element, if it
        # exists, is a list of other dictionaries on which the first element
        # depends.
        #
        tgt = d.rstrip().split(':')
        if (len(tgt) > 1):
            tgt[1] = [x for x in tgt[1].split(' ') if x != '']

        # Add to the target list for the build
        target.append(dict_inc_tgt + '/' + tgt[0] + '.bsh')
        target.append(dict_inc_tgt + '/' + tgt[0] + '.h')

        # Build a list of BSV files for building later
        bsv = [hw_tgt + '/' + tgt[0] + '_DICT.bsv']
        target.append(bsv[0])
        if (len(tgt) > 1):
            bsv.append([hw_tgt + '/' + x + '_DICT.bsv' for x in tgt[1]])
        all_gen_bsv.append(bsv)
    return target, source

# Define the dictionary builder
d_bld = Builder(action = 'leap-dict --src-inc ' + inc_dirs + ' --tgt-inc ' + dict_inc_tgt + ' --tgt-hw ' + hw_tgt + ' $SOURCES',
                emitter = dict_emitter)
env.Append(BUILDERS = {'DIC' : d_bld})

# Add dependence info computed by previous dictionary builds (it uses cpp).
for dic in env['DEFS']['DICT_SRCS'].split():
    d = '.depends-dic-' + os.path.basename(dic)
    env.ParseDepends(d, must_exist = False)

# Finally, request dictionary build
d_tgt = env.DIC(dict_inc_tgt + '/init.h', env['DEFS']['DICT_SRCS'].split())
tgt += d_tgt

# Add dependence info computed by previous RRR builds (it uses cpp).
for rrr in env['DEFS']['RRR_SRCS'].split():
    d = '.depends-rrr-' + os.path.basename(rrr)
    env.ParseDepends(d, must_exist = False)

# Compile RRR stubs
#  NOTE: like dictionaries, some files must be created even when no .rrr
#  files exist.
tgt += env.Command(rrr_inc_tgt + '/service_ids.h',
                   env['DEFS']['RRR_SRCS'].split(),
                   'leap-rrr-stubgen --incdirs ' + inc_dirs + ' --odir ' + rrr_inc_tgt + ' --mode stub --target hw --type server $SOURCES')

#
# Compile generated BSV stubs
#
def emitter_bo(target, source, env):
    target.append(str(target[0]).replace('.bo', '.bi'))
    return target, source

def compile_bo(source, target, env, for_signature):
    bdir = os.path.dirname(str(target[0]))

    # Older compilers don't put -bdir on the search path
    maybe_bdir_tgt = ''
    if (bsc_version < 15480):
        maybe_bdir_tgt = ':' + bdir

    cmd = BSC + ' ' + BSC_FLAGS_VERILOG + \
          ' -p +:' + inc_tgt + ':' + hw_tgt + maybe_bdir_tgt + \
          ' -bdir ' + bdir + ' -vdir ' + bdir + ' -simdir ' + bdir + ' ' + \
          str(source[0])
    return cmd

bsc = Builder(generator = compile_bo, suffix = '.bo', src_suffix = '.bsv',
              emitter = emitter_bo)

env.Append(BUILDERS = {'BSC' : bsc})

#
# Describe BSV builds.  At the same time collect a Python dictionary of the
# targets of the BSV builds.
#
bsv_targets = {}
for bsv in all_gen_bsv:
    bo = os.path.dirname(bsv[0]) + '/' + TMP_BSC_DIR + '/' + os.path.splitext(os.path.basename(bsv[0]))[0] + '.bo'
    bsv_targets[bsv[0]] = env.BSC(bo, bsv[0])
    tgt += bsv_targets[bsv[0]]

#
# Add BSV module dependence information.
#
for bsv in all_gen_bsv:
    if (len(bsv) > 1):
       for dep in bsv[1]:
           Depends(bsv_targets[bsv[0]], bsv_targets[dep])

#
# Build everything
#
if tgt != []:
    Default(tgt)

#
# Backwards compatability link
#
if not env.GetOption('clean') and not os.path.exists('build/include/asim'):
    os.symlink('awb', 'build/include/asim')
