implementation module Linker;

import StdFile,StdArray,StdClass,StdEnum,StdInt,StdBool,StdChar;
from StdMisc import abort;
from StdList import ++;
//1.3
from StdString import String,%;
from ReadObject import ExtFileSystem, read_xcoff_files,read_library_files,read_library_files_new,read_library_files2; // only for PC,read_static_lib_files;
//3.1
/*2.0
import StdEnv;
from ReadObject import class ExtFileSystem, instance ExtFileSystem Files, read_xcoff_files,read_library_files,read_library_files_new,read_library_files2; // only for PC,read_static_lib_files;
from lib import ::ImportLibrary(..);
0.2*/

import SymbolTable;

from piObjectToDisk import write_object_to_disk;
import SymbolTable;
//import ObjectToDisk;
import ExtString, ExtFile;
import PlatformLinkOptions;
import LinkerMessages;
import UtilStrictLists;
import LibraryDynamics;
import directory_structure;

// windows ...
//1.3
from lib import read_static_lib_files, read_static_lib_files_new, ReadStaticLibState, default_rsl_state, ImportLibrary;
//3.1
/*2.0
from lib import read_static_lib_files, read_static_lib_files_new, ::ReadStaticLibState(..), default_rsl_state, ::ImportLibrary;
0.2*/
// ... windows

import link_switches;

append_library_lists EmptyLibraryList library_list
	= library_list;
append_library_lists (Library s i1 lsl i2 xs) library_list
	= (Library s i1 lsl i2 (append_library_lists xs library_list));


select_import_libraries :: !*ReadStaticLibState -> *([ImportLibrary],*ReadStaticLibState);
select_import_libraries rsl_state=:{import_libraries}
	= (import_libraries,{rsl_state & import_libraries = []});

link_xcoff_files :: !String !Bool ![String] ![String] ![String] !String !PlatformLinkOptions !Files  -> (!*State,!Files);
link_xcoff_files dynamics_path 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
		= ALLOW_UNUSED_DEFINED_SYMBOLS; //True;
	
	// 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 default_redirection_state;
	| 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";
	// read static libraries
	# (errors,xcoff_list,names_table,n_xcoff_files,files,lib_library_list,n_library_symbols,n_total_libraries)
//		= case False of { 
		
		= case (sel_platform True False) of { 
			True
				// winos
				#! (errors,xcoff_list, _, names_table, n_xcoff_files, files,rsl_state,_)
					= read_static_lib_files static_libraries [] names_table n_xcoff_files xcoff_list files default_rsl_state default_redirection_state;

				// new ...
			
				# (import_libraries,rsl_state)
					= select_import_libraries rsl_state;
				# n_import_libraries
					= length import_libraries;
					
				#! n_libraries
					= n_import_libraries + length library_file_names;
				#! import_libraries
					= [[ il_name:il_symbols] \\ {il_name,il_symbols} <- import_libraries ];
				#! (library_list,n_library_symbols,names_table)
					= read_library_files2 import_libraries (~n_libraries) 0 names_table;
				-> (errors,xcoff_list,names_table,n_xcoff_files,files,library_list,n_library_symbols,n_libraries);			
				// ... new					
			False
				#! n_libraries = length library_file_names;
				-> ([],xcoff_list,names_table,n_xcoff_files,files,EmptyLibraryList,0,n_libraries);
		};
	| 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;
//		n_library_symbols = 0;
	   (read_library_errors,library_list,n_library_symbols,files,names_table)
		= read_library_files library_file_names (~n_libraries) /* old 0 */ n_library_symbols 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);
		
	// new ...
	# n_libraries
		= n_total_libraries;
	# library_list
		= append_library_lists lib_library_list library_list;		
	// ... new
		
	// 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
		<<- ("%%%",undefined_symbols,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;
	| False //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);
	
	
	
	# linker_messages_state
		= setLinkerMessages [LinkerWarning ("unused undefined symbol " +++ e) \\ e <- unused_undefined_symbols] DefaultLinkerMessages;

	
	
	
	
		
/*
// 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 of { //(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
				// open .bat-file
				#! (path_file,_)
					= ExtractPathFileAndExtension application_file_name;
				#! (ok1,bat_file,files)
					= fopen (path_file +++ ".bat") FWriteText files;
					
				// write path to dynamic linker
				#! bat_file
					= fwritec '\"' bat_file;
				#! bat_file
					= fwrites dynamics_path bat_file;
				#! bat_file
					= fwritec '\"' bat_file;
				#! bat_file
					= fwritec ' ' bat_file;

				
				// get application name
				#! (_,application_name)
					= ExtractPathAndFile application_file_name;
				#! (application_name,_)
					= ExtractPathFileAndExtension application_name;
					
				// get root of dynamic linker
				#! (dynamic_linker_path,_)
					= ExtractPathAndFile dynamics_path;
					
				#! (library_path_name,files)
					= ds_generate_library_name application_name dynamic_linker_path files;
					
				// write application
				#! bat_file
					= fwritec '\"' bat_file;
				#! bat_file
					= fwrites library_path_name bat_file;
				#! bat_file
					= fwritec '\"' bat_file;
				#! bat_file
					= fwritec '\n' bat_file;
					
				#! (ok2,files)
					= fclose bat_file files;
					
				// report error if occured
				#! state
					= case (not ok1 || not ok2) of {
						True
							#! error
								= LinkerError ("Error writing '" +++ path_file +++ ".bat'");
							#! state
								= AddMessage error state;
							-> state;
						False
							-> state;
					};
					
				// plo_set_console_window
				-> build_type_and_code_library file_names library_file_names static_libraries library_path_name state platform_link_options files;
		};
	= (state,files);
