Skip to content

Commit e07ab75

Browse files
authored
Fix Segfault and UX of the c3c project add-target subcommand (#1729)
* Fix UX of the `c3c project add-target` subcommand - Fix segfault on `project.json` containing empty map `{}` - Enable `add-target` to operate on non-existing project files - Extend `add-target` syntax to accept source through CLI args This enables the following workflow without friction and needless crashes: ```console $ cat > main.c3 <<END import std::io; fn void main() { io::printfn("Hello, World"); } END $ c3c project add-target main executable main.c3 $ c3c run ``` * Fix read_project() call in fetch_project() * Keep the style of curlies consistent
1 parent cf10837 commit e07ab75

File tree

3 files changed

+40
-10
lines changed

3 files changed

+40
-10
lines changed

src/build/build.h

+1
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ typedef struct BuildOptions_
453453
ProjectSubcommand command;
454454
const char *target_name;
455455
TargetType target_type;
456+
const char **sources;
456457
} project_options;
457458
CompileOption compile_option;
458459
TrustLevel trust_level;

src/build/build_options.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ static void project_usage()
198198
PRINTF("Usage: %s [<options>] project <subcommand> [<args>]", args[0]);
199199
PRINTF("");
200200
PRINTF("Project Subcommands:");
201-
PRINTF(" view view the current projects structure");
202-
PRINTF(" add-target <name> <target_type> add a new target to the project");
201+
PRINTF(" view view the current projects structure");
202+
PRINTF(" add-target <name> <target_type> [sources...] add a new target to the project");
203203
#if FETCH_AVAILABLE
204204
PRINTF(" fetch fetch missing project libraries");
205205
#endif
@@ -222,6 +222,11 @@ static void parse_project_subcommand(BuildOptions *options)
222222
if (at_end() || next_is_opt()) error_exit("Expected a target type like 'executable' or 'static-lib'");
223223
options->project_options.target_type = (TargetType)get_valid_enum_from_string(next_arg(), "type", targets, ELEMENTLEN(targets), "a target type like 'executable' or 'static-lib'");
224224

225+
while (!at_end() && !next_is_opt())
226+
{
227+
vec_add(options->project_options.sources, next_arg());
228+
}
229+
225230
return;
226231
}
227232
if (arg_match("fetch"))

src/build/project_manipulation.c

+32-8
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@
33
#define PRINTFN(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT
44
#define PRINTF(string, ...) fprintf(stdout, string, ##__VA_ARGS__) // NOLINT
55

6-
static JSONObject *read_project(const char **file_used)
6+
static JSONObject *read_project(const char **file_used, bool assume_empty_if_not_exists)
77
{
88
size_t size;
99
const char *project_filename = file_exists(PROJECT_JSON5) ? PROJECT_JSON5 : PROJECT_JSON;
1010
*file_used = project_filename;
11-
char *read = file_read_all(project_filename, &size);
11+
char *read;
12+
if (assume_empty_if_not_exists)
13+
{
14+
// If project file does not exist assume the project simply being empty instead of
15+
// failing. This is useful for such commands as `project add-target`. It enables
16+
// them to update otherwise non-existing project files reducing the friction.
17+
read = "{}";
18+
if (file_exists(project_filename)) read = file_read_all(project_filename, &size);
19+
}
20+
else
21+
{
22+
read = file_read_all(project_filename, &size);
23+
}
1224
JsonParser parser;
1325
json_init_string(&parser, read);
1426
JSONObject *json = json_parse(&parser);
@@ -26,7 +38,7 @@ static JSONObject *read_project(const char **file_used)
2638
const char** get_project_dependency_directories()
2739
{
2840
const char *filename;
29-
JSONObject *json = read_project(&filename);
41+
JSONObject *json = read_project(&filename, false);
3042

3143
const char *target = NULL;
3244
const char **deps_dirs = NULL;
@@ -57,7 +69,7 @@ const char** get_project_dependencies()
5769
const char *filename;
5870
const char** dependencies = NULL;
5971

60-
JSONObject *project_json = read_project(&filename);
72+
JSONObject *project_json = read_project(&filename, false);
6173
JSONObject *dependencies_json = json_map_get(project_json, "dependencies");
6274

6375
FOREACH(JSONObject *, element, dependencies_json->elements)
@@ -290,7 +302,7 @@ void fetch_project(BuildOptions* options)
290302
const char **libdirs = get_project_dependency_directories();
291303
const char **deps = get_project_dependencies();
292304
const char *filename;
293-
JSONObject *project_json = read_project(&filename);
305+
JSONObject *project_json = read_project(&filename, false);
294306

295307
JSONObject *targets_json = json_map_get(project_json, "targets");
296308

@@ -359,7 +371,7 @@ void add_libraries_to_project_file(const char** libs, const char* target_name) {
359371
//TODO! Target name option not implemented
360372

361373
const char *filename;
362-
JSONObject *project_json = read_project(&filename);
374+
JSONObject *project_json = read_project(&filename, false);
363375

364376
// TODO! check if target is specified and exists (NULL at the moment)
365377
JSONObject *libraries_json = json_map_get(project_json, "dependencies");
@@ -396,9 +408,15 @@ void add_libraries_to_project_file(const char** libs, const char* target_name) {
396408
void add_target_project(BuildOptions *build_options)
397409
{
398410
const char *filename;
399-
JSONObject *project_json = read_project(&filename);
411+
JSONObject *project_json = read_project(&filename, true);
400412
JSONObject *targets_json = json_map_get(project_json, "targets");
401413

414+
if (targets_json == NULL)
415+
{
416+
targets_json = json_new_object(J_OBJECT);
417+
json_map_set(project_json, "targets", targets_json);
418+
}
419+
402420
if (json_map_get(targets_json, build_options->project_options.target_name) != NULL)
403421
{
404422
error_exit("Target with name '%s' already exists", build_options->project_options.target_name);
@@ -408,6 +426,12 @@ void add_target_project(BuildOptions *build_options)
408426

409427
JSONObject *new_target = json_new_map();
410428
json_map_set(new_target, "type", target_type_obj);
429+
JSONObject *target_sources = json_new_object(J_ARRAY);
430+
FOREACH(const char *, source, build_options->project_options.sources)
431+
{
432+
vec_add(target_sources->elements, json_new_string(source));
433+
}
434+
json_map_set(new_target, "sources", target_sources);
411435

412436
json_map_set(targets_json, build_options->project_options.target_name, new_target);
413437

@@ -419,7 +443,7 @@ void add_target_project(BuildOptions *build_options)
419443
void view_project(BuildOptions *build_options)
420444
{
421445
const char *filename;
422-
JSONObject *project_json = read_project(&filename);
446+
JSONObject *project_json = read_project(&filename, false);
423447

424448
/* General information */
425449
VIEW_MANDATORY_STRING_ARRAY("Authors", "authors");

0 commit comments

Comments
 (0)