Allow executing `run.sh`.
parent
a17d6eb3e3
commit
683bf863ab
4
Makefile
4
Makefile
|
@ -1,4 +1,4 @@
|
|||
BUILD=clang -Og -g -I include src/*.c main.c -o bootstrap
|
||||
BUILD=clang -Og -g -I include src/*.c main.c -o bootstrap -lm
|
||||
|
||||
all: build bear
|
||||
|
||||
|
@ -12,6 +12,6 @@ test: test/runner
|
|||
$^
|
||||
|
||||
test/runner: include/*.h src/*.c test/*.h test/*.c
|
||||
clang -I include src/*.c test/*.c -o test/runner
|
||||
clang -I include src/*.c test/*.c -o test/runner -lm
|
||||
|
||||
.PHONY: test
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
#include "validator.h"
|
||||
|
||||
enum SpecEvaluationError {
|
||||
// Then `run.sh` file could not be found.
|
||||
SEE_RUN_SH_NOT_FOUND = 1,
|
||||
// The provided input does not match the expected prompt response type.
|
||||
SEE_INVALID_PROMPT_RESPONSE = 1
|
||||
SEE_INVALID_PROMPT_RESPONSE,
|
||||
};
|
||||
|
||||
enum SpecEvaluationError evaluate_spec_json(
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef _BOOTSTRAP_STRING_BUF_H
|
||||
#define _BOOTSTRAP_STRING_BUF_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* @brief A dynamic character array.
|
||||
*
|
||||
* A `char*` wrapper. Appending `char`s or NUL-terminated strings allocates
|
||||
* additional space as needed.
|
||||
*/
|
||||
struct StringBuf;
|
||||
|
||||
/**
|
||||
* Create a new `StringBuf` instance.
|
||||
*
|
||||
* @param capacity
|
||||
* The initial size of the internal array (including the trailing `NUL`
|
||||
* character). To avoid too many reallocations, aim to make this value large
|
||||
* enough to accommodate the size the string is expected to eventually take.
|
||||
* @return
|
||||
* A new `StringBuf` instance. The caller takes ownership of this value.
|
||||
*
|
||||
* @see string_buf_free
|
||||
*/
|
||||
struct StringBuf *string_buf_new(size_t capacity);
|
||||
|
||||
/**
|
||||
* Return the number of characters contained in the internal buffer.
|
||||
*
|
||||
* @param sb
|
||||
* A valid pointer to a `StringBuf` instance.
|
||||
* @return
|
||||
* The number of characters contained in the internal buffer.
|
||||
*/
|
||||
size_t string_buf_size(struct StringBuf *sb);
|
||||
|
||||
/**
|
||||
* Return the internal `NUL`-terminated string buffer.
|
||||
*
|
||||
* @param sb
|
||||
* A valid pointer to a `StringBuf` instance.
|
||||
* @return
|
||||
* The internally managed string.
|
||||
*/
|
||||
const char *string_buf_value(struct StringBuf *sb);
|
||||
|
||||
/**
|
||||
* Append a character to the end of a `StringBuf`.
|
||||
*
|
||||
* If appending would cause the internal buffer to overflow, reallocates the
|
||||
* internal array to accommodate.
|
||||
*
|
||||
* @param sb
|
||||
* A valid pointer to a `StringBuf` instance.
|
||||
* @param c
|
||||
* The `char` to append to the end of `sb`.
|
||||
*/
|
||||
void string_buf_cappend(struct StringBuf *sb, char c);
|
||||
|
||||
/**
|
||||
* Append a `NUL`-terminated string to the end of a `StringBuf`.
|
||||
*
|
||||
* If appending would cause the internal buffer to overflow, `realloc`s are
|
||||
* performed internally to accommodate.
|
||||
*
|
||||
* @param sb
|
||||
* A valid pointer to a `StringBuf` instance.
|
||||
* @param s
|
||||
* The `char*` to append to the end of `sb`.
|
||||
*/
|
||||
void string_buf_sappend(struct StringBuf *sb, const char s[static 1]);
|
||||
|
||||
/**
|
||||
* Convert a `StringBuf` instance into a `char*`.
|
||||
*
|
||||
* @param sb
|
||||
* A valid pointer to a `StringBuf` instance.
|
||||
* @return
|
||||
* A null pointer if `sb` is null. Otherwise a `NUL`-terminated string
|
||||
* corresponding to the value of `sb`. The caller takes ownership of this
|
||||
* value.
|
||||
*/
|
||||
const char *string_buf_convert(struct StringBuf *sb);
|
||||
|
||||
/**
|
||||
* Deallocate a previously allocated `StringBuf` instance.
|
||||
*
|
||||
* @param sb
|
||||
* A valid pointer to a `StringBuf` instance.
|
||||
*
|
||||
* @see string_buf_new
|
||||
*/
|
||||
void string_buf_free(struct StringBuf *sb);
|
||||
|
||||
#endif /* _BOOTSTRAP_STRING_BUF_H */
|
10
main.c
10
main.c
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "cJSON.h"
|
||||
#include "config.h"
|
||||
#include "evaluator.h"
|
||||
#include "parser.h"
|
||||
#include "validator.h"
|
||||
|
||||
|
@ -73,7 +74,14 @@ static int run(const char *root_dir, const char *target) {
|
|||
goto cleanup_parsed;
|
||||
}
|
||||
|
||||
// TODO: Run `run.sh`.
|
||||
switch (evaluate_spec_json(config, prompts)) {
|
||||
case SEE_RUN_SH_NOT_FOUND:
|
||||
fprintf(stderr, "Could not find `%s/run.sh`.\n", target);
|
||||
goto cleanup_parsed;
|
||||
case SEE_INVALID_PROMPT_RESPONSE:
|
||||
fprintf(stderr, "Could not interpret response.\n");
|
||||
goto cleanup_parsed;
|
||||
}
|
||||
|
||||
retval = EXIT_SUCCESS;
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cp -r "template/*" "$OUT"
|
||||
cp -r "template" "$OUT"
|
||||
|
|
|
@ -57,4 +57,5 @@ void config_free(struct Config *config) {
|
|||
return;
|
||||
}
|
||||
free(config);
|
||||
config = 0;
|
||||
}
|
||||
|
|
|
@ -35,4 +35,5 @@ void dyn_array_free(struct DynArray *a) {
|
|||
}
|
||||
free(a->buf);
|
||||
free(a);
|
||||
a = 0;
|
||||
}
|
||||
|
|
|
@ -2,36 +2,61 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "path.h"
|
||||
#include "string_buf.h"
|
||||
|
||||
static int find_run_sh(const struct Config *const config, FILE **handle) {
|
||||
static enum SpecEvaluationError find_run_sh(const struct Config *const config) {
|
||||
struct stat sb;
|
||||
const char *segments[] = {config->root_dir, config->target, "run.sh"};
|
||||
char *filepath =
|
||||
join_path_segments(sizeof(segments) / sizeof(char *), segments);
|
||||
int stat_res = stat(filepath, &sb);
|
||||
free(filepath);
|
||||
|
||||
int retval = 0;
|
||||
// It is ok if the file does not exist. It is not ok if we couldn't open the
|
||||
// file for any other reason.
|
||||
*handle = fopen(filepath, "r");
|
||||
if (!*handle && errno != ENOENT) {
|
||||
retval = errno;
|
||||
if (stat_res == -1 && errno == ENOENT) {
|
||||
return SEE_RUN_SH_NOT_FOUND;
|
||||
}
|
||||
|
||||
free(filepath);
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum SpecEvaluationError evaluate_spec_json(
|
||||
const struct Config *const config, const struct DynArray *const prompts
|
||||
) {
|
||||
enum SpecEvaluationError retval = find_run_sh(config);
|
||||
if (retval != 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (prompts) {
|
||||
for (int i = 0; i < prompts->size; ++i) {
|
||||
}
|
||||
// TODO: Display prompts and collect answers.
|
||||
}
|
||||
|
||||
// TODO: Run `run.sh`.
|
||||
const char *segments[] = {config->root_dir, config->target, "run.sh"};
|
||||
char *filepath =
|
||||
join_path_segments(sizeof(segments) / sizeof(char *), segments);
|
||||
|
||||
struct StringBuf *command_buf = string_buf_new(1024);
|
||||
string_buf_sappend(command_buf, "cd ");
|
||||
string_buf_sappend(command_buf, config->root_dir);
|
||||
string_buf_cappend(command_buf, '/');
|
||||
string_buf_sappend(command_buf, config->target);
|
||||
string_buf_sappend(command_buf, " && ");
|
||||
string_buf_sappend(command_buf, "OUT=");
|
||||
string_buf_sappend(command_buf, config->cwd);
|
||||
string_buf_cappend(command_buf, ' ');
|
||||
string_buf_sappend(command_buf, filepath);
|
||||
const char *command = string_buf_convert(command_buf);
|
||||
|
||||
free(filepath);
|
||||
|
||||
// TODO: Want to return this status out.
|
||||
int status = system(command);
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#include "string_buf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct StringBuf {
|
||||
char *buf;
|
||||
// The length of @buf excluding `NUL`.
|
||||
size_t size;
|
||||
// The allocated size of @buf including `NUL`.
|
||||
size_t _capacity;
|
||||
};
|
||||
|
||||
struct StringBuf *string_buf_new(size_t capacity) {
|
||||
struct StringBuf *sb = malloc(sizeof(struct StringBuf));
|
||||
sb->buf = calloc(capacity, sizeof(char));
|
||||
sb->size = 0;
|
||||
sb->_capacity = capacity;
|
||||
return sb;
|
||||
}
|
||||
|
||||
size_t string_buf_size(struct StringBuf *sb) {
|
||||
assert(sb);
|
||||
|
||||
return sb->size;
|
||||
}
|
||||
|
||||
const char *string_buf_value(struct StringBuf *sb) {
|
||||
assert(sb);
|
||||
|
||||
return sb->buf;
|
||||
}
|
||||
|
||||
void string_buf_cappend(struct StringBuf *sb, char c) {
|
||||
assert(sb);
|
||||
|
||||
if (sb->_capacity) {
|
||||
sb->_capacity *= 2;
|
||||
} else {
|
||||
sb->_capacity = 2;
|
||||
}
|
||||
|
||||
sb->buf = realloc((void *)sb->buf, sb->_capacity);
|
||||
sb->buf[sb->size++] = c;
|
||||
sb->buf[sb->size] = 0;
|
||||
}
|
||||
|
||||
void string_buf_sappend(struct StringBuf *sb, const char s[static 1]) {
|
||||
assert(sb);
|
||||
|
||||
double goal = sb->size + strlen(s) + 1;
|
||||
double denom = sb->_capacity ? sb->_capacity : 1;
|
||||
double scale = pow(2, ceil(log2(goal / denom)));
|
||||
|
||||
if (sb->_capacity) {
|
||||
sb->_capacity *= scale;
|
||||
} else {
|
||||
sb->_capacity = scale;
|
||||
}
|
||||
|
||||
sb->buf = realloc((void *)sb->buf, sb->_capacity);
|
||||
for (const char *i = s; *i; ++i) {
|
||||
sb->buf[sb->size++] = *i;
|
||||
}
|
||||
sb->buf[sb->size] = 0;
|
||||
}
|
||||
|
||||
const char *string_buf_convert(struct StringBuf *sb) {
|
||||
assert(sb);
|
||||
|
||||
const char *buf = sb->buf;
|
||||
free(sb);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void string_buf_free(struct StringBuf *sb) {
|
||||
assert(sb);
|
||||
|
||||
free((void *)sb->buf);
|
||||
free(sb);
|
||||
sb = 0;
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include "test_dyn_array.h"
|
||||
#include "test_parser.h"
|
||||
#include "test_path.h"
|
||||
#include "test_string_buf.h"
|
||||
#include "test_validator.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
@ -18,6 +19,10 @@ int main(int argc, char *argv[]) {
|
|||
sput_run_test(test_dyn_array_zero_capacity);
|
||||
sput_run_test(test_dyn_array_nonzero_capacity);
|
||||
|
||||
sput_enter_suite("string_buf");
|
||||
sput_run_test(test_string_buf_sappend);
|
||||
sput_run_test(test_string_buf_cappend);
|
||||
|
||||
sput_enter_suite("path");
|
||||
sput_run_test(test_join_path_single_segments);
|
||||
sput_run_test(test_join_path_multiple_segments);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef _BOOTSTRAP_TEST_STRING_BUF
|
||||
#define _BOOTSTRAP_TEST_STRING_BUF
|
||||
|
||||
#include "sput.h"
|
||||
#include "string_buf.h"
|
||||
|
||||
static void test_string_buf_sappend() {
|
||||
struct StringBuf *sb = string_buf_new(0);
|
||||
string_buf_sappend(sb, "hello world");
|
||||
string_buf_sappend(sb, "!!");
|
||||
|
||||
sput_fail_unless(
|
||||
string_buf_size(sb) == strlen("hello world!!"), "sappend size"
|
||||
);
|
||||
const char *converted = string_buf_convert(sb);
|
||||
sput_fail_unless(
|
||||
strcmp(converted, "hello world!!") == 0, "sappend converted"
|
||||
);
|
||||
}
|
||||
|
||||
static void test_string_buf_cappend() {
|
||||
struct StringBuf *sb = string_buf_new(0);
|
||||
string_buf_sappend(sb, "hello world");
|
||||
string_buf_cappend(sb, '!');
|
||||
string_buf_cappend(sb, '!');
|
||||
|
||||
sput_fail_unless(
|
||||
string_buf_size(sb) == strlen("hello world!!"), "cappend size"
|
||||
);
|
||||
const char *converted = string_buf_convert(sb);
|
||||
sput_fail_unless(
|
||||
strcmp(converted, "hello world!!") == 0, "cappend converted"
|
||||
);
|
||||
}
|
||||
|
||||
static void test_string_buf_nonzero_capacity() {
|
||||
struct StringBuf *sb = string_buf_new(100);
|
||||
string_buf_sappend(sb, "hello world");
|
||||
string_buf_cappend(sb, '!');
|
||||
string_buf_free(sb);
|
||||
sput_fail_unless(sb == 0, "free");
|
||||
}
|
||||
|
||||
#endif /* _BOOTSTRAP_TEST_STRING_BUF */
|
Loading…
Reference in New Issue