#!/usr/bin/env python

# Copyright (c) 2017      IBM Corporation. All rights reserved.
# $COPYRIGHT$
#

import glob, os, re, shutil

projects= {'opal' : ["$(top_builddir)/opal/lib@OPAL_LIB_PREFIX@open-pal.la"],
           'orte' : ["$(top_builddir)/orte/lib@ORTE_LIB_PREFIX@open-rte.la"],
           'ompi' : ["$(top_builddir)/ompi/lib@OMPI_LIBMPI_NAME@.la"],
           'oshmem' : ["$(top_builddir)/oshmem/liboshmem.la"],
          }

no_anchor_file = []
missing_files = []
skipped_files = []
partly_files = []
updated_files = []

#
# Check of all of the libadd fields are accounted for in the LIBADD
# Return a list indicating which are missing (positional)
#
def check_libadd(content, libadd_field, project):
    global projects

    libadd_list = projects[project]
    libadd_missing = [True] * len(libadd_list)

    on_libadd = False
    for line in content:
        # First libadd line
        if re.search( r"^\s*"+libadd_field, line):
            # If line continuation, then keep searching after this point
            if line[-2] == '\\':
                on_libadd = True

            for idx, lib in enumerate(libadd_list):
                if True == libadd_missing[idx]:
                    if 0 <= line.find(lib):
                        libadd_missing[idx] = False

        # Line continuation
        elif True == on_libadd:
            for idx, lib in enumerate(libadd_list):
                if True == libadd_missing[idx]:
                    if 0 <= line.find(lib):
                        libadd_missing[idx] = False

            # No more line continuations, so stop processing
            if line[-2] != '\\':
                on_libadd = False
                break

    return libadd_missing

#
# Update all of the Makefile.am's with the proper LIBADD additions
#
def update_makefile_ams():
    global projects
    global no_anchor_file
    global missing_files
    global skipped_files
    global partly_files
    global updated_files

    for project, libadd_list in projects.items():
        libadd_str = " \\\n\t".join(libadd_list)

        print("="*40)
        print("Project: "+project)
        print("LIBADD:\n"+libadd_str)
        print("="*40)

        #
        # Walk the directory structure
        #
        for root, dirs, files in os.walk(project+"/mca"):
            parts = root.split("/")
            if len(parts) != 4:
                continue
            if parts[-1] == ".libs" or parts[-1] == ".deps" or parts[-1] == "base":
                continue
            if parts[2] == "common":
                continue

            print("Processing: "+root)

            #
            # Find Makefile.am
            #
            make_filename = os.path.join(root, "Makefile.am")
            if False == os.path.isfile( make_filename ):
                missing_files.append("Missing: "+make_filename)
                print("  ---> Error: "+make_filename+" is not present in this directory")
                continue

            #
            # Stearching for: mca_FRAMEWORK_COMPONENT_la_{LIBADD|LDFLAGS}
            # First scan file to see if it has an LIBADD / LDFLAGS
            #
            libadd_field  = "mca_"+parts[2]+"_"+parts[3]+"_la_LIBADD"
            ldflags_field = "mca_"+parts[2]+"_"+parts[3]+"_la_LDFLAGS"
            has_ldflags = False
            has_libadd  = False

            r_fd = open(make_filename, 'r')
            orig_content = r_fd.readlines()
            r_fd.close()
            libadd_missing = []

            for line in orig_content:
                if re.search( r"^\s*"+ldflags_field, line):
                    has_ldflags = True
                elif re.search( r"^\s*"+libadd_field, line):
                    has_libadd = True

            if True == has_libadd:
                libadd_missing = check_libadd(orig_content, libadd_field, project)

            #
            # Sanity Check: Was there an anchor field.
            # If not skip, we might need to manually update or it might be a
            # static component.
            #
            if False == has_ldflags and False == has_libadd:
                no_anchor_file.append("No anchor ("+ldflags_field+"): "+make_filename)
                print("  ---> Error: Makefile.am does not contain necessary anchor")
                continue

            #
            # Sanity Check: This file does not need to be updated.
            #
            if True == has_libadd and all(False == v for v in libadd_missing):
                skipped_files.append("Skip: "+make_filename)
                print("       Skip: Already updated Makefile.am")
                continue

            #
            # Now go though and create a new version of the Makefile.am
            #
            r_fd = open(make_filename, 'r')
            w_fd = open(make_filename+".mod", 'w')

            num_libadds=0
            for line in r_fd:
                # LDFLAGS anchor
                if re.search( r"^\s*"+ldflags_field, line):
                    w_fd.write(line)
                    # If there is no LIBADD, then put it after the LDFLAGS
                    if False == has_libadd:
                        w_fd.write(libadd_field+" = "+libadd_str+"\n")
                # Existing LIBADD field to extend
                elif 0 == num_libadds and re.search( r"^\s*"+libadd_field, line):
                    parts = line.partition("=")
                    num_libadds += 1

                    if parts[0][-1] == '+':
                        w_fd.write(libadd_field+" += ")
                    else:
                        w_fd.write(libadd_field+" = ")

                    # If all libs are missing, then add the full string
                    # Otherwise only add the missing items
                    if all(True == v for v in libadd_missing):
                        w_fd.write(libadd_str)
                        # Only add a continuation if there is something to continue
                        if 0 != len(parts[2].strip()):
                            w_fd.write(" \\")
                        w_fd.write("\n")
                    else:
                        partly_files.append("Partly updated: "+make_filename)
                        for idx, lib in enumerate(libadd_list):
                            if True == libadd_missing[idx]:
                                w_fd.write(lib+" \\\n")

                    # Original content (unless it's just a line continuation)
                    if 0 != len(parts[2].strip()) and parts[2].strip() != "\\":
                        w_fd.write("\t"+parts[2].lstrip())

                # Non matching line, just echo
                else:
                    w_fd.write(line)

            r_fd.close()
            w_fd.close()

            #
            # Replace the original with the updated version
            #
            shutil.move(make_filename+".mod", make_filename)
            updated_files.append(make_filename)


if __name__ == "__main__":

    update_makefile_ams()

    print("")

    print("="*40);
    print("{:>3} : Files skipped".format(len(skipped_files)))
    print("="*40);

    print("="*40);
    print("{:>3} : Files updated, but had some libs already in place.".format(len(partly_files)))
    print("="*40);
    for fn in partly_files:
        print(fn)

    print("="*40);
    print("{:>3} : Files fully updated".format(len(updated_files)))
    print("="*40);
    for fn in updated_files:
        print(fn)

    print("="*40);
    print("{:>3} : Missing Makefile.am".format(len(missing_files)))
    print("="*40);
    for err in missing_files:
        print(err)

    print("="*40);
    print("{:>3} : Missing Anchor for parsing (might be static-only components)".format(len(no_anchor_file)))
    print("="*40);
    for err in no_anchor_file:
        print(err)