hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-18 18:40:37 +0200
committerhachem <im@hachem.wtf>2026-09-18 18:40:37 +0200
commit2e9e1221538fe8148e542103552ff339891dd21b (patch)
tree7bb2a930f7df4c59cf6a17d0988b162eb3e213a3
parent875e2a75eae13298a36a9f631305a68e40653606 (diff)
feat: add nasm projects
-rw-r--r--mkproj.16
-rw-r--r--mkproj.c149
-rwxr-xr-xtests.sh6
3 files changed, 150 insertions, 11 deletions
diff --git a/mkproj.1 b/mkproj.1
index 1f716d1..b416bcc 100644
--- a/mkproj.1
+++ b/mkproj.1
@@ -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
diff --git a/mkproj.c b/mkproj.c
index 6e790f4..7fa6a54 100644
--- a/mkproj.c
+++ b/mkproj.c
@@ -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);
diff --git a/tests.sh b/tests.sh
index f860ae7..98b30fb 100755
--- a/tests.sh
+++ b/tests.sh
@@ -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