diff options
| author | hachem <im@hachem.wtf> | 2026-09-18 18:40:37 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-18 18:40:37 +0200 |
| commit | 2e9e1221538fe8148e542103552ff339891dd21b (patch) | |
| tree | 7bb2a930f7df4c59cf6a17d0988b162eb3e213a3 | |
| parent | 875e2a75eae13298a36a9f631305a68e40653606 (diff) | |
feat: add nasm projects
| -rw-r--r-- | mkproj.1 | 6 | ||||
| -rw-r--r-- | mkproj.c | 149 | ||||
| -rwxr-xr-x | tests.sh | 6 |
3 files changed, 150 insertions, 11 deletions
@@ -36,9 +36,13 @@ sanitizers enabled by default. .TP .BR \-l ", " \-\-lang " " \fIlang\fR Source language: -.BR c ", " cpp " or " objc . +.BR c ", " cpp ", " objc " or " nasm . Default .BR c . +.B nasm +projects are x86\-64 assembly and only support the +.B make +build system (executables only, no language standard). .TP .BR \-s ", " \-\-std " " \fIstd\fR Language standard passed to the compiler as @@ -29,6 +29,7 @@ enum Language language_c, language_cpp, language_objc, + language_nasm, language_count }; @@ -149,6 +150,16 @@ static const struct Language_Info languages[language_count] = .standard_count = sizeof c_standards / sizeof c_standards[0], .default_standard = "c17", }, + [language_nasm] = + { + .key = "nasm", + .name = "NASM", + .compiler = "nasm", + .extension = "asm", + .standards = NULL, + .standard_count = 0, + .default_standard = NULL, + }, }; static const struct Build_Info build_systems[build_system_count] = @@ -216,7 +227,7 @@ static void print_usage(FILE* stream) " %s [-v | -h]\n" "\n" "options:\n" - " -l, --lang LANG language: c, cpp, objc (default c)\n" + " -l, --lang LANG language: c, cpp, objc, nasm (default c)\n" " -s, --std STD language standard (default per language)\n" " -b, --build SYSTEM build system: make, cmake, meson, premake, xmake (default make)\n" " -k, --kind KIND exe or lib (default exe)\n" @@ -519,7 +530,17 @@ static int parse_args(int argc, char** argv, struct Config* config) return 1; } - if (std_arg != NULL) + if (config->language == language_nasm) + { + if (std_arg != NULL) + { + error("nasm has no language standard", std_arg); + return 1; + } + + config->standard = NULL; + } + else if (std_arg != NULL) { if (!valid_standard(config->language, std_arg)) { @@ -563,6 +584,18 @@ static int parse_args(int argc, char** argv, struct Config* config) return 1; } + if (config->language == language_nasm && config->build != build_make) + { + error("nasm projects only support make", build_systems[config->build].key); + return 1; + } + + if (config->language == language_nasm && config->kind == kind_lib) + { + error("nasm projects only support executables", NULL); + return 1; + } + config->bin_dir = bin_arg != NULL ? bin_arg : build_systems[config->build].default_bin; config->bin_int_dir = bin_int_arg != NULL ? bin_int_arg : build_systems[config->build].default_bin_int; @@ -616,6 +649,27 @@ static int git_init(const char* root) static const char* main_source(const struct Config* config) { + if (config->language == language_nasm) + return + " default rel\n" + "\n" + " global _main\n" + " extern _printf\n" + "\n" + " section .text\n" + "_main:\n" + " push rbp\n" + " mov rbp, rsp\n" + " lea rdi, [msg]\n" + " xor eax, eax\n" + " call _printf\n" + " xor eax, eax\n" + " pop rbp\n" + " ret\n" + "\n" + " section .data\n" + "msg: db \"type shi\", 10, 0\n"; + if (config->language == language_objc) return "#import <Foundation/Foundation.h>\n" @@ -759,8 +813,76 @@ static const char* cmake_language(enum Language language) return "C"; } +/* nasm is its own thing: assemble each .asm with nasm, then link with cc. no + * -std, no warning flags, no sanitizers. macho64 is the macOS object format. */ +static void write_makefile_nasm(FILE* out, const struct Config* config) +{ + const char* obj_dir = config->use_intermediate ? config->bin_int_dir : config->bin_dir; + + char clean[2 * PATH_MAX + 16]; + + if (config->use_intermediate) + (void) snprintf(clean, sizeof clean, "rm -rf %s %s", config->bin_dir, config->bin_int_dir); + else + (void) snprintf(clean, sizeof clean, "rm -rf %s", config->bin_dir); + + (void) fprintf( + out, + "AS := nasm\n" + "LD := cc\n" + "\n" + "DEBUG ?= 1\n" + "\n" + "ASFLAGS := -f macho64\n" + "LDFLAGS := -arch x86_64\n" + "\n" + "ifeq ($(DEBUG),1)\n" + "ASFLAGS += -g -F dwarf\n" + "endif\n" + "\n" + "TARGET := %s/%s\n", + config->bin_dir, config->name); + + if (config->use_flat) + (void) fprintf(out, "SRC := $(wildcard *.asm)\nOBJ := $(SRC:%%=%s/%%.o)\n", obj_dir); + else + (void) fprintf(out, "SRC := $(shell find src -name '*.asm')\nOBJ := $(SRC:src/%%=%s/%%.o)\n", obj_dir); + + (void) fprintf( + out, + "\n" + "all: $(TARGET)\n" + "\n" + "$(TARGET): $(OBJ)\n" + "\t@mkdir -p $(@D)\n" + "\t$(LD) $^ -o $@ $(LDFLAGS)\n" + "\n"); + + if (config->use_flat) + (void) fprintf(out, "%s/%%.asm.o: %%.asm\n", obj_dir); + else + (void) fprintf(out, "%s/%%.asm.o: src/%%.asm\n", obj_dir); + + (void) fprintf( + out, + "\t@mkdir -p $(@D)\n" + "\t$(AS) $(ASFLAGS) $< -o $@\n" + "\n" + "clean:\n" + "\t%s\n" + "\n" + ".PHONY: all clean\n", + clean); +} + static void write_makefile(FILE* out, const struct Config* config) { + if (config->language == language_nasm) + { + write_makefile_nasm(out, config); + return; + } + const struct Language_Info* lang = &languages[config->language]; const char* ext = lang->extension; const char* obj_dir = config->use_intermediate ? config->bin_int_dir : config->bin_dir; @@ -1272,7 +1394,7 @@ static void write_gitignore(FILE* out, const struct Config* config) if (config->build == build_xmake) (void) fputs(".xmake/\n", out); - if (config->use_clangd) + if (config->use_clangd && config->language != language_nasm) (void) fputs("\n" ".clangd\n" "compile_commands.json\n", @@ -1316,11 +1438,17 @@ static int make_dir(const char* path) static void print_next_steps(const struct Config* config) { - (void) printf("\ncreated %s (%s, %s, %s)\n\n", - config->name, - languages[config->language].name, - config->standard, - build_systems[config->build].name); + if (config->standard != NULL) + (void) printf("\ncreated %s (%s, %s, %s)\n\n", + config->name, + languages[config->language].name, + config->standard, + build_systems[config->build].name); + else + (void) printf("\ncreated %s (%s, %s)\n\n", + config->name, + languages[config->language].name, + build_systems[config->build].name); (void) printf(" cd %s\n", config->name); @@ -1328,7 +1456,8 @@ static void print_next_steps(const struct Config* config) { case build_make: (void) printf(" make\n"); - (void) printf(" make check\n"); + if (config->language != language_nasm) + (void) printf(" make check\n"); break; case build_cmake: @@ -1423,7 +1552,7 @@ static int generate_project(const struct Config* config) if (emit(path, config, write_gitignore) != 0) return -1; - if (config->use_clangd) + if (config->use_clangd && config->language != language_nasm) { (void) snprintf(path, sizeof path, "%s/compile_flags.txt", config->name); @@ -123,6 +123,12 @@ for bs in make cmake meson premake xmake; do check_lib "$bs" "t_${bs}_lib" done +if [ "$(uname)" = Darwin ] && have nasm; then + check "nasm / make" make t_nasm -l nasm +else + skip "nasm (needs macOS + nasm)" +fi + echo echo "generated files:" ( cd "$work" && "$mkproj" --no-git clangd_on ) >/dev/null 2>&1 |
