I believe both of the CI systems (Github and Gitlab) use Docker.
These systems will place a new line in the input pipe before starting the programs being called.
This is bad behavior but I cannot do anything about it.
The previous design of Featureless Make is to use only the input pipe if it is specified.
This results in the Featureless Make operations to fail.
This redesigns the pipe handling behavior of Featurleess Make rather than try to submit a bug report to a team that is likely to completely ignore me.
The new behavior is as follows:
- If a pipe is specified, treat the pipe is prepended to the buffer that will be used for processing the file.
- This works for both "build" operations and "make" operations.
- The input pipe is treated as a "fakefile" when using the "make" operation.
- The input pipe is treated as a "settings" file when using the "build" operation.
- The input pipe may have an error bit so always clear the error.
- The "build" and "make" operations can be called within the "make" operation, recursively, so the pipe must only be processed by the outermost fakefile.
- The operation needs to be detected and identified as "default" to better determine how to handle missing files when a pipe is present and when a pipe is not present.
- Avoid resetting the buffer on every load so that the input pipe can be prepended without being reset.
Some of the code is cleaned up and simplified in regards to files.
Only perform the pre-process cleanup checks when there are more than two operations.
fl_print_format(" For example, with '%[%r%r ./my_fakefile%]' the fakefile at", file.stream, context.set.notable, f_console_symbol_long_enable_s, fake_long_fakefile_s, context.set.notable);
fl_print_format(" '%[./my_fakefile%]' is used if found, but if it is not found then no other paths are attempted.%r%r", file.stream, context.set.notable, context.set.notable, f_string_eol_s, f_string_eol_s);
- fl_print_format(" When piping data to this program, the piped data is treated as a %[%r%].%r", file.stream, context.set.notable, fake_make_parameter_variable_fakefile_s, context.set.notable, f_string_eol_s);
- fl_print_format(" Only the %[%r%] operation is supported when using piped data.%r%r", file.stream, context.set.notable, fake_other_operation_make_s, context.set.notable, f_string_eol_s, f_string_eol_s);
+ fl_print_format(" When piping data to this program, the piped data is treated as if it were prepended to the %[%r%]", file.stream, context.set.notable, fake_make_parameter_variable_fakefile_s, context.set.notable);
+ fl_print_format("or the %[%r%], depending on the operation.%r%r", file.stream, context.set.notable, fake_make_parameter_variable_settings_s, context.set.notable, f_string_eol_s, f_string_eol_s);
funlockfile(file.stream);
operations_length += main->parameters.array[fake_parameter_operation_make_e].locations.used;
operations_length += main->parameters.array[fake_parameter_operation_skeleton_e].locations.used;
- // Ensure the default operation exists.
- if (!operations_length && !main->parameters.remaining.used) {
+ // Ensure the default operation always exists.
+ if (operations_length) {
+ data.flag |= fake_data_flag_has_operation_e;
+ }
+ else {
operations_length = 1;
}
uint8_t operations[operations_length];
f_string_static_t operations_name = f_string_static_t_initialize;
- if (main->parameters.array[fake_parameter_operation_build_e].locations.used || main->parameters.array[fake_parameter_operation_clean_e].locations.used || main->parameters.array[fake_parameter_operation_make_e].locations.used || main->parameters.array[fake_parameter_operation_skeleton_e].locations.used) {
+ if (data.flag & fake_data_flag_has_operation_e) {
f_array_length_t locations[operations_length];
f_array_length_t locations_length = 0;
f_array_length_t i = 0;
++locations_length;
} // for
}
- else if (operations_length) {
+ else {
operations[0] = fake_operation_make_e;
- }
- else if (!main->process_pipe) {
- status = F_status_set_error(F_parameter);
- if (main->error.verbosity != f_console_verbosity_quiet_e) {
- fll_print_format("%r%[%QYou failed to specify a valid operation.%]%r%r", main->error.to.stream, f_string_eol_s, main->error.context, main->error.prefix, main->error.context, f_string_eol_s, f_string_eol_s);
+ if (!main->process_pipe && main->parameters.remaining.used) {
+ status = F_status_set_error(F_parameter);
+
+ if (main->error.verbosity != f_console_verbosity_quiet_e) {
+ fll_print_format("%r%[%QYou failed to specify a valid operation.%]%r", main->error.to.stream, f_string_eol_s, main->error.context, main->error.prefix, main->error.context, f_string_eol_s);
+ }
}
}
- if (F_status_is_error_not(status) && main->process_pipe) {
- if (operations_length > 1 || operations_length && operations[0] != fake_operation_make_e) {
+ if (F_status_is_error_not(status)) {
+ if (main->parameters.array[fake_parameter_operation_build_e].locations.used && main->parameters.array[fake_parameter_operation_make_e].locations.used) {
status = F_status_set_error(F_parameter);
if (main->error.verbosity != f_console_verbosity_quiet_e) {
flockfile(main->error.to.stream);
- fl_print_format("%r%[%QWhen using an input pipe, only the '%]", main->error.to.stream, f_string_eol_s, main->error.context, main->error.prefix, main->error.context);
+ fl_print_format("%r%[%QThe operation '%]", main->error.to.stream, f_string_eol_s, main->error.context, main->error.prefix, main->error.context);
+ fl_print_format("%[%r%]", main->error.to.stream, main->error.notable, fake_other_operation_build_s, main->error.notable);
+ fl_print_format("%[' cannot be specified with the operation '%]", main->error.to.stream, main->error.context, main->error.context);
fl_print_format("%[%r%]", main->error.to.stream, main->error.notable, fake_other_operation_make_s, main->error.notable);
- fl_print_format("%[' operation is supported.%]%r", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s);
+ fl_print_format("%['.%]%r", main->error.to.stream, main->error.context, main->error.context, f_string_eol_s);
funlockfile(main->error.to.stream);
}
{
uint8_t i = 0;
- if (main->process_pipe) {
+ if (main->process_pipe && !(data.flag & fake_data_flag_has_operation_e)) {
data.file_data_build_fakefile.used = 0;
status = f_string_dynamic_append(f_string_ascii_minus_s, &data.file_data_build_fakefile);
fll_error_print(data.main->error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
}
}
- else {
- // Pre-process and perform validation when "clean" is before a "build" or "make" command as a safety check.
+ // Pre-process and perform validation when "clean" is before a "build" or "make" command as a safety check.
+ if (operations_length > 1) {
for (uint8_t has_clean = F_false; i < operations_length; ++i) {
if (operations[i] == fake_operation_clean_e) {
status = fake_validate_parameter_paths(&data);
- if (F_status_is_error_not(status)) {
+ if (F_status_is_error_not(status) && !main->process_pipe) {
f_string_static_t *path = 0;
if (operations[i] == fake_operation_build_e) {
}
if (F_status_is_error_not(status)) {
- status = fake_build_operate(&data, 0);
+ status = fake_build_operate(&data, 0, main->process_pipe);
}
}
else if (data.operation == fake_operation_clean_e) {
#endif // _di_fake_build_load_environment_
#ifndef _di_fake_build_load_setting_
- void fake_build_load_setting(fake_data_t * const data, const f_string_statics_t * const build_arguments, fake_build_setting_t * const setting, f_status_t * const status) {
+ void fake_build_load_setting(fake_data_t * const data, const f_string_statics_t * const build_arguments, const bool process_pipe, fake_build_setting_t * const setting, f_status_t * const status) {
if (F_status_is_error(*status)) return;
return;
}
- const f_string_static_t setting_file = build_arguments && build_arguments->used ? build_arguments->array[0] : f_string_empty_s;
f_string_statics_t modes_custom = f_string_statics_t_initialize;
const f_string_statics_t *modes_custom_ptr = 0;
if (build_arguments && build_arguments->used > 1) {
modes_custom.array = build_arguments->array + 1;
modes_custom.used = build_arguments->used - 1;
-
modes_custom_ptr = &modes_custom;
}
f_string_static_t path_file = f_string_static_t_initialize;
- path_file.used = data->path_data_build.used + setting_file.used;
+
+ if (build_arguments && build_arguments->used) {
+ path_file.used = data->path_data_build.used + build_arguments->array[0].used;
+ }
+ else if (data->flag & fake_data_flag_has_operation_e) {
+ path_file.used = data->file_data_build_settings.used;
+ }
+ else {
+ path_file.used = f_string_ascii_minus_s.used;
+ }
f_char_t path_file_string[path_file.used + 1];
path_file.string = path_file_string;
path_file_string[path_file.used] = 0;
+ if (build_arguments && build_arguments->used) {
+ memcpy(path_file_string, data->path_data_build.string, sizeof(f_char_t) * data->path_data_build.used);
+ memcpy(path_file_string + data->path_data_build.used, build_arguments->array[0].string, sizeof(f_char_t) * build_arguments->array[0].used);
+ }
+ else if (data->flag & fake_data_flag_has_operation_e) {
+ memcpy(path_file_string, data->file_data_build_settings.string, sizeof(f_char_t) * data->file_data_build_settings.used);
+ }
+ else {
+ memcpy(path_file_string, f_string_ascii_minus_s.string, sizeof(f_char_t) * f_string_ascii_minus_s.used);
+ }
+
{
f_string_dynamic_t buffer = f_string_dynamic_t_initialize;
-
f_fss_objects_t objects = f_fss_objects_t_initialize;
f_fss_contents_t contents = f_fss_contents_t_initialize;
- if (setting_file.used) {
- memcpy(path_file_string, data->path_data_build.string, sizeof(f_char_t) * data->path_data_build.used);
- memcpy(path_file_string + data->path_data_build.used, setting_file.string, sizeof(f_char_t) * setting_file.used);
+ if (process_pipe) {
+ *status = fake_pipe_buffer(data, &buffer);
- *status = fake_file_buffer(data, path_file, &buffer);
+ if (F_status_is_error(*status)) {
+ buffer.used = 0;
+ }
+ else {
+ *status = f_string_dynamic_append_assure(f_string_eol_s, &buffer);
+ }
}
- else {
- *status = fake_file_buffer(data, data->file_data_build_settings, &buffer);
+
+ if (F_status_is_error_not(*status)) {
+ if (build_arguments && build_arguments->used || (data->flag & fake_data_flag_has_operation_e)) {
+ *status = fake_file_buffer(data, path_file, process_pipe ? F_false : F_true, &buffer);
+ }
}
if (F_status_is_error_not(*status)) {
fll_error_print(data->main->error, F_status_set_fine(*status), "f_fss_apply_delimit", F_true);
}
else {
- fake_build_load_setting_process(data, F_true, setting_file.used ? path_file : data->file_data_build_settings, modes_custom_ptr, buffer, objects, contents, setting, status);
+ fake_build_load_setting_process(data, F_true, path_file, modes_custom_ptr, buffer, objects, contents, setting, status);
}
}
fl_print_format("%r%[%QThe setting '%]", data->main->error.to.stream, f_string_eol_s, data->main->error.context, data->main->error.prefix, data->main->error.context);
fl_print_format("%[%Q%]", data->main->error.to.stream, data->main->error.notable, names[i], data->main->error.notable);
fl_print_format("%[' is required but is not specified in the settings file '%]", data->main->error.to.stream, data->main->error.context, data->main->error.context);
- fl_print_format("%[%Q%]", data->main->error.to.stream, data->main->error.notable, setting_file.used ? path_file : data->file_data_build_settings, data->main->error.notable);
+ fl_print_format("%[%Q%]", data->main->error.to.stream, data->main->error.notable, path_file, data->main->error.notable);
fl_print_format("%['.%]%r", data->main->error.to.stream, data->main->error.context, data->main->error.context, f_string_eol_s);
funlockfile(data->main->error.to.stream);
* If build_arguments.used is 0, then the default or program parameter supplied file is used.
* Set the first argument used length to 0 to use the default program parameter supplied file.
* Set the second argument used length to 0 without any further arguments to not use any modes.
+ * @param process_pipe
+ * If TRUE, then use the program input pipe.
+ * If FALSE, then ignore the program input pipe.
* @param setting
* All build related setting data from the build setting file are loaded into this.
* These setting will have any specified mode property applied.
* Status codes (with error bit) are returned on any problem.
*/
#ifndef _di_fake_build_load_setting_
- extern void fake_build_load_setting(fake_data_t * const data, const f_string_statics_t * const build_arguments, fake_build_setting_t * const setting, f_status_t * const status) F_attribute_visibility_internal_d;
+ extern void fake_build_load_setting(fake_data_t * const data, const f_string_statics_t * const build_arguments, const bool process_pipe, fake_build_setting_t * const setting, f_status_t * const status) F_attribute_visibility_internal_d;
#endif // _di_fake_build_load_setting_
/**
#endif // _di_fake_build_objects_add_
#ifndef _di_fake_build_operate_
- f_status_t fake_build_operate(fake_data_t * const data, const f_string_statics_t * const build_arguments) {
+ f_status_t fake_build_operate(fake_data_t * const data, const f_string_statics_t * const build_arguments, const bool process_pipe) {
if (fll_program_standard_signal_received(data->main)) {
fake_print_signal_received(data);
macro_f_mode_t_set_default_umask(mode, data->main->umask);
- fake_build_load_setting(data, build_arguments, &data_build.setting, &status);
+ fake_build_load_setting(data, build_arguments, process_pipe, &data_build.setting, &status);
if (F_status_is_fine(status)) {
if (data->main->output.verbosity != f_console_verbosity_quiet_e && data->main->output.verbosity != f_console_verbosity_error_e) {
* If build_arguments.used is 0, then the default or program parameter supplied file is used.
* Set the first argument used length to 0 to use the default program parameter supplied file.
* Set the second argument used length to 0 without any further arguments to not use any modes.
+ * @param process_pipe
+ * If TRUE, then use the program input pipe.
+ * If FALSE, then ignore the program input pipe.
*
* @return
* F_none on success.
* Status codes (with error bit) are returned on any problem.
*/
#ifndef _di_fake_build_operate_
- extern f_status_t fake_build_operate(fake_data_t * const data, const f_string_statics_t * const build_arguments) F_attribute_visibility_internal_d;
+ extern f_status_t fake_build_operate(fake_data_t * const data, const f_string_statics_t * const build_arguments, const bool process_pipe) F_attribute_visibility_internal_d;
#endif // _di_fake_build_operate_
/**
/**
* The program data.
*
+ * fake_data_flag_*:
+ * - has_operation: Designate that an operation is explicitly passed.
+ *
* argv: The argument structure in the progam data parameters for simplifying syntax.
* at: The processed at parameter value.
*
+ * flag: A set of flags, such as designating that no operations are provided.
* operation: A code representing the currrent operation.
*
* fakefile: The fakefile data.
* mode: The mode data.
*/
#ifndef _di_fake_data_t_
+ enum {
+ fake_data_flag_has_operation_e = 0x1,
+ };
+
typedef struct {
fll_program_data_t *main;
f_string_static_t *argv;
+ uint8_t flag;
uint8_t operation;
f_string_dynamic_t fakefile;
0, \
0, \
0, \
+ 0, \
f_string_dynamic_t_initialize, \
f_string_dynamic_t_initialize, \
f_string_dynamic_t_initialize, \
}
// When custom fakefile or settings are used and they are paths to a file, remove the default path.
- if (data->main->process_pipe || f_path_is(data->fakefile) == F_true || f_file_exists(data->fakefile, F_true) == F_true) {
+ if (f_path_is(data->fakefile) == F_true || f_file_exists(data->fakefile, F_true) == F_true) {
data->file_data_build_fakefile.used = 0;
}
- if (data->main->process_pipe || f_path_is(data->settings) == F_true || f_file_exists(data->settings, F_true) == F_true) {
+ if (f_path_is(data->settings) == F_true || f_file_exists(data->settings, F_true) == F_true) {
data->file_data_build_settings.used = 0;
}
{
#endif // _di_fake_execute_
#ifndef _di_fake_file_buffer_
- f_status_t fake_file_buffer(fake_data_t * const data, const f_string_static_t path_file, f_string_dynamic_t * const buffer) {
-
- f_file_t file = f_file_t_initialize;
- char *name_function = "f_file_exists";
- f_status_t status = F_none;
+ f_status_t fake_file_buffer(fake_data_t * const data, const f_string_static_t path_file, const bool required, f_string_dynamic_t * const buffer) {
if (fll_program_standard_signal_received(data->main)) {
fake_print_signal_received(data);
return F_status_set_error(F_interrupt);
}
- status = f_file_exists(path_file, F_true);
+ f_file_t file = f_file_t_initialize;
+ char *name_function = "f_file_exists";
+
+ f_status_t status = f_file_exists(path_file, F_true);
if (status == F_true) {
{
size_file = fake_common_initial_buffer_max_d;
}
- status = f_string_dynamic_resize(size_file, buffer);
+ status = f_string_dynamic_increase_by(size_file, buffer);
if (F_status_is_error(status)) {
const f_string_static_t message = macro_f_string_static_t_initialize("allocate buffer size for", 0, 24);
fll_error_file_print(data->main->error, F_status_set_fine(status), name_function, F_true, path_file, message, fll_error_file_type_file_e);
- f_string_dynamic_resize(0, buffer);
-
return status;
}
}
}
}
else if (status == F_false) {
- status = F_status_set_error(F_file_found_not);
+ if (required) {
+ status = F_status_set_error(F_file_found_not);
+ }
}
if (F_status_is_error(status)) {
fll_error_file_print(data->main->error, F_status_set_fine(status), name_function, F_true, path_file, f_file_operation_read_s, fll_error_file_type_file_e);
-
- f_string_dynamic_resize(0, buffer);
}
return status;
file.stream = F_type_input_d;
file.size_read = fake_default_allocation_pipe_d;
- buffer->used = 0;
status = f_string_dynamic_increase_by(fake_common_initial_buffer_max_d, buffer);
if (F_status_is_error(status)) {
const f_string_static_t message = macro_f_string_static_t_initialize("allocate buffer size for", 0, 24);
fll_error_file_print(data->main->error, F_status_set_fine(status), "f_string_dynamic_increase_by", F_true, f_string_ascii_minus_s, message, fll_error_file_type_file_e);
- f_string_dynamic_resize(0, buffer);
-
return status;
}
+ // Reset the error state before processing.
+ clearerr(F_type_input_d);
+
do {
if (fll_program_standard_signal_received(data->main)) {
- buffer->used = 0;
-
fake_print_signal_received(data);
return F_status_set_error(F_interrupt);
if (F_status_is_error(status)) {
fll_error_file_print(data->main->error, F_status_set_fine(status), "f_file_stream_read_block", F_true, f_string_ascii_minus_s, f_file_operation_read_s, fll_error_file_type_file_e);
-
- buffer->used = 0;
}
return status;
#ifndef _di_fake_validate_parameter_paths_
f_status_t fake_validate_parameter_paths(fake_data_t * const data) {
- // Only perform these checks when not a pipe.
- if (data->main->process_pipe) return F_none;
-
if (fll_program_standard_signal_received(data->main)) {
fake_print_signal_received(data);
uint8_t parameters_required[] = {
F_false,
- F_true,
+ data->main->process_pipe ? F_false : F_true,
F_false,
};
* The program data.
* @param path_file
* The path to the file to load.
+ * @param required
+ * If TRUE, then return error when file is not found.
+ * If FALSE, then return F_false when file is not found.
* @param buffer
* A buffer containing the contents of the file.
*
* @return
* F_none on success.
+ * F_false on file not found and file is not required.
*
+ * F_file_found_not (with error bit) if file is not found and file is required.
* F_interrupt (with error bit) on receiving a terminate process signal, such as an interrupt signal.
*
* Status codes (with error bit) are returned on any problem.
*/
#ifndef _di_fake_file_buffer_
- extern f_status_t fake_file_buffer(fake_data_t * const data, const f_string_static_t path_file, f_string_dynamic_t * const buffer) F_attribute_visibility_internal_d;
+ extern f_status_t fake_file_buffer(fake_data_t * const data, const f_string_static_t path_file, const bool required, f_string_dynamic_t * const buffer) F_attribute_visibility_internal_d;
#endif // _di_fake_file_buffer_
/**
#endif
#ifndef _di_fake_make_load_fakefile_
- void fake_make_load_fakefile(fake_make_data_t * const data_make, f_status_t * const status) {
+ void fake_make_load_fakefile(fake_make_data_t * const data_make, const bool process_pipe, f_status_t * const status) {
if (F_status_is_error(*status)) return;
data_make->fakefile.used = 0;
- if (data_make->main->process_pipe) {
+ if (process_pipe) {
*status = fake_pipe_buffer(data_make->data, &data_make->buffer);
+
+ if (F_status_is_error(*status)) {
+ data_make->buffer.used = 0;
+ }
+ else {
+ *status = f_string_dynamic_append_assure(f_string_eol_s, &data_make->buffer);
+ }
}
- else {
- *status = fake_file_buffer(data_make->data, data_make->data->file_data_build_fakefile, &data_make->buffer);
+
+ if (F_status_is_error_not(*status)) {
+ *status = fake_file_buffer(data_make->data, data_make->data->file_data_build_fakefile, process_pipe ? F_false : F_true, &data_make->buffer);
}
if (F_status_is_error(*status)) return;
}
if (F_status_is_error_not(*status) && data_make->setting_make.load_build) {
- fake_build_load_setting(data_make->data, 0, &data_make->setting_build, status);
+ fake_build_load_setting(data_make->data, 0, F_false, &data_make->setting_build, status);
if (F_status_is_error(*status) && *status != F_status_set_error(F_interrupt)) {
fll_error_print(data_make->main->error, F_status_set_fine(*status), "fake_build_load_setting", F_true);
*
* @param data_make
* All make related setting data, including data from the fakefile and the build settings file.
+ * @param process_pipe
+ * If TRUE, then use the program input pipe.
+ * If FALSE, then ignore the program input pipe.
* @param status
* The return status.
*
* @see fake_build_load_setting()
*/
#ifndef _di_fake_make_load_fakefile_
- extern void fake_make_load_fakefile(fake_make_data_t * const data_make, f_status_t * const status) F_attribute_visibility_internal_d;
+ extern void fake_make_load_fakefile(fake_make_data_t * const data_make, const bool process_pipe, f_status_t * const status) F_attribute_visibility_internal_d;
#endif // _di_fake_make_load_fakefile_
/**
fake_make_load_parameters(&data_make, &status);
- fake_make_load_fakefile(&data_make, &status);
+ fake_make_load_fakefile(&data_make, data_make.main->process_pipe, &status);
if (F_status_is_error(status)) {
fake_make_data_delete(&data_make);
#ifndef _di_fake_make_operate_process_type_build_
f_status_t fake_make_operate_process_type_build(fake_make_data_t * const data_make) {
- const f_status_t status = fake_build_operate(data_make->data, data_make->cache_arguments.used ? &data_make->cache_arguments : 0);
+ const f_status_t status = fake_build_operate(data_make->data, data_make->cache_arguments.used ? &data_make->cache_arguments : 0, F_false);
if (F_status_set_fine(status) == F_interrupt) return status;
return fake_make_operate_process_return(data_make, F_status_is_error(status) ? 1 : 0);