mirror of
https://github.com/meekrosoft/fff
synced 2026-01-23 00:15:59 +01:00
Replace makefiles with CMakeLists.txt. This will allow for IDE and platform agnostic builds of FFF. Update the CI for FFF to use github workflows which don't depend on MS VC. The workflow added will verify the pull requests sent to master buy running 'buildandtest' which mirrors the developer workflow. Signed-off-by: Yuval Peress <peress@google.com>
679 lines
23 KiB
Ruby
679 lines
23 KiB
Ruby
|
|
# fakegen.rb
|
|
# A simple code generator to create some C macros for defining test fake functions
|
|
|
|
|
|
$cpp_output = true
|
|
$MAX_ARGS = 20
|
|
$DEFAULT_ARG_HISTORY = 50
|
|
$MAX_CALL_HISTORY = 50
|
|
|
|
def license
|
|
File.foreach("#{__dir__}/LICENSE") { |line| putd line }
|
|
end
|
|
|
|
def include_dependencies
|
|
putd "#include <stdarg.h>"
|
|
putd "#include <string.h> /* For memset and memcpy */"
|
|
puts
|
|
end
|
|
|
|
def output_constants
|
|
putd "#define FFF_MAX_ARGS (#{$MAX_ARGS}u)"
|
|
putd "#ifndef FFF_ARG_HISTORY_LEN"
|
|
indent {
|
|
putd "#define FFF_ARG_HISTORY_LEN (#{$DEFAULT_ARG_HISTORY}u)"
|
|
}
|
|
putd "#endif"
|
|
putd "#ifndef FFF_CALL_HISTORY_LEN"
|
|
indent {
|
|
putd "#define FFF_CALL_HISTORY_LEN (#{$MAX_CALL_HISTORY}u)"
|
|
}
|
|
putd "#endif"
|
|
putd "#ifndef FFF_GCC_FUNCTION_ATTRIBUTES"
|
|
indent {
|
|
putd "#define FFF_GCC_FUNCTION_ATTRIBUTES"
|
|
}
|
|
putd "#endif"
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# ------ Helper macros to use internally ------ #
|
|
def output_internal_helper_macros
|
|
putd "/* -- INTERNAL HELPER MACROS -- */"
|
|
|
|
define_return_sequence_helper
|
|
define_custom_fake_sequence_helper
|
|
define_reset_fake_macro
|
|
define_declare_arg_helper
|
|
define_declare_all_func_common_helper
|
|
define_declare_return_value_history
|
|
define_save_arg_helper
|
|
define_room_for_more_history
|
|
define_save_ret_history_helper
|
|
define_save_arg_history_helper
|
|
define_history_dropped_helper
|
|
define_value_function_variables_helper
|
|
define_custom_fake_seq_variables_helper
|
|
define_increment_call_count_helper
|
|
define_return_fake_result_helper
|
|
define_extern_c_helper
|
|
define_reset_fake_helper
|
|
|
|
putd "/* -- END INTERNAL HELPER MACROS -- */"
|
|
puts
|
|
end
|
|
|
|
def define_return_sequence_helper
|
|
putd_backslash "#define SET_RETURN_SEQ(FUNCNAME, ARRAY_POINTER, ARRAY_LEN)"
|
|
indent {
|
|
putd_backslash "FUNCNAME##_fake.return_val_seq = ARRAY_POINTER;"
|
|
putd "FUNCNAME##_fake.return_val_seq_len = ARRAY_LEN;"
|
|
}
|
|
end
|
|
|
|
def define_custom_fake_sequence_helper
|
|
putd_backslash "#define SET_CUSTOM_FAKE_SEQ(FUNCNAME, ARRAY_POINTER, ARRAY_LEN)"
|
|
indent {
|
|
putd_backslash "FUNCNAME##_fake.custom_fake_seq = ARRAY_POINTER;"
|
|
putd "FUNCNAME##_fake.custom_fake_seq_len = ARRAY_LEN;"
|
|
}
|
|
end
|
|
|
|
def define_reset_fake_macro
|
|
puts
|
|
putd "/* Defining a function to reset a fake function */"
|
|
putd_backslash "#define RESET_FAKE(FUNCNAME) {"
|
|
indent {
|
|
putd_backslash "FUNCNAME##_reset();"
|
|
}
|
|
putd_backslash "}"
|
|
puts
|
|
end
|
|
|
|
def define_declare_arg_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_ARG(type, n, FUNCNAME)"
|
|
indent {
|
|
putd_backslash "type arg##n##_val;"
|
|
putd "type arg##n##_history[FFF_ARG_HISTORY_LEN];"
|
|
}
|
|
end
|
|
|
|
def define_declare_all_func_common_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_ALL_FUNC_COMMON"
|
|
indent {
|
|
putd_backslash "unsigned int call_count;"
|
|
putd_backslash "unsigned int arg_history_len;"
|
|
putd_backslash "unsigned int arg_histories_dropped;"
|
|
}
|
|
end
|
|
|
|
def define_declare_return_value_history
|
|
putd ""
|
|
putd_backslash "#define DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)"
|
|
indent {
|
|
putd "RETURN_TYPE return_val_history[FFF_ARG_HISTORY_LEN];"
|
|
}
|
|
end
|
|
|
|
def define_save_arg_helper
|
|
puts
|
|
putd_backslash "#define SAVE_ARG(FUNCNAME, n)"
|
|
indent {
|
|
putd "memcpy((void*)&FUNCNAME##_fake.arg##n##_val, (void*)&arg##n, sizeof(arg##n));"
|
|
}
|
|
end
|
|
|
|
def define_save_ret_history_helper
|
|
putd ""
|
|
putd_backslash "#define SAVE_RET_HISTORY(FUNCNAME, RETVAL)"
|
|
indent {
|
|
putd_backslash "if ((FUNCNAME##_fake.call_count - 1) < FFF_ARG_HISTORY_LEN)"
|
|
indent {
|
|
putd_backslash "memcpy((void *)&FUNCNAME##_fake.return_val_history[FUNCNAME##_fake.call_count - 1], (const void *) &RETVAL, sizeof(RETVAL));"
|
|
}
|
|
}
|
|
end
|
|
|
|
def define_room_for_more_history
|
|
puts
|
|
putd_backslash "#define ROOM_FOR_MORE_HISTORY(FUNCNAME)"
|
|
indent {
|
|
putd "FUNCNAME##_fake.call_count < FFF_ARG_HISTORY_LEN"
|
|
}
|
|
end
|
|
|
|
def define_save_arg_history_helper
|
|
puts
|
|
putd_backslash "#define SAVE_ARG_HISTORY(FUNCNAME, ARGN)"
|
|
indent {
|
|
putd "memcpy((void*)&FUNCNAME##_fake.arg##ARGN##_history[FUNCNAME##_fake.call_count], (void*)&arg##ARGN, sizeof(arg##ARGN));"
|
|
}
|
|
end
|
|
|
|
def define_history_dropped_helper
|
|
puts
|
|
putd_backslash "#define HISTORY_DROPPED(FUNCNAME)"
|
|
indent {
|
|
putd "FUNCNAME##_fake.arg_histories_dropped++"
|
|
}
|
|
end
|
|
|
|
def define_value_function_variables_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE return_val;"
|
|
putd_backslash "int return_val_seq_len;"
|
|
putd_backslash "int return_val_seq_idx;"
|
|
putd_backslash "RETURN_TYPE * return_val_seq;"
|
|
}
|
|
end
|
|
|
|
def define_custom_fake_seq_variables_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_CUSTOM_FAKE_SEQ_VARIABLES"
|
|
indent {
|
|
putd_backslash "int custom_fake_seq_len;"
|
|
putd_backslash "int custom_fake_seq_idx;"
|
|
}
|
|
end
|
|
|
|
def define_increment_call_count_helper
|
|
puts
|
|
putd_backslash "#define INCREMENT_CALL_COUNT(FUNCNAME)"
|
|
indent {
|
|
putd "FUNCNAME##_fake.call_count++"
|
|
}
|
|
end
|
|
|
|
def define_return_fake_result_helper
|
|
puts
|
|
putd_backslash "#define RETURN_FAKE_RESULT(FUNCNAME)"
|
|
indent {
|
|
putd_backslash "if (FUNCNAME##_fake.return_val_seq_len){ /* then its a sequence */"
|
|
indent {
|
|
putd_backslash "if(FUNCNAME##_fake.return_val_seq_idx < FUNCNAME##_fake.return_val_seq_len) {"
|
|
indent {
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_idx])"
|
|
putd_backslash "return FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_idx++];"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_len-1])"
|
|
putd_backslash "return FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_len-1]; /* return last element */"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val)"
|
|
putd_backslash "return FUNCNAME##_fake.return_val;"
|
|
}
|
|
end
|
|
|
|
def define_extern_c_helper
|
|
puts
|
|
putd "#ifdef __cplusplus"
|
|
indent {
|
|
putd "#define FFF_EXTERN_C extern \"C\"{"
|
|
putd "#define FFF_END_EXTERN_C } "
|
|
}
|
|
putd "#else /* ansi c */"
|
|
indent {
|
|
putd "#define FFF_EXTERN_C "
|
|
putd "#define FFF_END_EXTERN_C "
|
|
}
|
|
putd "#endif /* cpp/ansi c */"
|
|
end
|
|
|
|
def define_reset_fake_helper
|
|
puts
|
|
putd_backslash "#define DEFINE_RESET_FUNCTION(FUNCNAME)"
|
|
indent {
|
|
putd_backslash "void FUNCNAME##_reset(void){"
|
|
indent {
|
|
putd_backslash "memset(&FUNCNAME##_fake, 0, sizeof(FUNCNAME##_fake));"
|
|
putd_backslash "FUNCNAME##_fake.arg_history_len = FFF_ARG_HISTORY_LEN;"
|
|
}
|
|
putd "}"
|
|
}
|
|
end
|
|
# ------ End Helper macros ------ #
|
|
|
|
#fakegen helpers to print at levels of indentation
|
|
$current_depth = 0
|
|
def putd(str)
|
|
$current_depth.times {|not_used| print " "}
|
|
puts str
|
|
end
|
|
|
|
#multiline putd which adds a \ at the end of the generated macro
|
|
def putd_backslash str
|
|
putd(str + " \\")
|
|
end
|
|
|
|
def pushd
|
|
$current_depth = $current_depth + 4
|
|
end
|
|
|
|
def popd
|
|
$current_depth = $current_depth - 4
|
|
end
|
|
|
|
def indent
|
|
pushd
|
|
yield
|
|
popd
|
|
end
|
|
|
|
def output_macro(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
|
|
vararg_name = has_varargs ? "_VARARG" : ""
|
|
fake_macro_name = is_value_function ? "FAKE_VALUE_FUNC#{arg_count}#{vararg_name}" : "FAKE_VOID_FUNC#{arg_count}#{vararg_name}"
|
|
declare_macro_name = "DECLARE_#{fake_macro_name}"
|
|
define_macro_name = "DEFINE_#{fake_macro_name}"
|
|
saved_arg_count = arg_count - (has_varargs ? 1 : 0)
|
|
return_type = is_value_function ? "RETURN_TYPE" : ""
|
|
|
|
puts
|
|
output_macro_header(declare_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
indent {
|
|
output_variables(saved_arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
}
|
|
|
|
puts
|
|
output_macro_header(define_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
indent {
|
|
putd_backslash "FUNCNAME##_Fake FUNCNAME##_fake;"
|
|
putd_backslash function_signature(saved_arg_count, has_varargs, has_calling_conventions, is_value_function) + "{"
|
|
indent {
|
|
output_function_body(saved_arg_count, has_varargs, is_value_function)
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "DEFINE_RESET_FUNCTION(FUNCNAME)"
|
|
}
|
|
|
|
puts
|
|
|
|
output_macro_header(fake_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
indent {
|
|
putd macro_signature_for(declare_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
putd macro_signature_for(define_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
puts
|
|
}
|
|
end
|
|
|
|
def output_macro_header(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
output_macro_name(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
end
|
|
|
|
# #define #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...)
|
|
def output_macro_name(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
putd "#define " + macro_signature_for(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
end
|
|
|
|
# #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...) \
|
|
def macro_signature_for(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
parameter_list = "#{macro_name}("
|
|
if return_type != ""
|
|
parameter_list += return_type
|
|
parameter_list += ", "
|
|
end
|
|
parameter_list += "CALLING_CONVENTION, " if (has_calling_conventions)
|
|
parameter_list += "FUNCNAME"
|
|
|
|
arg_count.times { |i| parameter_list += ", ARG#{i}_TYPE" }
|
|
|
|
parameter_list += ", ..." if has_varargs
|
|
|
|
parameter_list += ") \\"
|
|
|
|
parameter_list
|
|
end
|
|
|
|
def output_variables(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
in_struct{
|
|
arg_count.times { |argN|
|
|
putd_backslash "DECLARE_ARG(ARG#{argN}_TYPE, #{argN}, FUNCNAME)"
|
|
}
|
|
putd_backslash "DECLARE_ALL_FUNC_COMMON"
|
|
putd_backslash "DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)" unless not is_value_function
|
|
putd_backslash "DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)" unless not is_value_function
|
|
putd_backslash "DECLARE_CUSTOM_FAKE_SEQ_VARIABLES"
|
|
output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
}
|
|
putd_backslash "extern FUNCNAME##_Fake FUNCNAME##_fake;"
|
|
putd_backslash "void FUNCNAME##_reset(void);"
|
|
putd_backslash function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function) + ";"
|
|
end
|
|
|
|
#example: ARG0_TYPE arg0, ARG1_TYPE arg1
|
|
def arg_val_list(args_count)
|
|
return "void" if (args_count == 0)
|
|
arguments = []
|
|
args_count.times { |i| arguments << "ARG#{i}_TYPE arg#{i}" }
|
|
arguments.join(", ")
|
|
end
|
|
|
|
#example: arg0, arg1
|
|
def arg_list(args_count)
|
|
arguments = []
|
|
args_count.times { |i| arguments << "arg#{i}" }
|
|
arguments.join(", ")
|
|
end
|
|
|
|
# RETURN_TYPE (*custom_fake)(ARG0_TYPE arg0);\
|
|
# OR
|
|
# RETURN_TYPE (CALLING_CONVENTION *custom_fake)(ARG0_TYPE arg0);\
|
|
#
|
|
# void (*custom_fake)(ARG0_TYPE arg0, ARG1_TYPE arg1, ARG2_TYPE arg2);\
|
|
def output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
|
ap_list = has_varargs ? ", va_list ap" : ""
|
|
signature = has_calling_conventions ? "(CALLING_CONVENTION *custom_fake)" : "(*custom_fake)"
|
|
signature += "(#{arg_val_list(arg_count)}#{ap_list});"
|
|
putd_backslash return_type + signature
|
|
end
|
|
|
|
def output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
|
ap_list = has_varargs ? ", va_list ap" : ""
|
|
custom_array = has_calling_conventions ? "(CALLING_CONVENTION **custom_fake_seq)" : "(**custom_fake_seq)"
|
|
custom_array += "(#{arg_val_list(arg_count)}#{ap_list});"
|
|
putd_backslash return_type + custom_array
|
|
end
|
|
|
|
# example: RETURN_TYPE FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
|
# OR
|
|
# RETURN_TYPE CALLING_CONVENTION FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
|
def function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
|
varargs = has_varargs ? ", ..." : ""
|
|
calling_conventions = has_calling_conventions ?
|
|
"#{return_type} FFF_GCC_FUNCTION_ATTRIBUTES CALLING_CONVENTION FUNCNAME(#{arg_val_list(arg_count)}#{varargs})" :
|
|
"#{return_type} FFF_GCC_FUNCTION_ATTRIBUTES FUNCNAME(#{arg_val_list(arg_count)}#{varargs})"
|
|
end
|
|
|
|
def output_function_body(arg_count, has_varargs, is_value_function)
|
|
arg_count.times { |i| putd_backslash "SAVE_ARG(FUNCNAME, #{i});" }
|
|
putd_backslash "if(ROOM_FOR_MORE_HISTORY(FUNCNAME)){"
|
|
indent {
|
|
arg_count.times { |i| putd_backslash "SAVE_ARG_HISTORY(FUNCNAME, #{i});" }
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "else{"
|
|
indent {
|
|
putd_backslash "HISTORY_DROPPED(FUNCNAME);"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "INCREMENT_CALL_COUNT(FUNCNAME);"
|
|
putd_backslash "REGISTER_CALL(FUNCNAME);"
|
|
|
|
if has_varargs
|
|
return_type = is_value_function ? "return " : ""
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_len){ /* a sequence of custom fakes */"
|
|
indent {
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_idx < FUNCNAME##_fake.custom_fake_seq_len){"
|
|
indent {
|
|
putd_backslash "va_list ap;"
|
|
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)}, ap);" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "va_end(ap);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)}, ap);" unless is_value_function
|
|
putd_backslash "va_end(ap);" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "else{"
|
|
indent {
|
|
putd_backslash "va_list ap;"
|
|
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)}, ap);" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "va_end(ap);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)}, ap);"
|
|
putd_backslash "va_end(ap);" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "if(FUNCNAME##_fake.custom_fake){"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret;" if is_value_function
|
|
putd_backslash "va_list ap;"
|
|
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
|
}
|
|
custom_fake_call = "FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)}, ap);"
|
|
indent {
|
|
if is_value_function
|
|
putd_backslash "ret = #{custom_fake_call}"
|
|
else
|
|
putd_backslash "#{custom_fake_call}"
|
|
end
|
|
putd_backslash "va_end(ap);"
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" if is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
else
|
|
return_type = is_value_function ? "return " : ""
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_len){ /* a sequence of custom fakes */"
|
|
indent {
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_idx < FUNCNAME##_fake.custom_fake_seq_len){"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)});" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)});" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "else{"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)});" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)});"
|
|
}
|
|
putd_backslash "}"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake){ "
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});"
|
|
}
|
|
putd_backslash "}"
|
|
end
|
|
|
|
putd_backslash "RETURN_FAKE_RESULT(FUNCNAME)" if is_value_function
|
|
end
|
|
|
|
def define_fff_globals
|
|
putd "typedef void (*fff_function_t)(void);"
|
|
putd "typedef struct { "
|
|
indent {
|
|
putd "fff_function_t call_history[FFF_CALL_HISTORY_LEN];"
|
|
putd "unsigned int call_history_idx;"
|
|
}
|
|
putd "} fff_globals_t;"
|
|
puts
|
|
putd "FFF_EXTERN_C"
|
|
putd "extern fff_globals_t fff;"
|
|
putd "FFF_END_EXTERN_C"
|
|
puts
|
|
putd_backslash "#define DEFINE_FFF_GLOBALS"
|
|
indent {
|
|
putd_backslash "FFF_EXTERN_C"
|
|
indent {
|
|
putd_backslash "fff_globals_t fff;"
|
|
}
|
|
putd "FFF_END_EXTERN_C"
|
|
}
|
|
puts
|
|
putd_backslash "#define FFF_RESET_HISTORY()"
|
|
indent {
|
|
putd_backslash "fff.call_history_idx = 0;"
|
|
putd "memset(fff.call_history, 0, sizeof(fff.call_history));"
|
|
}
|
|
puts
|
|
putd_backslash "#define REGISTER_CALL(function)"
|
|
indent {
|
|
putd_backslash "if(fff.call_history_idx < FFF_CALL_HISTORY_LEN)"
|
|
indent {
|
|
putd "fff.call_history[fff.call_history_idx++] = (fff_function_t)function;"
|
|
}
|
|
}
|
|
end
|
|
|
|
def in_struct
|
|
putd_backslash "typedef struct FUNCNAME##_Fake {"
|
|
indent {
|
|
yield
|
|
}
|
|
putd_backslash "} FUNCNAME##_Fake;"
|
|
end
|
|
|
|
def include_guard
|
|
putd "#ifndef FAKE_FUNCTIONS"
|
|
putd "#define FAKE_FUNCTIONS"
|
|
puts
|
|
|
|
yield
|
|
|
|
puts
|
|
putd "#endif /* FAKE_FUNCTIONS */"
|
|
end
|
|
|
|
def msvc_expand_macro_fix
|
|
putd "/* MSVC expand macro fix */"
|
|
putd "#define EXPAND(x) x"
|
|
end
|
|
|
|
def generate_arg_sequence(args, prefix, do_reverse, joinstr)
|
|
fmap = (0..args).flat_map {|i| [prefix + i.to_s]}
|
|
if do_reverse then fmap.reverse.join(joinstr) else fmap.join(", ") end
|
|
end
|
|
|
|
def counting_macro_instance(type, has_calling_conventions, vararg = :non_vararg, prefix = "")
|
|
appendix = (vararg == :vararg) ? "_VARARG" : ""
|
|
if has_calling_conventions
|
|
minus_count = (type == :VOID) ? 2 : 3
|
|
else
|
|
minus_count = (type == :VOID) ? 1 : 2
|
|
end
|
|
|
|
<<-MACRO_COUNTING_INSTANCE
|
|
#define #{prefix}FAKE_#{type.to_s}_FUNC#{appendix}(...) \
|
|
EXPAND(#{prefix}FUNC_#{type.to_s}#{appendix}_(PP_NARG_MINUS#{minus_count}(__VA_ARGS__), __VA_ARGS__))
|
|
|
|
#define #{prefix}FUNC_#{type.to_s}#{appendix}_(N,...) \
|
|
EXPAND(#{prefix}FUNC_#{type.to_s}#{appendix}_N(N,__VA_ARGS__))
|
|
|
|
#define #{prefix}FUNC_#{type.to_s}#{appendix}_N(N,...) \
|
|
EXPAND(#{prefix}FAKE_#{type.to_s}_FUNC ## N#{" ## _VARARG" if vararg == :vararg}(__VA_ARGS__))
|
|
|
|
MACRO_COUNTING_INSTANCE
|
|
end
|
|
|
|
def output_macro_counting_shortcuts(has_calling_conventions)
|
|
has_calling_conventions ?
|
|
(arg_depth = ["3", "2"]; calling_conv = "callingConv, ") :
|
|
(arg_depth = ["2", "1"]; calling_conv = "")
|
|
|
|
msvc_expand_macro_fix
|
|
putd <<-MACRO_COUNTING
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[0]}(...) \
|
|
EXPAND(PP_NARG_MINUS#{arg_depth[0]}_(__VA_ARGS__, PP_RSEQ_N_MINUS#{arg_depth[0]}()))
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[0]}_(...) \
|
|
EXPAND(PP_ARG_MINUS#{arg_depth[0]}_N(__VA_ARGS__))
|
|
|
|
#define PP_ARG_MINUS#{arg_depth[0]}_N(returnVal, #{calling_conv} #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
|
|
|
#define PP_RSEQ_N_MINUS#{arg_depth[0]}() \
|
|
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[1]}(...) \
|
|
EXPAND(PP_NARG_MINUS#{arg_depth[1]}_(__VA_ARGS__, PP_RSEQ_N_MINUS#{arg_depth[1]}()))
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[1]}_(...) \
|
|
EXPAND(PP_ARG_MINUS#{arg_depth[1]}_N(__VA_ARGS__))
|
|
|
|
#define PP_ARG_MINUS#{arg_depth[1]}_N(#{calling_conv} #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
|
|
|
#define PP_RSEQ_N_MINUS#{arg_depth[1]}() \
|
|
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
|
|
|
|
|
|
|
/* DECLARE AND DEFINE FAKE FUNCTIONS - PLACE IN TEST FILES */
|
|
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions)}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions)}
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg)}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg)}
|
|
|
|
/* DECLARE FAKE FUNCTIONS - PLACE IN HEADER FILES */
|
|
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :non_vararg, "DECLARE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :non_vararg, "DECLARE_")}
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg, "DECLARE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg, "DECLARE_")}
|
|
|
|
/* DEFINE FAKE FUNCTIONS - PLACE IN SOURCE FILES */
|
|
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :non_vararg, "DEFINE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :non_vararg, "DEFINE_")}
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg, "DEFINE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg, "DEFINE_")}
|
|
|
|
MACRO_COUNTING
|
|
end
|
|
|
|
def output_c_and_cpp(has_calling_conventions)
|
|
license
|
|
include_guard {
|
|
include_dependencies
|
|
output_constants
|
|
output_internal_helper_macros
|
|
yield
|
|
output_macro_counting_shortcuts(has_calling_conventions)
|
|
}
|
|
end
|
|
|
|
def help
|
|
# Check if we should generate _with_ support for specifying calling conventions
|
|
if (ARGV[0] == "--help" or ARGV[0] == "-h")
|
|
puts "Usage: fakegen.rb [options]
|
|
-h, --help Show this help message
|
|
-wcc, --with-calling-conventions Support specifying calling conventions"
|
|
exit
|
|
end
|
|
yield
|
|
end
|
|
|
|
help {
|
|
# Determine if we should generate with support for calling conventions
|
|
has_calling_conventions = true if (ARGV[0] == "--with-calling-conventions" or ARGV[0] == "-wcc")
|
|
# lets generate!!
|
|
output_c_and_cpp(has_calling_conventions) {
|
|
define_fff_globals
|
|
# Create fake generators for 0..MAX_ARGS
|
|
num_fake_generators = $MAX_ARGS + 1
|
|
num_fake_generators.times {|arg_count| output_macro(arg_count, false, has_calling_conventions, false)}
|
|
num_fake_generators.times {|arg_count| output_macro(arg_count, false, has_calling_conventions, true)}
|
|
# generate the varargs variants
|
|
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, has_calling_conventions, false)}
|
|
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, has_calling_conventions, true)}
|
|
}
|
|
}
|