Updated the command line parsing a little bit, as well as the README.

This commit is contained in:
Christoffer Lerno
2019-07-24 01:56:37 +02:00
parent bd7c743e93
commit e229d19b7c
6 changed files with 317 additions and 175 deletions

View File

@@ -3,5 +3,259 @@
// license that can be found in the LICENSE file.
#include "build_options.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
BuildOptions build_options;
#include "../utils/errors.h"
static const char* DEFAULT_TARGET = "default";
BuildOptions build_options;
static int arg_index;
static int arg_count;
static const char** args;
static const char* current_arg;
#define OUTPUT(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__)
#define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(); exit(EXIT_FAILURE); } while (0)
static void usage(void)
{
OUTPUT("Usage: %s [<options>] <command> [<args>]", args[0]);
OUTPUT("");
OUTPUT("Commands:");
OUTPUT("");
OUTPUT(" compile <file1> [<file2> ...] Compile files without a project into an executable.");
OUTPUT(" init <project name> Initialize a new project structure.");
OUTPUT(" build [<target>] Build the target in the current project.");
OUTPUT(" clean Clean all build files.");
OUTPUT(" run [<target>] Run (and build if needed) the target in the current project.");
OUTPUT(" dist [<target>] Clean and build a target for distribution.");
OUTPUT(" docs [<target>] Generate documentation for the target.");
OUTPUT(" bench [<target>] Benchmark a target.");
OUTPUT(" clean-run [<target>] Clean, then run the target.");
OUTPUT(" compile-run <file1> [<file2> ...] Compile files then immediately run the result.");
OUTPUT("");
OUTPUT("Options:");
OUTPUT(" --lib <dir> - Use this directory as the c3 library path.");
OUTPUT(" --path <dir> - Use this as the base directory for the current command.");
OUTPUT(" --template <template> - Use a different template: \"lib\", \"staticlib\" or a path.");
OUTPUT(" --about - Prints a short description of C3.");
}
static const char* check_dir(const char *path)
{
static char *original_path = NULL;
if (!original_path)
{
original_path = getcwd(NULL, 0);
}
if (chdir(path) == -1) error_exit("The path \"%s\" does not point to a valid directory.", path);
int err = chdir(original_path);
assert(!err);
return path;
}
static inline bool at_end()
{
return arg_index == arg_count - 1;
}
static inline const char* next_arg()
{
assert(!at_end());
current_arg = args[++arg_index];
return current_arg;
}
static inline bool next_is_opt()
{
return args[arg_index + 1][0] == '-';
}
static inline bool match_longopt(const char* name)
{
return strcmp(&current_arg[2], name) == 0;
}
static inline bool match_shortopt(const char* name)
{
return strcmp(&current_arg[1], name) == 0;
}
void append_file()
{
if (build_options.file_count == MAX_FILES)
{
fprintf(stderr, "Max %d files may be specified\n", MAX_FILES);
exit(EXIT_FAILURE);
}
build_options.files[build_options.file_count++] = current_arg;
}
static bool arg_match(const char *candidate)
{
return strcmp(current_arg, candidate) == 0;
}
static void parse_optional_target()
{
if (at_end() || next_is_opt())
{
build_options.target = DEFAULT_TARGET;
}
else
{
build_options.target = next_arg();
}
}
static void parse_command(void)
{
if (arg_match("init"))
{
build_options.command = COMMAND_INIT;
if (at_end() || next_is_opt()) error_exit("Expected a project name after init");
build_options.project_name = next_arg();
return;
}
if (arg_match("compile"))
{
build_options.command = COMMAND_COMPILE;
return;
}
if (arg_match("build"))
{
build_options.command = COMMAND_BUILD;
parse_optional_target();
return;
}
if (arg_match("run"))
{
build_options.command = COMMAND_RUN;
parse_optional_target();
return;
}
if (arg_match("compile-run"))
{
build_options.command = COMMAND_COMPILE_RUN;
parse_optional_target();
return;
}
if (arg_match("clean-run"))
{
build_options.command = COMMAND_CLEAN_RUN;
parse_optional_target();
return;
}
if (arg_match("clean"))
{
build_options.command = COMMAND_CLEAN;
return;
}
if (arg_match("dist"))
{
build_options.command = COMMAND_CLEAN_RUN;
parse_optional_target();
return;
}
if (arg_match("docs"))
{
build_options.command = COMMAND_DOCS;
parse_optional_target();
return;
}
if (arg_match("bench"))
{
build_options.command = COMMAND_BENCH;
parse_optional_target();
return;
}
FAIL_WITH_ERR("Cannot process the unknown command \"%s\".", current_arg);
}
static void parse_option()
{
switch (current_arg[1])
{
case 'h':
break;
case '-':
if (match_longopt("about"))
{
OUTPUT("The C3 Compiler");
OUTPUT("C3 is low level programming language based on C.");
exit(EXIT_SUCCESS);
}
if (match_longopt("lib"))
{
if (at_end() || next_is_opt()) error_exit("error: --lib needs a directory.");
if (build_options.lib_count == MAX_LIB_DIRS) error_exit("Max %d libraries may be specified.", MAX_LIB_DIRS);
build_options.lib_dir[build_options.lib_count++] = check_dir(next_arg());
return;
}
if (match_longopt("path"))
{
if (at_end() || next_is_opt()) error_exit("error: --path needs a directory.");
build_options.path = check_dir(next_arg());
return;
}
if (match_longopt("help"))
{
break;
}
break;
default:
break;
}
FAIL_WITH_ERR("Cannot process the unknown option \"%s\".", current_arg);
}
void parse_arguments(int argc, const char *argv[])
{
if (argc < 2)
{
usage();
exit(EXIT_SUCCESS);
}
build_options.path = ".";
build_options.command = COMMAND_MISSING;
arg_count = argc;
args = argv;
for (arg_index = 1; arg_index < arg_count; arg_index++)
{
current_arg = args[arg_index];
if (current_arg[0] == '-')
{
parse_option();
continue;
}
if (build_options.command == COMMAND_MISSING)
{
parse_command();
continue;
}
if (build_options.command == COMMAND_COMPILE_RUN || build_options.command == COMMAND_COMPILE)
{
append_file();
continue;
}
FAIL_WITH_ERR("Found the unexpected argument \"%s\".", current_arg);
}
if (build_options.command == COMMAND_MISSING)
{
FAIL_WITH_ERR("No command found.");
}
}

View File

@@ -7,6 +7,21 @@
#define MAX_LIB_DIRS 1024
#define MAX_FILES 2048
typedef enum
{
COMMAND_MISSING = 0,
COMMAND_COMPILE,
COMMAND_INIT,
COMMAND_BUILD,
COMMAND_COMPILE_RUN,
COMMAND_RUN,
COMMAND_CLEAN_RUN,
COMMAND_CLEAN,
COMMAND_DIST,
COMMAND_DOCS,
COMMAND_BENCH,
} CompilerCommand;
typedef struct
{
const char* lib_dir[MAX_LIB_DIRS];
@@ -14,8 +29,11 @@ typedef struct
const char* files[MAX_FILES];
int file_count;
const char* project_name;
const char* target;
const char* path;
const char* original_path;
CompilerCommand command;
} BuildOptions;
extern BuildOptions build_options;
extern BuildOptions build_options;
void parse_arguments(int argc, const char *argv[]);

View File

@@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "project_creation.h"
#include "build_options.h"
#include "../utils/string_utils.h"

View File

@@ -1,179 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <assert.h>
#include "build/build_options.h"
#include "build/project_creation.h"
#include "utils/errors.h"
static void usage(const char* name) {
fprintf(stderr, "Usage: %s <options> <target>\n", name);
fprintf(stderr, "Options:\n");
fprintf(stderr, " --lib <dir> - use this directory as the c3 library path\n");
fprintf(stderr, " --path <dir> - use this as the base directory for the current command\n");
exit(EXIT_SUCCESS);
}
typedef enum
{
COMMAND_COMPILE,
COMMAND_NEW,
} CompilerCommand;
static CompilerCommand command;
static int arg_index;
static int arg_count;
static const char** args;
static const char* current_arg;
static void select_compiler_command(CompilerCommand new_command)
{
if (command != COMMAND_COMPILE)
{
fprintf(stderr, "Please select only one command\n");
exit(EXIT_FAILURE);
}
command = new_command;
}
static const char* check_dir(const char *path)
{
if (chdir(path) == -1) error_exit("The path \"%s\" does not point to a valid directory.", path);
int err = chdir(build_options.original_path);
assert(!err);
return path;
}
static inline bool at_end()
{
return arg_index == arg_count - 1;
}
static inline const char* next_arg()
{
assert(!at_end());
current_arg = args[++arg_index];
return current_arg;
}
static inline bool next_is_arg()
{
return args[arg_index + 1][0] == '-';
}
static inline bool match_longopt(const char* name)
{
return strcmp(&current_arg[2], name) == 0;
}
static inline bool match_shortopt(const char* name)
{
return strcmp(&current_arg[1], name) == 0;
}
void append_file()
{
if (build_options.file_count == MAX_FILES)
{
fprintf(stderr, "Max %d files may be specified\n", MAX_FILES);
exit(EXIT_FAILURE);
}
build_options.files[build_options.file_count++] = current_arg;
}
static void parse_option()
{
switch (current_arg[1])
{
case 'h':
break;
case 'n':
if (match_shortopt("new"))
{
select_compiler_command(COMMAND_NEW);
if (at_end() || next_is_arg()) error_exit("Expected a project name after -new");
build_options.project_name = next_arg();
return;
}
break;
case '-':
if (match_longopt("about"))
{
fprintf(stderr, "The C3 Compiler\n");
fprintf(stderr, "\nC3 is an evolution of C.\n");
exit(0);
}
if (match_longopt("lib"))
{
if (at_end() || next_is_arg()) error_exit("error: --lib needs a directory.");
if (current_arg[0] == '-') error_exit("Expected a directory after --lib.");
if (build_options.lib_count == MAX_LIB_DIRS) error_exit("Max %d libraries may be specified.", MAX_LIB_DIRS);
build_options.lib_dir[build_options.lib_count++] = check_dir(next_arg());
return;
}
if (match_longopt("path"))
{
if (at_end() || next_is_arg()) error_exit("error: --path needs a directory.");
build_options.path = check_dir(next_arg());
return;
}
if (match_longopt("help"))
{
break;
}
break;
default:
break;
}
usage(args[0]);
}
static void handle_compile_command()
{
printf("TODO\n");
exit(EXIT_SUCCESS);
}
static void parse_arguments()
{
command = COMMAND_COMPILE;
for (arg_index = 1; arg_index < arg_count; arg_index++)
{
current_arg = args[arg_index];
if (current_arg[0] == '-')
{
parse_option();
continue;
}
append_file();
}
switch (command)
{
case COMMAND_NEW:
create_project();
break;
case COMMAND_COMPILE:
handle_compile_command();
break;
default:
exit(EXIT_FAILURE);
}
}
int main(int argc, const char *argv[])
{
build_options.path = ".";
build_options.original_path = getcwd(NULL, 0);
arg_count = argc;
args = argv;
parse_arguments();
return EXIT_SUCCESS;
parse_arguments(argc, argv);
switch (build_options.command)
{
case COMMAND_INIT:
create_project();
break;
case COMMAND_COMPILE:
case COMMAND_COMPILE_RUN:
case COMMAND_MISSING:
case COMMAND_BUILD:
case COMMAND_RUN:
case COMMAND_CLEAN_RUN:
case COMMAND_CLEAN:
case COMMAND_DIST:
case COMMAND_DOCS:
case COMMAND_BENCH:
printf("TODO\n");
}
return 0;
}