Interface#

The interface directory within the OpenBLAS repository is critical, serving as a foundational component that bridges high-level BLAS API calls with their corresponding low-level, optimized implementations. The key point are that these contain…

Wrapper Functions: Housing C wrapper functions that which are the entry points for the API calls, ensuring that the correct, optimized kernel is executed based on the architecture and the specific operation being performed.

In essence, the interface directory encapsulates the strategic integration of compatibility, efficiency, and user accessibility, which is a pretty good design.

Buildsystem Perspective#

make handles each object file on its own, so that won’t be covered further. Let’s delve into the cmake perspective and then the equivalent meson perspective.

CMake OBJECT Library#

In CMake an OBJECT library compiles source files into object files without archiving them into a library file. This approach is useful for compiling code that will be used in multiple targets without recompiling the source multiple times. In the context of OpenBLAS, compiling the interface directory as an OBJECT library allows these compiled objects (wrappers and interface functions) to be reused across different final targets (e.g., shared and static versions of the library). The linker can then resolve symbols to the optimized implementations, which might be compiled separately and linked together in the final build step.

Meson static_library#

Similarly, in Meson, a static_library is a collection of object files archived together. When we compile source files from the interface directory into a static_library, we effectively prepare a library which contains all the wrapper functions which act as entry points to the optimized routines. These wrappers can invoke different implementations based on the compile-time or runtime conditions (e.g., architecture-specific optimizations). The unresolved symbols in this static library (like calls to the optimized kernels) are intentionally left for the linker to resolve, allowing for flexible linking with other parts of the project that implement these symbols.

Relevant discussions#

See gh-11591 for a discussion on porting cmake object libraries to meson.

Practicalities#

This means that the interface is pretty useful, since it includes the cblas_definitions.

import openblas_buildsys_snips.make.blas_kernel as opbk
import openblas_buildsys_snips._utils as oputil

from pathlib import Path
ml = Path.cwd() / Path("../../../tests/test_blas_symb/interface_Makefile")
lines = ml.read_text().split('\n')
opbk.parse_compilation_commands(oputil.pair_suffix_lines(lines), "min")
print(parsed_lines)

import re

def parse_blas_routine_commands(lines, base):
    """
    Parse BLAS routine compilation commands to extract types and directives.

    Parameters
    ----------
    lines : list of str
        Compilation commands extracted from a Makefile.
    base : str
        Base routine name with a '?' where type letters should be replaced.

    Returns
    -------
    dict
        Dictionary with routine base, types encountered, and associated defines and undefines.
    """
    type_prefixes = ['s', 'd', 'q', 'c', 'z', 'x']
    routine_details = {
        'base': base.replace('?', ''),
        '_types': [],
        'def': [],
        'undef': []
    }

    # Prepare regex to capture the necessary parts
    pattern = re.compile(r'^(\w+)\.\$\((?:SUFFIX|PSUFFIX)\).*?-c(.*?)\$<.*$')

    # Collect information
    for line in lines:
        match = pattern.search(line)
        if match:
            routine_name, flags = match.groups()
            type_char = routine_name[0] if routine_name[0] in type_prefixes else routine_name[:2]
            routine_details['_types'].append(type_char)
            # Process defines and undefines
            routine_details['def'].extend(re.findall(r'-D(\S+)', flags))
            routine_details['undef'].extend(re.findall(r'-U(\S+)', flags))

    # Remove duplicates and sort for consistency
    routine_details['_types'] = sorted(set(routine_details['_types']))
    routine_details['def'] = sorted(set(routine_details['def']))
    routine_details['undef'] = sorted(set(routine_details['undef']))

    return routine_details

# Example lines
lines = [
    'isamax.$(SUFFIX) isamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
    'idamax.$(SUFFIX) idamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
    'icamax.$(SUFFIX) icamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
    'izamax.$(SUFFIX) izamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
    'ixamax.$(SUFFIX) ixamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)'
]

# Example usage
base = '?amax'
result = parse_blas_routine_commands(lines, base)
print(result)

# Example usage with command lines and specifying the base symbols
lines = [
    'smax.$(SUFFIX) smax.$(PSUFFIX) : max.c\t$(CC) $(CFLAGS) -c -UUSE_ABS -UUSE_MIN $< -o $(@F)',
    'dmax.$(SUFFIX) dmax.$(PSUFFIX) : max.c\t$(CC) $(CFLAGS) -c -UUSE_ABS -UUSE_MIN $< -o $(@F)',
    'qmax.$(SUFFIX) qmax.$(PSUFFIX) : max.c\t$(CC) $(CFLAGS) -c -UUSE_ABS -UUSE_MIN $< -o $(@F)',
    'smin.$(SUFFIX) smin.$(PSUFFIX) : max.c\t$(CC) $(CFLAGS) -c -UUSE_ABS -DUSE_MIN $< -o $(@F)',
    'dmin.$(SUFFIX) dmin.$(PSUFFIX) : max.c\t$(CC) $(CFLAGS) -c -UUSE_ABS -DUSE_MIN $< -o $(@F)',
    'qmin.$(SUFFIX) qmin.$(PSUFFIX) : max.c\t$(CC) $(CFLAGS) -c -UUSE_ABS -DUSE_MIN $< -o $(@F)',
    'isamax.$(SUFFIX) isamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
    'idamax.$(SUFFIX) idamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
    'iqamax.$(SUFFIX) iqamax.$(PSUFFIX) : imax.c\t$(CC) $(CFLAGS) -c -DUSE_ABS -UUSE_MIN $< -o $(@F)',
 'srot.$(SUFFIX) srot.$(PSUFFIX) : rot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'drot.$(SUFFIX) drot.$(PSUFFIX) : rot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'qrot.$(SUFFIX) qrot.$(PSUFFIX) : rot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'csrot.$(SUFFIX) csrot.$(PSUFFIX) : zrot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'zdrot.$(SUFFIX) zdrot.$(PSUFFIX) : zrot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'xqrot.$(SUFFIX) xqrot.$(PSUFFIX) : zrot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
     'saxpy.$(SUFFIX) saxpy.$(PSUFFIX) : axpy.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'daxpy.$(SUFFIX) daxpy.$(PSUFFIX) : axpy.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'qaxpy.$(SUFFIX) qaxpy.$(PSUFFIX) : axpy.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'caxpy.$(SUFFIX) caxpy.$(PSUFFIX) : zaxpy.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'zaxpy.$(SUFFIX) zaxpy.$(PSUFFIX) : zaxpy.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'xaxpy.$(SUFFIX) xaxpy.$(PSUFFIX) : zaxpy.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
 'caxpyc.$(SUFFIX) caxpyc.$(PSUFFIX) : zaxpy.c\t$(CC) $(CFLAGS) -c -DCONJ $< -o $(@F)',
 'zaxpyc.$(SUFFIX) zaxpyc.$(PSUFFIX) : zaxpy.c\t$(CC) $(CFLAGS) -c -DCONJ $< -o $(@F)',
 'xaxpyc.$(SUFFIX) xaxpyc.$(PSUFFIX) : zaxpy.c\t$(CC) $(CFLAGS) -c -DCONJ $< -o $(@F)',
]

base_symbols = ['?max', '?min', 'i?amax', '?rot', '?axpy']  # Base symbols with wildcards
parsed_configs = parse_blas_routine_commands(lines, 'axpy')
print(parsed_configs)
import re

import re

def parse_blas_routine_commands(lines, symbol):
    prefix, base = symbol.split('?')
    pattern = re.compile(rf'^{prefix}(\w+){base}\.\$\((?:SUFFIX|PSUFFIX)\) : ([^ \t]+)\t.*?-c\s*(.*)$')

    results = {}

    for line in lines:
        matcher = pattern.match(line)
        if matcher:
            type_char, filename, flags = matcher.groups()
            key = (base, filename)
            if key not in results:
                results[key] = {
                    'base': base,
                    'fname': filename.replace('.c', ''),
                    '_types': [],
                    'def': set(),
                    'undef': set(),
                }
            results[key]['_types'].append(type_char)
            results[key]['def'].update(re.findall(r'-D(\w+)', flags))
            results[key]['undef'].update(re.findall(r'-U(\w+)', flags))

    # Convert sets to sorted lists to finalize the structure
    for result in results.values():
        result['_types'] = sorted(set(result['_types']))
        result['def'] = sorted(result['def'])
        result['undef'] = sorted(result['undef'])

    return list(results.values())


# Test the function with a list of lines from a hypothetical Makefile
lines = [
    'srot.$(SUFFIX) srot.$(PSUFFIX) : rot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
    'drot.$(SUFFIX) drot.$(PSUFFIX) : rot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
    'qrot.$(SUFFIX) qrot.$(PSUFFIX) : rot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
    'csrot.$(SUFFIX) csrot.$(PSUFFIX) : zrot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
    'zdrot.$(SUFFIX) zdrot.$(PSUFFIX) : zrot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
    'xqrot.$(SUFFIX) xqrot.$(PSUFFIX) : zrot.c\t$(CC) $(CFLAGS) -c $< -o $(@F)',
]

symbol = '?rot'
result = parse_blas_routine_commands(lines, symbol)
print(result)

print(parse_blas_routine_commands([x for x in oputil.pair_suffix_lines(lines)][1:], 'i?amin'))
print(parse_blas_routine_commands([x for x in oputil.pair_suffix_lines(lines)][1:], '?amin'))
print(parse_blas_routine_commands([x for x in oputil.pair_suffix_lines(lines)][1:], '?min'))
sam=[x for x in oputil.pair_suffix_lines(lines)][2]
sam.split('\t')
symb = '?rot'
prefix, base = symb.split('?')
print(base, prefix)
'i?amax'.split('?')
'?rot'.split('?')
import re
import textwrap
from typing import List, Dict, Any

def parse_makefile_lines(lines: List[str]) -> List[Dict[str, Any]]:
    ext_mappings_l3 = []
    pattern = re.compile(
        r'^(?P<name>[a-z]+)(?P<ext>_[A-Z]+)\.\$\(\w+\) : (?P<file>[\w.]+)\s*\n?'
        r'\s*\$\(CC\) -c \$\(CFLAGS\) (?P<flags>.*) \$< -o \$\(@F\)'
    )

    for line in lines:
        match = pattern.match(line)
        if match:
            details = match.groupdict()
            ext_details = {
                'ext': details['ext'],
                'def': [],
                'undef': [],
                'for': []
            }

            # Determine 'for' based on the 'name' prefix
            if details['name'][0] in ['s', 'd']:
                ext_details['for'] = ['s', 'd']
            elif details['name'][0] in ['c', 'z', 'x']:
                ext_details['for'] = ['c', 'z', 'x']
            elif details['name'][0] == 'q':
                ext_details['for'] = ['q']

            # Parse flags
            flags = details['flags'].split()
            for flag in flags:
                if flag.startswith('-D') and flag[2:] not in ['DOUBLE', 'COMPLEX']:
                    ext_details['def'].append(flag[2:])
                elif flag.startswith('-U') and flag[2:] not in ['DOUBLE', 'COMPLEX']:
                    ext_details['undef'].append(flag[2:])

            ext_mappings_l3.append(ext_details)

    return ext_mappings_l3

# Example usage
makefile_lines = [
    'strmm_LNUU.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT $< -o $(@F)',
    'strmm_LNUN.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT $< -o $(@F)',
    'strmm_LNLU.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT $< -o $(@F)',
    'strmm_LNLN.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT $< -o $(@F)',
    'strmm_LTUU.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT $< -o $(@F)',
    'strmm_LTUN.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT $< -o $(@F)',
    'strmm_LTLU.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT $< -o $(@F)',
    'strmm_LTLN.$(SUFFIX) : trmm_L.c\n\t$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT $< -o $(@F)',
]

# Parse the makefile lines
parsed_ext_mappings = parse_makefile_lines(makefile_lines)

# Display the parsed mappings
for entry in parsed_ext_mappings:
    print(entry)

from openblas_buildsys_snips import _utils
makefile_string = """
strmm_LNUU.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT $< -o $(@F)

strmm_LNUN.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT $< -o $(@F)

strmm_LNLU.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT $< -o $(@F)

strmm_LNLN.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT $< -o $(@F)

strmm_LTUU.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT $< -o $(@F)

strmm_LTUN.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT $< -o $(@F)

strmm_LTLU.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT $< -o $(@F)

strmm_LTLN.$(SUFFIX) : trmm_L.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT $< -o $(@F)

strmm_RNUU.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT $< -o $(@F)

strmm_RNUN.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT $< -o $(@F)

strmm_RNLU.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT $< -o $(@F)

strmm_RNLN.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT $< -o $(@F)

strmm_RTUU.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT $< -o $(@F)

strmm_RTUN.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT $< -o $(@F)

strmm_RTLU.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT $< -o $(@F)

strmm_RTLN.$(SUFFIX) : trmm_R.c
	$(CC) -c $(CFLAGS) -UCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT $< -o $(@F)
"""

for x in parse_makefile_lines(_utils.pair_suffix_lines(makefile_string.split("\n"))):
    print(x)
makefile_string = """
ctrmm_LNUU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_LNUN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_LNLU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_LNLN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_LTUU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_LTUN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_LTLU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_LTLN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_LRUU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_LRUN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_LRLU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_LRLN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_LCUU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_LCUN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_LCLU.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_LCLN.$(PSUFFIX) : trmm_L.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_RNUU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_RNUN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_RNLU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_RNLN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_RTUU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_RTUN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_RTLU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT -UCONJ $< -o $(@F)

ctrmm_RTLN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT -UCONJ $< -o $(@F)

ctrmm_RRUU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_RRUN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -DUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_RRLU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_RRLN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -UTRANSA -UUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_RCUU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_RCUN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -DUPPER -UUNIT -DCONJ $< -o $(@F)

ctrmm_RCLU.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -DUNIT -DCONJ $< -o $(@F)

ctrmm_RCLN.$(PSUFFIX) : trmm_R.c
	$(CC) -c $(PFLAGS) -DCOMPLEX -UDOUBLE -DTRANSA -UUPPER -UUNIT -DCONJ $< -o $(@F)
"""

for x in parse_makefile_lines(_utils.pair_suffix_lines(makefile_string.split("\n"))):
    print(x)
makefile_string = """
ssymm_LU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -UCOMPLEX -ULOWER -URSIDE -DNN $< -o $(@F)

ssymm_LL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -UCOMPLEX -DLOWER -URSIDE -DNN $< -o $(@F)

ssymm_RU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -UCOMPLEX -ULOWER -DRSIDE -DNN $< -o $(@F)

ssymm_RL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -UCOMPLEX -DLOWER -DRSIDE -DNN $< -o $(@F)

dsymm_LU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -UCOMPLEX -ULOWER -URSIDE -DNN $< -o $(@F)

dsymm_LL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -UCOMPLEX -DLOWER -URSIDE -DNN $< -o $(@F)

dsymm_RU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -UCOMPLEX -ULOWER -DRSIDE -DNN $< -o $(@F)

dsymm_RL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -UCOMPLEX -DLOWER -DRSIDE -DNN $< -o $(@F)

qsymm_LU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -UCOMPLEX -ULOWER -URSIDE -DNN $< -o $(@F)

qsymm_LL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -UCOMPLEX -DLOWER -URSIDE -DNN $< -o $(@F)

qsymm_RU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -UCOMPLEX -ULOWER -DRSIDE -DNN $< -o $(@F)

qsymm_RL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -UCOMPLEX -DLOWER -DRSIDE -DNN $< -o $(@F)

csymm_LU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -DCOMPLEX -ULOWER -URSIDE -DNN $< -o $(@F)

csymm_LL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -DCOMPLEX -DLOWER -URSIDE -DNN $< -o $(@F)

csymm_RU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -DCOMPLEX -ULOWER -DRSIDE -DNN $< -o $(@F)

csymm_RL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -UDOUBLE -DCOMPLEX -DLOWER -DRSIDE -DNN $< -o $(@F)

zsymm_LU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -DCOMPLEX -ULOWER -URSIDE -DNN $< -o $(@F)

zsymm_LL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -DCOMPLEX -DLOWER -URSIDE -DNN $< -o $(@F)

zsymm_RU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -DCOMPLEX -ULOWER -DRSIDE -DNN $< -o $(@F)

zsymm_RL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DDOUBLE -DCOMPLEX -DLOWER -DRSIDE -DNN $< -o $(@F)

xsymm_LU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -DCOMPLEX -ULOWER -URSIDE -DNN $< -o $(@F)

xsymm_LL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -DCOMPLEX -DLOWER -URSIDE -DNN $< -o $(@F)

xsymm_RU.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -DCOMPLEX -ULOWER -DRSIDE -DNN $< -o $(@F)

xsymm_RL.$(SUFFIX) : symm_k.c level3.c ../../param.h
	$(CC) -c $(CFLAGS) -DXDOUBLE -DCOMPLEX -DLOWER -DRSIDE -DNN $< -o $(@F)
"""

def parse_makefile_lines(lines: List[str]) -> List[Dict[str, Any]]:
    ext_mappings_l3 = []
    pattern = re.compile(
        r'^(?P<name>[a-z]+)(?P<ext>_[A-Z]+)\.\$\(\w+\) : (?P<file>[\w.]+)\s*\n?'
        r'\s*\$\(CC\) -c \$\(PFLAGS\) (?P<flags>.*) \$< -o \$\(@F\)'
    )

    for line in lines:
        match = pattern.match(line)
        if match:
            details = match.groupdict()
            ext_details = {
                'ext': details['ext'],
                'def': [],
                'undef': [],
                'for': []
            }

            # Determine 'for' based on the 'name' prefix
            if details['name'][0] in ['s', 'd']:
                ext_details['for'] = ['s', 'd']
            elif details['name'][0] in ['c', 'z', 'x']:
                ext_details['for'] = ['c', 'z', 'x']
            elif details['name'][0] == 'q':
                ext_details['for'] = ['q']

            # Parse flags
            flags = details['flags'].split()
            for flag in flags:
                if flag.startswith('-D') and flag[2:] not in ['DOUBLE', 'COMPLEX']:
                    ext_details['def'].append(flag[2:])
                elif flag.startswith('-U') and flag[2:] not in ['DOUBLE', 'COMPLEX']:
                    ext_details['undef'].append(flag[2:])

            ext_mappings_l3.append(ext_details)

    return ext_mappings_l3


for x in parse_makefile_lines(_utils.pair_suffix_lines(makefile_string.split("\n"))):
    print(f'{x},')
list(_utils.pair_suffix_lines(makefile_string.split("\n")))