def generate_typedefs_makefile(metadata_define, typedefs_makefile, typedefs_cmakefile, typedefs_sourcefile): """Generate list of Fortran modules containing CCPP type/kind definitions, and create makefile/cmakefile snippets for host model build system""" logging.info( 'Generating list of Fortran modules containing CCPP type definitions ...' ) success = True # typedefs = [] # (1) Search for type definitions in the metadata, defined by: # (a) the type not being a standard type, and # (b) the type not being the CCPP framework internal type # (c) the standard_name being identical to the type name # (2) Search for kind definitions in the metadata, defined by: # (a) the standard_name starting with "kind_" # (b) the type being integer and the units being none for key in metadata_define.keys(): # derived data types if not metadata_define[key][0].type in STANDARD_VARIABLE_TYPES and \ not metadata_define[key][0].type == CCPP_TYPE and \ metadata_define[key][0].type == metadata_define[key][0].standard_name: container = decode_container_as_dict( metadata_define[key][0].container) if not 'MODULE' in container.keys(): logging.error("Invalid type definition for type {}: {}".format( metadata_define[key][0].type, metadata_define[key][0].print_debug())) success = False continue # Fortran modules are lowercase and have the ending ".mod" typedef_fortran_module = "{}.mod".format( container['MODULE']).lower() if not typedef_fortran_module in typedefs: typedefs.append(typedef_fortran_module) # kind definitions elif metadata_define[key][0].standard_name.startswith("kind_") and \ metadata_define[key][0].type == STANDARD_INTEGER_TYPE and \ metadata_define[key][0].units == 'none': container = decode_container_as_dict( metadata_define[key][0].container) if not 'MODULE' in container.keys(): logging.error("Invalid kind definition for kind {}: {}".format( metadata_define[key][0].type, metadata_define[key][0].print_debug())) success = False continue # Fortran modules are lowercase and have the ending ".mod" typedef_fortran_module = "{}.mod".format( container['MODULE']).lower() if not typedef_fortran_module in typedefs: typedefs.append(typedef_fortran_module) logging.info('Generating typedefs makefile/cmakefile snippet ...') # Write the Fortran modules without path - the build system knows where they are makefile = TypedefsMakefile() makefile.filename = typedefs_makefile + '.tmp' cmakefile = TypedefsCMakefile() cmakefile.filename = typedefs_cmakefile + '.tmp' sourcefile = TypedefsSourcefile() sourcefile.filename = typedefs_sourcefile + '.tmp' # Sort typedefs so that the order remains the same (for cmake to avoid) recompiling typedefs.sort() # Generate list of type definitions makefile.write(typedefs) cmakefile.write(typedefs) sourcefile.write(typedefs) if os.path.isfile(typedefs_makefile) and \ filecmp.cmp(typedefs_makefile, makefile.filename): os.remove(makefile.filename) os.remove(cmakefile.filename) os.remove(sourcefile.filename) else: if os.path.isfile(typedefs_makefile): os.remove(typedefs_makefile) if os.path.isfile(typedefs_cmakefile): os.remove(typedefs_cmakefile) if os.path.isfile(typedefs_sourcefile): os.remove(typedefs_sourcefile) os.rename(makefile.filename, typedefs_makefile) os.rename(cmakefile.filename, typedefs_cmakefile) os.rename(sourcefile.filename, typedefs_sourcefile) # logging.info('Added {0} typedefs to {1}, {2}, {3}'.format( len(typedefs), typedefs_makefile, typedefs_cmakefile, typedefs_sourcefile)) return success
def convert_local_name_from_new_metadata(metadata, standard_name, typedefs_new_metadata, converted_variables): """Convert local names in new metadata format (no old-style DDT references, array references as standard names) to old metadata format (with old-style DDT references, array references as local names).""" success = True var = metadata[standard_name][0] # Check if this variable has already been converted if standard_name in converted_variables: logging.debug( 'Variable {0} was in old metadata format and has already been converted' .format(standard_name)) return (success, var.local_name, converted_variables) # Decode container into a dictionary container = decode_container_as_dict(var.container) # Check if variable is in old or new metadata format module_name = container['MODULE'] if not module_name in typedefs_new_metadata.keys(): logging.debug( 'Variable {0} is in old metadata format, no conversion necessary'. format(standard_name)) return (success, var.local_name, converted_variables) # For module variables set type_name to module_name if not 'TYPE' in container.keys(): type_name = module_name else: type_name = container['TYPE'] # Check that this module/type is configured (modules will have empty prefices) if not type_name in typedefs_new_metadata[module_name].keys(): logging.error( "Module {0} uses the new metadata format, but module/type {1} is not configured" .format(module_name, type_name)) success = False return (success, None, converted_variables) # The local name (incl. the array reference) is in new metadata format local_name = var.local_name logging.info( "Converting local name {0} of variable {1} from new to old metadata". format(local_name, standard_name)) if "(" in local_name: (actual_var_name, array_reference) = split_var_name_and_array_reference(local_name) indices = array_reference.lstrip('(').rstrip(')').split(',') indices_local_names = [] for index_range in indices: # Leave colons-only dimension alone if index_range == ':': indices_local_names.append(index_range) continue # Split by colons to get a pair of dimensions dimensions = index_range.split(':') dimensions_local_names = [] for dimension in dimensions: # Leave literals alone try: int(dimension) dimensions_local_names.append(dimension) continue except ValueError: pass # Convert the local name of the dimension to old metadata standard, if necessary (recursive call) (success, local_name_dim, converted_variables) = convert_local_name_from_new_metadata( metadata, dimension, typedefs_new_metadata, converted_variables) if not success: return (success, None, converted_variables) # Update the local name of the dimension, if necessary if not metadata[dimension][0].local_name == local_name_dim: logging.debug( "Updating local name of variable {0} from {1} to {2}". format(dimension, metadata[dimension][0].local_name, local_name_dim)) metadata[dimension][0].local_name = local_name_dim dimensions_local_names.append(local_name_dim) indices_local_names.append(':'.join(dimensions_local_names)) # Put back together the array reference with local names in old metadata format array_reference_local_names = '(' + ','.join(indices_local_names) + ')' # Compose local name (still without any DDT reference prefix) local_name = actual_var_name + array_reference_local_names # Prefix the local name with the reference if not empty if typedefs_new_metadata[module_name][type_name]: local_name = typedefs_new_metadata[module_name][ type_name] + '%' + local_name if success: converted_variables.append(standard_name) return (success, local_name, converted_variables)