diff --git a/SConstruct b/SConstruct index 04076c8..448a0f6 100644 --- a/SConstruct +++ b/SConstruct @@ -1,17 +1,14 @@ # -*- python -*- from pathlib import Path +from dataclasses import dataclass # # File lists # -build_dir = "build/" -variant_dir = build_dir + "default/" -src_dir = "src/" - -program_sources = ["src/main.c"] -lib_srcs = [ +PROGRAM_SOURCES = ["src/main.c"] +LIB_SRCS = [ "src/arena.c", "src/builtins.c", "src/evaluate.c", @@ -21,69 +18,159 @@ lib_srcs = [ "src/utility.c", "src/parse.c", ] -lib_includes = ["src/"] +LIB_INCLUDES = ["src/"] -test_srcs = [ +TEST_SRCS = [ "test/test_arena.c", "test/test_e2e.c", "test/test_parse.c", "test/test_scope.c", "test/test_utility.c", ] -test_lib_srcs = ["third-party/unity/src/unity.c"] -test_lib_includes = ["third-party/unity/src/"] - -# -# Construct Environments -# - -VariantDir(variant_dir, ".", duplicate=0) -# Minimization flags for later: -# CCFLAGS = ["-Oz", "-flto", "-ffunction-sections", "-fdata-sections", "-Wl,--gc-sections", "-lreadline"] -CCFLAGS = ["-ggdb", "-O0", "-Werror", "-Wall", "-Wextra", "-Wno-unused-parameter"] -env = Environment( - CPPPATH=lib_includes, COMPILATIONDB_USE_ABSPATH=True, CCFLAGS=CCFLAGS -) -env.Tool("compilation_db") -env.CompilationDatabase() - -test_env = env.Clone() -test_env.Append(CPPPATH=test_lib_includes) +TEST_LIB_SRCS = ["third-party/unity/src/unity.c"] +TEST_LIB_INCLUDES = ["third-party/unity/src/"] -# -# Construct Environments -# - -# Generate REPL +def with_suffix(path, suffix): + return str(Path(path).with_suffix(suffix)) -lib_objs = [env.Object(p) for p in lib_srcs] -env.Program(variant_dir + "uclisp", lib_objs + program_sources, LIBS=["readline"]) +@dataclass +class BuildVariant: + variant_name: str + cc: str + ccflags: list[str] + libs: list[str] + linkflags: list[str] + enable_compile_commands_db: bool = False -# Generate unit test executables + def built(self, path): + return self.variant_dir + path -test_lib_objs = [ - test_env.Object(variant_dir + p, CPPPATH=lib_includes + test_lib_includes) - for p in test_lib_srcs + def enable_compile_commands(self): + self.env.Tool("compilation_db") + self.env.CompilationDatabase() + + def configure_lib(self): + self.lib_objs = [ + self.env.Object(target=self.built(with_suffix(p, ".o")), source=p) + for p in LIB_SRCS + ] + + def configure_repl(self): + pgm_objs = [ + self.env.Object(target=self.built(with_suffix(p, ".o")), source=p) + for p in PROGRAM_SOURCES + ] + self.repl_program = self.env.Program( + self.built("uclisp"), self.lib_objs + pgm_objs + ) + + def configure_tests(self): + test_lib_objs = [ + self.test_env.Object(target=self.built(with_suffix(p, ".o")), source=p) + for p in TEST_LIB_SRCS + ] + + test_deps = test_lib_objs + self.lib_objs + test_objs = [ + self.test_env.Object(target=self.built(with_suffix(p, ".o")), source=p) + for p in TEST_SRCS + ] + + self.tests = [ + self.test_env.Program( + self.built(with_suffix(p, "")), + test_deps + [self.built(with_suffix(p, ".o"))], + ) + for p in TEST_SRCS + ] + + def configure(self): + self.variant_dir = f"build/{self.variant_name}/" + + # + # Construct Environments + # + + self.env = Environment( + CC=self.cc, + CPPPATH=LIB_INCLUDES, + COMPILATIONDB_USE_ABSPATH=True, + CCFLAGS=self.ccflags, + LINKFLAGS=self.linkflags, + LIBS=self.libs, + ) + + if self.enable_compile_commands_db: + self.enable_compile_commands() + + self.test_env = self.env.Clone() + self.test_env.Append(CPPPATH=TEST_LIB_INCLUDES) + + self.configure_lib() + self.configure_repl() + self.configure_tests() + + self.env.Substfile( + target=self.built("run_tests.sh"), + source="run_tests.sh.in", + SUBST_DICT={ + "@tests@": " ".join( + str(Path(str(test[0])).resolve()) for test in self.tests + ) + }, + ) + + self.env.Command( + self.built("run_tests"), + self.built("run_tests.sh"), + Chmod(self.built("run_tests.sh"), 0o755), + ) + + # Copy default build compile commands to root, which is where tools seem to want + # it. + if self.enable_compile_commands_db: + Copy("compile_commands.json", self.built("compile_commands.json")) + + self.env.Alias(self.variant_name, self.variant_dir) + + +base_ccflags = ["-Werror", "-Wall", "-Wextra", "-Wno-unused-parameter"] +debug_ccflags = ["-ggdb", "-O0"] +release_ccflags = [ + "-Oz", + "-flto", + "-ffunction-sections", + "-fdata-sections", + "-Wl,--gc-sections", ] -test_deps = test_lib_objs + lib_objs -tests = [test_env.Program(variant_dir + p, [p] + test_deps) for p in test_srcs] -# -# Generate Test Runner script -# +variants = [ + BuildVariant( + variant_name="debug", + cc="gcc", + ccflags=debug_ccflags + base_ccflags, + libs=["readline"], + linkflags=[], + enable_compile_commands_db=True, + ), + BuildVariant( + variant_name="release", + cc="gcc", + ccflags=release_ccflags + base_ccflags, + linkflags=[], + libs=["readline"], + ), + BuildVariant( + variant_name="arm-debug", + cc="arm-none-eabi-gcc", + ccflags=["-DNO_READLINE"] + debug_ccflags + base_ccflags, + libs=[], + linkflags=["--specs=nosys.specs"], + ), +] -env.Substfile( - "run_tests.sh.in", - SUBST_DICT={ - "@tests@": " ".join(str(Path(str(test[0])).resolve()) for test in tests) - }, -) - -env.Command(variant_dir + "run_tests", "run_tests.sh", Chmod("run_tests.sh", 0o755)) - -# Copy default build compile commands to root, which is where tools seem to want -# it. -Copy("compile_commands.json", variant_dir + "compile_commands.json") +for variant in variants: + variant.configure() diff --git a/src/main.c b/src/main.c index 9fe7121..8c7f271 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,10 @@ #include #include + +#ifndef NO_READLINE #include #include +#endif #include "uclisp.h" @@ -71,14 +74,18 @@ int main(int argc, const char **argv) { if (argc < 2) { while (1) { - char *line = readline("> "); + char *line = NULL; +#ifndef NO_READLINE + line = readline("> "); if (line == NULL) { break; } if (strlen(line) > 0) { add_history(line); } - +#else + // TODO +#endif struct ucl_object *sexp = ucl_parse(line); struct ucl_object *result = NULL; if (sexp->type == UCL_TYPE_ERROR) {