implementation module Linker;

import StdFile,StdArray,StdClass,StdEnum,StdInt,StdBool,StdChar;
from StdMisc import abort;
from StdList import ++;
from StdString import String,%;

import SymbolTable;
from ReadObject import ExtFileSystem, read_xcoff_files,read_library_files; // only for PC,read_static_lib_files;
from piObjectToDisk import write_object_to_disk;
import SymbolTable;
//import ObjectToDisk;
import ExtString, ExtFile;
import PlatformLinkOptions;
import LinkerMessages;
import UtilStrictLists;
import LibraryDynamics;

link_xcoff_files :: !Bool ![String] ![String] ![String] !String !PlatformLinkOptions !Files  -> (!*State,!Files);
link_xcoff_files normal_static_link file_names library_file_names static_libraries application_file_name platform_link_options files 
	// platform independent options
	#! one_pass_link 
		= True;
	/* WARNING:
		The MAC only supports one pass linking. Thus the one_pass_link *must* be set to
		True. The PC simply ignores the flag.
	*/ 	
	#! allow_unused_undefined_symbols
		= False;
	
	// read object files
	#! (any_extra_sections,read_xcoff_files_errors,sections,n_xcoff_files,xcoff_list,names_table,files)
		= read_xcoff_files False file_names create_names_table one_pass_link files 0;
	| not (isEmpty read_xcoff_files_errors)
		# linker_messages_state
			= setLinkerMessages [LinkerError e \\ e <- read_xcoff_files_errors] DefaultLinkerMessages;
		= ({ EmptyState & linker_messages_state = linker_messages_state },files);      //; //(read_xcoff_files_errors,files);
	# platform_link_options
		= plo_set_sections sections platform_link_options;
	# platform_link_options
		= plo_any_extra_sections any_extra_sections platform_link_options;
		
//
//	| any_extra_sections
//		= abort "any_extra_sections";
/*
	only forPC
	// read static libraries
	#! (errors,xcoff_list, _, names_table, n_xcoff_files, files)
		= read_static_lib_files static_libraries [] names_table n_xcoff_files xcoff_list files;
	| not (isEmpty errors)
		# linker_messages_state
			= setLinkerMessages [LinkerError e \\ e <- errors] DefaultLinkerMessages;
		= ({ EmptyState & linker_messages_state = linker_messages_state },files);
*/
		
	// read dynamic libraries	
	#! n_libraries = length library_file_names;
	   (read_library_errors,library_list,n_library_symbols,files,names_table)
		= read_library_files library_file_names (~n_libraries) 0 files names_table;
	| not (isEmpty read_library_errors)
		# linker_messages_state
			= setLinkerMessages [LinkerError e \\ e <- read_library_errors] DefaultLinkerMessages;
		= ({ EmptyState & linker_messages_state = linker_messages_state },files);
		
	// resolve symbolic references by name
	#! (undefined_symbols,xcoff_list,names_table)
		= import_symbols_in_xcoff_files xcoff_list 0 [] names_table;
	| not (isEmpty undefined_symbols) && not allow_unused_undefined_symbols
		# linker_messages_state
			= setLinkerMessages [LinkerError ("Undefined symbol: " +++ symbol_name) \\ (symbol_name,_,_)<-undefined_symbols] DefaultLinkerMessages;
		= ({ EmptyState & linker_messages_state = linker_messages_state },files);
		
	// check for the existence of main entry and any exported symbols
	# (main_entry_found,main_file_n,main_symbol_n,			// main entry
	   all_exported_symbols_found,entry_datas,				// exported symbols (found,symbol_name,file_n,symbol_n)
	   names_table,											// names table
	   platform_link_options)
	   	= find_root_symbols names_table platform_link_options;
	| not main_entry_found || not all_exported_symbols_found
		# undefined_main_entry
			= case main_entry_found of {
				True
					-> [];
				False
					-> ["Symbol \"main\" is not defined"];
			};
		# undefined_exported_entries
			= case all_exported_symbols_found of {
				True
					-> [];
				False 
					-> [ ("Exported symbol \"" +++ name +++ "\" is not defined.") \\ (found,name,file_n,symbol_n) <- entry_datas | not found ];
			};
		# linker_messages_state
			= setLinkerMessages [LinkerError e \\ e <- (undefined_main_entry ++ undefined_exported_entries)] DefaultLinkerMessages;
		= ({ EmptyState & linker_messages_state = linker_messages_state },files);

	# platform_link_options
		= plo_set_main_file_n_and_symbol_n main_file_n main_symbol_n platform_link_options

	// mark only used symbols
	# root_entries
		= [(True,"",main_file_n,main_symbol_n) : entry_datas];
	#! (unused_undefined_symbols,n_xcoff_symbols,marked_bool_a,marked_offset_a,xcoff_a,names_table)
		= mark_modules_list [] xcoff_list n_xcoff_files n_libraries n_library_symbols library_list root_entries names_table;
	| not (isEmpty undefined_symbols)
		# linker_messages_state
			= setLinkerMessages [LinkerError ("unused undefined symbol " +++ e) \\ e <- unused_undefined_symbols] DefaultLinkerMessages;
		= ({ EmptyState & linker_messages_state = linker_messages_state },files);
		
/*
// PC; should be built-in
	// remove garbage from symbol table (only for static linker)
	#! (marked_bool_a,xcoff_a) 
		= case normal_static_link of {
			True
				-> remove_garbage_from_symbol_table 0 n_xcoff_files 0 marked_bool_a xcoff_a;
			False
				-> (marked_bool_a,xcoff_a);
		};
*/

/*
// MAC; Xcoff-executable should be built-in
	# (sections,platform_link_options)
		= plo_get_sections platform_link_options;
	# (ok,pef_application_size,files)
		= case /*generate_xcoff*/ False of { 
			False
				-> write_pef_file application_file_name n_xcoff_files n_libraries n_library_symbols library_list 
						main_symbol_n main_file_n one_pass_link sections 
						n_xcoff_symbols marked_bool_a marked_offset_a xcoff_a files;				
/*
uniek maken
			True
				-> write_xcoff_file application_file_name n_xcoff_files n_libraries n_library_symbols library_list 
						main_symbol_n main_file_n one_pass_link sections 
						n_xcoff_symbols marked_bool_a marked_offset_a xcoff_a files;
						//abort "link_xcoff_files: a xcoff executable not supported";
*/
		};
		
*/
	// create state
	#! state = { EmptyState &
	// misc
		one_pass_link			= one_pass_link
	,	normal_static_link		= normal_static_link
	
	// linker tables
	,	application_name		= application_file_name
	,	n_libraries				= n_libraries
	,	n_xcoff_files 			= n_xcoff_files
	,	n_xcoff_symbols			= n_xcoff_symbols
	,	n_library_symbols		= n_library_symbols
	
	,	marked_bool_a			= marked_bool_a
	,	marked_offset_a			= marked_offset_a
//	,	module_offset_a			:: !*{#Int}
	,	xcoff_a 				= xcoff_a
	,	namestable				= names_table

	// dynamic libraries
	,	library_list 			= library_list
	,	library_file_names		= if normal_static_link [] (strip_paths_from_file_names library_file_names)
	
//	,	pd_state				:: !*PDState

};


	#! (state,platform_link_options,files)
		= case (normal_static_link || (not normal_static_link && not dynamics)) of {
			True			
				#! (state,platform_link_options,files)
					= write_object_to_disk platform_link_options state files;
				
				/*	
				#! (/*ok,*/state,platform_link_options,files)
					= write_object_to_disk normal_static_link application_file_name n_xcoff_files n_libraries
						 n_library_symbols library_list 
						 n_xcoff_symbols marked_bool_a marked_offset_a xcoff_a 
						 files names_table platform_link_options;
					*/	 
						 
						 
				// check for link errors during generation	
				#! (ok,state)
					= IsErrorOccured state;
					
				#! (_,_,state,platform_link_options,files)
					= case ok of {
						True
							-> post_process state platform_link_options files;
						False
							-> (False,[],state,platform_link_options,files);
					};
				-> (state,platform_link_options,files);
			False
				/*
				#! warning
					= LinkerWarning "Using library approach to dynamics"
				#! state
					= AddMessage warning state;
				*/
				// plo_set_console_window
				-> build_type_and_code_library file_names library_file_names static_libraries application_file_name state platform_link_options files;
		};
	= (state,files);

