Skip to content

Commit 313cea9

Browse files
committed
Lots of tests, fixes, changes and improvements
1 parent 261b5ab commit 313cea9

21 files changed

Lines changed: 782 additions & 151 deletions

File tree

code/cbuild_api.cpp

Lines changed: 120 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,48 @@
1111

1212
extern Platform_Info platform;
1313

14+
Config_Crash_Handler crash_handler_hook;
15+
16+
static void __require_non_null (const void *value, const char *function_name, const char *parameter_name) {
17+
if (value == nullptr) [[unlikely]] {
18+
char temp_buffer[512];
19+
Memory_Arena temp_arena(temp_buffer, 512);
20+
print(&temp_arena, "Invalid '%' value passed to '%': value must NOT BE Null\n", parameter_name, function_name);
21+
crash_handler_hook(EXIT_FAILURE);
22+
}
23+
}
24+
#define require_non_null(PARAM) __require_non_null(PARAM, __FUNCTION__, #PARAM)
25+
26+
static void __require_non_empty (const char *value, const char *function_name, const char *parameter_name) {
27+
if (value[0] == '\0') [[unlikely]] {
28+
char temp_buffer[512];
29+
Memory_Arena temp_arena(temp_buffer, 512);
30+
print(&temp_arena, "Invalid '%' value passed to '%': value must NOT BE empty\n", parameter_name, function_name);
31+
crash_handler_hook(EXIT_FAILURE);
32+
}
33+
}
34+
#define require_non_empty(PARAM) __require_non_empty(PARAM, __FUNCTION__, #PARAM)
35+
1436
const char * get_argument_or_default (const Arguments *args, const char *key, const char *default_value) {
1537
if (key == nullptr) return default_value;
1638
if (key[0] == '\0') return default_value;
1739
if (is_empty_list(&args->args)) return default_value;
1840

1941
auto key_length = strlen(key);
2042
for (auto arg: args->args) {
21-
if (arg.type == Argument::Type::Flag) continue;
22-
23-
if (strncmp(arg.key, key, key_length) == 0) return arg.value;
43+
if ((strlen(arg.key) == key_length) &&
44+
(strncmp(arg.key, key, key_length) == 0)) {
45+
return (arg.type == Argument::Type::Flag) ? key : arg.value;
46+
}
2447
}
2548

2649
return default_value;
2750
}
2851

2952
bool find_toolchain_by_type (Project *project, Toolchain_Type type, Toolchain_Configuration *out_configuration) {
53+
require_non_null(project);
54+
require_non_null(out_configuration);
55+
3056
auto [status, result] = lookup_toolchain_by_type(&project->arena, type);
3157
check_status(status);
3258

@@ -36,6 +62,8 @@ bool find_toolchain_by_type (Project *project, Toolchain_Type type, Toolchain_Co
3662
}
3763

3864
void overwrite_toolchain (Project *project, Toolchain_Configuration toolchain) {
65+
require_non_null(project);
66+
3967
/*
4068
#REWORK:
4169
This is a temporary solution for 2 reasons:
@@ -51,42 +79,53 @@ void overwrite_toolchain (Project *project, Toolchain_Configuration toolchain) {
5179
}
5280

5381
void set_toolchain (Project *project, Toolchain_Type type) {
82+
require_non_null(project);
83+
5484
auto [status, result] = lookup_toolchain_by_type(&project->arena, type);
5585
if (!status) {
5686
print(&project->arena, "FATAL ERROR: Requested toolchain wasn't found on the system.\n");
57-
exit(EXIT_FAILURE);
87+
crash_handler_hook(EXIT_FAILURE);
5888
}
5989

6090
overwrite_toolchain(project, result);
6191
}
6292

6393
void disable_registry (Project *project) {
94+
require_non_null(project);
95+
6496
project->registry_disabled = true;
6597
}
6698

6799
void register_action (Project *project, const char *name, Action_Type action) {
100+
require_non_null(project);
101+
require_non_null(name);
102+
require_non_empty(name);
103+
68104
add(&project->arena, &project->user_defined_commands, {
69105
.name = copy_string(&project->arena, String(name)),
70106
.proc = action
71107
});
72108
}
73109

74110
void set_output_location (Project *project, const char *folder_path) {
111+
require_non_null(project);
112+
require_non_null(folder_path);
113+
require_non_empty(folder_path);
114+
75115
project->output_location = copy_string(&project->arena, folder_path);
76116
}
77117

78118
static Target * create_target (Project *project, const char *name) {
79-
auto arena = &project->arena;
119+
require_non_null(project);
120+
require_non_null(name);
121+
require_non_empty(name);
80122

81-
if (name == nullptr) {
82-
print(arena, "FATAL ERROR: Annonymous targets (target without a name) are not allowed.");
83-
exit(EXIT_FAILURE);
84-
}
123+
auto arena = &project->arena;
85124

86125
auto name_length = strlen(name);
87126
if (name_length > Target::Max_Name_Limit) {
88127
print(arena, "Target's name length is limited to % symbols. If your case requires a longer target name, please submit an issue on the project's Github page\n", Target::Max_Name_Limit);
89-
exit(EXIT_FAILURE);
128+
crash_handler_hook(EXIT_FAILURE);
90129
}
91130

92131
for (auto cursor = name; *cursor; cursor++) {
@@ -95,15 +134,15 @@ static Target * create_target (Project *project, const char *name) {
95134
(value >= 'A' && value <= 'Z') ||
96135
(value >= '0' && value <= '9') ||
97136
(value == '_'))) {
98-
print(arena, "FATAL ERROR: Target name contains disallowed characters, only alphanumeric characters are allow and '_'");
99-
exit(EXIT_FAILURE);
137+
print(arena, "FATAL ERROR: Target name contains disallowed characters, only alphanumeric characters are allow and '_'\n");
138+
crash_handler_hook(EXIT_FAILURE);
100139
}
101140
}
102141

103142
for (auto t: project->targets) {
104143
if (compare_strings(t->name, name)) {
105144
print(&project->arena, "FATAL ERROR: Target '%' already defined in the project. It's not allowed to have multiple targets with the same name\n", name);
106-
exit(EXIT_FAILURE);
145+
crash_handler_hook(EXIT_FAILURE);
107146
}
108147
}
109148

@@ -140,43 +179,60 @@ Target * add_executable (Project *project, const char *name) {
140179
return target;
141180
}
142181

143-
void add_source_file (Target *target, const char *_file_path) {
182+
void add_source_file (Target *target, const char *file_path) {
183+
require_non_null(target);
184+
require_non_null(file_path);
185+
require_non_empty(file_path);
186+
144187
auto arena = &target->project->arena;
145188

146-
auto file_path = get_absolute_path(arena, _file_path);
147-
add(arena, &target->files, *file_path);
189+
auto abs_file_path = get_absolute_path(arena, file_path);
190+
if (!check_file_exists(&abs_file_path)) {
191+
print(arena, "File '%' wasn't found, please check the correctness of the specified path and that the file exists\n", *abs_file_path);
192+
crash_handler_hook(EXIT_FAILURE);
193+
}
194+
195+
add(arena, &target->files, *abs_file_path);
148196

149197
target->project->total_files_count += 1;
150198
}
151199

152-
void exclude_source_file (Target *target, const char *_file_path) {
200+
void exclude_source_file (Target *target, const char *file_path) {
201+
require_non_null(target);
202+
require_non_null(file_path);
203+
require_non_empty(file_path);
204+
153205
auto arena = &target->project->arena;
154206

155207
if (is_empty_list(&target->files)) return;
156208

157-
auto [status, file_path] = get_absolute_path(arena, _file_path);
158-
if (!status) {
159-
print(arena, "File '%' not found, please check the correctness of the specified path\n", _file_path);
160-
exit(EXIT_FAILURE);
209+
auto [status, abs_file_path] = get_absolute_path(arena, file_path);
210+
if (!check_file_exists(&abs_file_path)) {
211+
print(arena, "File '%' not found, please check the correctness of the specified path\n", file_path);
212+
crash_handler_hook(EXIT_FAILURE);
161213
}
162214

163215
auto [found, position] =
164216
find_position(&target->files, [&] (const File_Path *node) {
165-
return compare_strings(*node, file_path);
217+
return compare_strings(*node, abs_file_path);
166218
});
167219

168220
if (!found) {
169-
print(arena, "File '%' not included for the target %\n", _file_path, target->name);
221+
print(arena, "File '%' not included for the target %\n", file_path, target->name);
170222
return;
171223
}
172224

173225
if (!remove_at(&target->files, position)) {
174-
print(arena, "Couldn't remove file '%' from the target due to an internal error, please report this case.\n", _file_path);
226+
print(arena, "Couldn't remove file '%' from the target due to an internal error, please report this case.\n", file_path);
175227
return;
176228
}
177229
}
178230

179231
void add_include_search_path (Target *target, const char *include_path) {
232+
require_non_null(target);
233+
require_non_null(include_path);
234+
require_non_empty(include_path);
235+
180236
auto arena = &target->project->arena;
181237

182238
auto file_path = get_absolute_path(arena, include_path);
@@ -185,42 +241,76 @@ void add_include_search_path (Target *target, const char *include_path) {
185241
}
186242

187243
void add_all_sources_from_directory (Target *target, const char *directory, const char *extension, bool recurse) {
244+
require_non_null(target);
245+
require_non_null(directory);
246+
require_non_empty(directory);
247+
require_non_null(extension);
248+
require_non_empty(extension);
249+
250+
auto arena = &target->project->arena;
251+
252+
auto folder_path = get_absolute_path(arena, directory);
253+
if (!folder_path.status) {
254+
print(arena, "Couldn't get absolute path for '%'\n", directory);
255+
crash_handler_hook(EXIT_FAILURE);
256+
}
257+
258+
if (!check_directory_exists(&folder_path)) {
259+
print(arena, "Directory '%' specified for 'add_all_sources_from_directory' wasn't found, please ensure that the path is correct and the directory exists\n", folder_path);
260+
crash_handler_hook(EXIT_FAILURE);
261+
}
262+
188263
auto existing_target_count = target->files.count;
189264
list_files_in_directory(&target->project->arena, &target->files, directory, extension, recurse);
190265
target->project->total_files_count += (target->files.count - existing_target_count);
191266
}
192267

193268
void add_compiler_option (Target *target, const char *option) {
194-
if (option == nullptr) return;
269+
require_non_null(target);
270+
require_non_null(option);
271+
require_non_empty(option);
195272

196273
auto arena = &target->project->arena;
197274
add(arena, &target->options.compiler, copy_string(arena, option));
198275
}
199276

200277
void add_linker_option (Target *target, const char *option) {
201-
if (option == nullptr) return;
278+
require_non_null(target);
279+
require_non_null(option);
280+
require_non_empty(option);
202281

203282
auto arena = &target->project->arena;
204283
add(arena, &target->options.linker, copy_string(arena, option));
205284
}
206285

207286
void link_with_target (Target *target, Target *dependency) {
208-
if (dependency == nullptr) return;
287+
require_non_null(target);
288+
require_non_null(dependency);
209289

210290
auto arena = &target->project->arena;
291+
292+
if (target == dependency) {
293+
print(arena, "Invalid 'dependency' value passed to 'link_with_target': the target cannot be linked with itself\n");
294+
crash_handler_hook(EXIT_FAILURE);
295+
}
296+
211297
add(arena, &target->depends_on, const_cast<const Target *>(dependency));
212298
add(arena, &dependency->required_by, const_cast<const Target *>(target));
213299
}
214300

215301
void link_with_library (Target *target, const char *library_name) {
216-
if (library_name == nullptr || library_name[0] == '\0') return;
302+
require_non_null(target);
303+
require_non_null(library_name);
304+
require_non_empty(library_name);
217305

218306
auto arena = &target->project->arena;
219307
auto copied = copy_string(arena, library_name);
220308
add(arena, &target->link_libraries, copied);
221309
}
222310

223311
void add_target_hook (Target *target, Hook_Type type, Hook_Func func) {
312+
require_non_null(target);
313+
224314
switch (type) {
225315
case Hook_Type_After_Target_Linked: {
226316
target->hooks.on_linked = func;
@@ -230,5 +320,7 @@ void add_target_hook (Target *target, Hook_Type type, Hook_Func func) {
230320
}
231321

232322
const char * get_target_name (const Target *target) {
323+
require_non_null(target);
324+
233325
return target->name.value;
234326
}

code/cbuild_api.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
struct Target_Tracker;
1414

15+
typedef void (*Config_Crash_Handler) (u32 exit_code);
16+
1517
enum Target_Arch {
1618
Target_Arch_x86,
1719
Target_Arch_x64
@@ -48,8 +50,8 @@ struct Project {
4850

4951
List<User_Defined_Command> user_defined_commands;
5052

51-
String output_location; // It's the value that the user can override from the configuration
52-
File_Path output_location_path;
53+
String output_location; // It's the value that the user can override from the configuration
54+
File_Path output_location_path; // The actual path resolved by the loader that should be used by the builder
5355

5456
Target_Arch target_architecture = Target_Arch_x64;
5557

code/main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,19 @@
2121
#error "TOOL_VERSION and API_VERSION values must be defined at compile time"
2222
#endif
2323

24+
extern Config_Crash_Handler crash_handler_hook;
25+
2426
CLI_Flags global_flags;
2527

2628
File_Path working_directory_path;
2729
File_Path cache_directory_path;
2830

2931
Platform_Info platform;
3032

33+
static void config_exit_failure (u32 exit_code) {
34+
exit(exit_code);
35+
}
36+
3137
static Result<Arguments> parse_arguments (Memory_Arena *arena, const CLI_Command *command) {
3238
use(Status_Code);
3339

@@ -81,6 +87,8 @@ static Result<Arguments> parse_arguments (Memory_Arena *arena, const CLI_Command
8187
}
8288

8389
int main (int argc, char **argv) {
90+
crash_handler_hook = config_exit_failure;
91+
8492
auto arena = Memory_Arena { reserve_virtual_memory(megabytes(64)) };
8593

8694
bool silence_report = false;

0 commit comments

Comments
 (0)