diff --git a/Makefile b/Makefile index e83136d..37cfb6b 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,48 @@ NAME = c-md -VERSION = 1.0.0 -TEST_BIN= c-md.test - +VERSION = 2.0.0 .DEFAULT_GOAL := all + +TEST_BIN= c-md.test +CMD = ./$(NAME) + MAKEFLAGS += --no-print-directory include sources.mk CC = clang -CPPFLAGS = -std=c99 -I includes +CPPFLAGS = -std=c99 CFLAGS = -Wall -Wextra -Werror -pipe LDFLAGS = TEST_LDFLAGS = -lcriterion +BUILD_DIR = .build +OBJ_DIR = $(BUILD_DIR)/objs +MAP_DIR = $(BUILD_DIR)/maps -OBJ_DIR = .build +GEN_SRCS = $(MD_SRCS:$(SRC_DIR)/%.c.md=$(BUILD_DIR)/srcs/%.c) +GEN_HDRS = $(MD_HDRS:$(INC_DIR)/%.h.md=$(BUILD_DIR)/$(INC_DIR)/%.h) -OBJS = $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) +OBJS = $(GEN_SRCS:$(BUILD_DIR)/srcs/%.c=$(OBJ_DIR)/%.o) DEPS = $(OBJS:.o=.d) +.SECONDARY: $(GEN_SRCS) $(GEN_HDRS) + .PHONY: all all: $(NAME) -$(NAME): $(OBJS) - $(CC) $(LDFLAGS) -o $@ $^ +$(NAME): $(GEN_HDRS) $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) -$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c +$(BUILD_DIR)/srcs/%.c: $(SRC_DIR)/%.c.md + @mkdir -p $(dir $@) $(dir $(MAP_DIR)/$*.map) + $(CMD) -i $< -o $@ -m $(MAP_DIR)/$*.map + +$(BUILD_DIR)/$(INC_DIR)/%.h: $(INC_DIR)/%.h.md + @mkdir -p $(dir $@) $(dir $(MAP_DIR)/$*.h.map) + $(CMD) -e c -i $< -o $@ -m $(MAP_DIR)/$*.h.map + +$(OBJ_DIR)/%.o: $(BUILD_DIR)/srcs/%.c $(GEN_HDRS) @mkdir -p $(dir $@) - $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@ + $(CC) $(CPPFLAGS) $(CFLAGS) -I $(BUILD_DIR)/$(INC_DIR) -MMD -MP -c $< -o $@ -include $(DEPS) @@ -37,13 +53,12 @@ test: $(TEST_BIN) $(TEST_BIN): $(TESTS) $(filter-out $(OBJ_DIR)/main.o,$(OBJS)) @mkdir -p $(dir $@) - $(CC) $(CPPFLAGS) $(CFLAGS) $(TEST_LDFLAGS) $^ -o $@ - + $(CC) $(CPPFLAGS) $(CFLAGS) -I $(BUILD_DIR)/$(INC_DIR) $(TEST_LDFLAGS) $^ -o $@ .PHONY: clean clean: - $(RM) -r $(OBJ_DIR) + $(RM) -r $(BUILD_DIR) .PHONY: fclean fclean: clean @@ -53,4 +68,3 @@ fclean: clean .PHONY: re re: fclean $(MAKE) all - diff --git a/README.md b/README.md index 9b83f58..0a4a8ef 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,35 @@ For file `main.c.md` with `-e c`: Multiple matching code blocks are concatenated with a blank line separator. +## Building + +c-md is self-hosted +(dogfooding)[https://en.wikipedia.org/wiki/Self-hosting_(compilers)]. The +source code is written in `.c.md` format and requires c-md itself to build. + +### First Build (Bootstrap) + +After cloning or after `make fclean`, run the bootstrap script: + +```bash +./bootstrap.sh +make +``` + +The bootstrap script builds c-md by traversing major version tags, +starting from v1.0.0 (plain C sources) up to the current version. + +### Regular Build + +Once c-md is available: + +```bash +make # build from .c.md sources +make clean # remove build artifacts +make fclean # remove everything including binary +make re # rebuild from scratch +``` + ## Line Mapping The transpiler generates `.map` files that track the correspondence between @@ -117,7 +146,7 @@ source lines (`.c.md`) and output lines (`.c`). This enables: ## Status -**v1.0.0** - Initial stable release +**v2.0.0** - Initial stable release self-hosted. ## License diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..2b23a5e --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env sh +# bootstrap.sh - Build c-md by traversing major version tags +# +# This script builds c-md from scratch by going through each major version +# tag in order. Each version is built using the binary from the previous +# version, starting from v1.0.0 which uses plain C sources. +# +# Use this script after a fresh clone or after 'make fclean'. + +set -e + +TMP_BIN="/tmp/c-md-bootstrap-$$" +CURRENT="" +STASH_NEEDED=0 + +cleanup() { + # Restore original branch if we switched + if [ -n "$CURRENT" ]; then + CURRENT_BRANCH=$(git branch --show-current) + if [ "$CURRENT_BRANCH" != "$CURRENT" ]; then + echo "Restoring original branch..." + git switch "$CURRENT" --quiet 2>/dev/null || git checkout "$CURRENT" --quiet 2>/dev/null || true + fi + fi + + # Restore stashed changes + if [ "$STASH_NEEDED" -eq 1 ]; then + echo "Restoring stashed changes..." + git stash pop --quiet 2>/dev/null || echo "Warning: Could not restore stashed changes" + fi + + rm -f "$TMP_BIN" +} + +trap cleanup EXIT INT TERM + +get_highest_major_tags() { + git tag -l 'v*' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ + | sort -V \ + | awk -F. ' + { + m=substr($1, 2)+0; + tag = $0 + tags[m] = tag # Overwrite; since input is sorted, last wins (highest) + } END { + for (m in tags) print tags[m] + }' \ + | sort -V +} + +echo "Bootstrapping c-md..." + +CURRENT=$(git branch --show-current) +if [ -n "$(git status --porcelain)" ]; then + echo "Stashing local changes..." + git stash --include-untracked --quiet + STASH_NEEDED=1 +fi + +TAGS=$(get_highest_major_tags) + +if [ -z "$TAGS" ]; then + echo "No version tags found. Building current version from C sources..." + make --quiet + echo "Bootstrap complete. Binary ready at ./c-md" + exit 0 +fi + +echo "Found major versions: $(echo $TAGS | tr '\n' ' ')" + +for TAG in $TAGS; do + echo "Building $TAG..." + git switch --detach "$TAG" --quiet + + if [ -f "$TMP_BIN" ]; then + cp "$TMP_BIN" ./c-md + fi + + make --quiet + cp c-md "$TMP_BIN" + make fclean --quiet +done + +echo "Restoring current branch..." +git switch "$CURRENT" --quiet + +if [ "$STASH_NEEDED" -eq 1 ]; then + echo "Restoring stashed changes..." + git stash pop --quiet + STASH_NEEDED=0 +fi + +echo "Building current version..." +cp "$TMP_BIN" ./c-md +make --quiet +cp c-md "$TMP_BIN" +make fclean --quiet +mv "$TMP_BIN" ./c-md + +echo "" +echo "Bootstrap complete. Binary ready at ./c-md" +echo "Run 'make' to rebuild from sources." diff --git a/includes/cli.h b/includes/cli.h.md similarity index 97% rename from includes/cli.h rename to includes/cli.h.md index e2d6d44..edbff2a 100644 --- a/includes/cli.h +++ b/includes/cli.h.md @@ -1,3 +1,4 @@ +```c #ifndef CLI_H # define CLI_H @@ -19,3 +20,4 @@ void cli_print_help(const char *progname); #endif +``` diff --git a/includes/internal/map_internal.h b/includes/internal/map_internal.h.md similarity index 96% rename from includes/internal/map_internal.h rename to includes/internal/map_internal.h.md index 3d5729b..8735b0d 100644 --- a/includes/internal/map_internal.h +++ b/includes/internal/map_internal.h.md @@ -1,3 +1,4 @@ +```c #ifndef MAP_INTERNAL_H # define MAP_INTERNAL_H @@ -17,3 +18,4 @@ int8_t write_ranges(FILE *f, t_map *map); #endif +``` diff --git a/includes/internal/transpile_internal.h b/includes/internal/transpile_internal.h.md similarity index 98% rename from includes/internal/transpile_internal.h rename to includes/internal/transpile_internal.h.md index 5c95eee..4bb63a1 100644 --- a/includes/internal/transpile_internal.h +++ b/includes/internal/transpile_internal.h.md @@ -1,3 +1,4 @@ +```c #ifndef TRANSPILE_INTERNAL_H # define TRANSPILE_INTERNAL_H @@ -35,3 +36,4 @@ int8_t handle_code_line(t_state *s, char *line); #endif +``` diff --git a/includes/io.h b/includes/io.h.md similarity index 96% rename from includes/io.h rename to includes/io.h.md index fc26a88..a5d18a4 100644 --- a/includes/io.h +++ b/includes/io.h.md @@ -1,3 +1,4 @@ +```c #ifndef IO_H # define IO_H @@ -17,3 +18,4 @@ void io_close(t_io *io); #endif +``` diff --git a/includes/map.h b/includes/map.h.md similarity index 98% rename from includes/map.h rename to includes/map.h.md index a21a0af..5576490 100644 --- a/includes/map.h +++ b/includes/map.h.md @@ -1,3 +1,4 @@ +```c #ifndef MAP_H # define MAP_H @@ -33,3 +34,4 @@ void map_free(t_map *map); #endif +``` diff --git a/includes/transpile.h b/includes/transpile.h.md similarity index 95% rename from includes/transpile.h rename to includes/transpile.h.md index 0147f8f..f26b11b 100644 --- a/includes/transpile.h +++ b/includes/transpile.h.md @@ -1,3 +1,4 @@ +```c #ifndef TRANSPILE_H # define TRANSPILE_H @@ -9,3 +10,4 @@ int8_t transpile(FILE *in, FILE *out, const char *ext, t_map *map); #endif +``` diff --git a/includes/utils.h b/includes/utils.h.md similarity index 97% rename from includes/utils.h rename to includes/utils.h.md index 1cf3141..533220b 100644 --- a/includes/utils.h +++ b/includes/utils.h.md @@ -1,3 +1,4 @@ +```c #ifndef UTILS_H # define UTILS_H @@ -20,3 +21,4 @@ const char * infer_ext_from_filename(const char *path); #endif +``` diff --git a/includes/validator.h b/includes/validator.h.md similarity index 93% rename from includes/validator.h rename to includes/validator.h.md index 03178a6..e18757a 100644 --- a/includes/validator.h +++ b/includes/validator.h.md @@ -1,3 +1,4 @@ +```c #ifndef VALIDATOR_H # define VALIDATOR_H @@ -8,3 +9,4 @@ int8_t validator_validate_args(t_args *args); #endif +``` diff --git a/sources.mk b/sources.mk index d1d5e45..113cdf1 100644 --- a/sources.mk +++ b/sources.mk @@ -1,21 +1,31 @@ SRC_DIR = srcs -SRCS = $(SRC_DIR)/main.c \ - $(SRC_DIR)/io/streams.c \ - $(SRC_DIR)/utils/io/read_line.c \ - $(SRC_DIR)/utils/string/starts_with.c \ - $(SRC_DIR)/utils/string/extract_fence_ext.c \ - $(SRC_DIR)/utils/string/extract_file_ext.c \ - $(SRC_DIR)/utils/string/infer_ext_from_filename.c \ - $(SRC_DIR)/transpile/state.c \ - $(SRC_DIR)/transpile/fence.c \ - $(SRC_DIR)/transpile/code.c \ - $(SRC_DIR)/transpile/core.c \ - $(SRC_DIR)/map/core.c \ - $(SRC_DIR)/map/io.c \ - $(SRC_DIR)/cli/cli.c \ - $(SRC_DIR)/cli/help.c \ - $(SRC_DIR)/validator/validator.c +MD_SRCS = $(SRC_DIR)/main.c.md \ + $(SRC_DIR)/io/streams.c.md \ + $(SRC_DIR)/utils/io/read_line.c.md \ + $(SRC_DIR)/utils/string/starts_with.c.md \ + $(SRC_DIR)/utils/string/extract_fence_ext.c.md \ + $(SRC_DIR)/utils/string/extract_file_ext.c.md \ + $(SRC_DIR)/utils/string/infer_ext_from_filename.c.md \ + $(SRC_DIR)/transpile/state.c.md \ + $(SRC_DIR)/transpile/fence.c.md \ + $(SRC_DIR)/transpile/code.c.md \ + $(SRC_DIR)/transpile/core.c.md \ + $(SRC_DIR)/map/core.c.md \ + $(SRC_DIR)/map/io.c.md \ + $(SRC_DIR)/cli/cli.c.md \ + $(SRC_DIR)/cli/help.c.md \ + $(SRC_DIR)/validator/validator.c.md + +INC_DIR = includes +MD_HDRS = $(INC_DIR)/cli.h.md \ + $(INC_DIR)/io.h.md \ + $(INC_DIR)/map.h.md \ + $(INC_DIR)/transpile.h.md \ + $(INC_DIR)/utils.h.md \ + $(INC_DIR)/validator.h.md \ + $(INC_DIR)/internal/map_internal.h.md \ + $(INC_DIR)/internal/transpile_internal.h.md TESTS_DIR = tests TESTS = $(TESTS_DIR)/test_integration.c \ diff --git a/srcs/cli/cli.c b/srcs/cli/cli.c.md similarity index 99% rename from srcs/cli/cli.c rename to srcs/cli/cli.c.md index d5fecd5..54f9dc5 100644 --- a/srcs/cli/cli.c +++ b/srcs/cli/cli.c.md @@ -1,3 +1,4 @@ +```c #include #include #include @@ -110,3 +111,4 @@ handle_map(t_args *args, const char *value) { args->map_path = value; } +``` diff --git a/srcs/cli/help.c b/srcs/cli/help.c.md similarity index 99% rename from srcs/cli/help.c rename to srcs/cli/help.c.md index 3eb251d..6cd6029 100644 --- a/srcs/cli/help.c +++ b/srcs/cli/help.c.md @@ -1,3 +1,4 @@ +```c #include #include "cli.h" @@ -19,3 +20,4 @@ cli_print_help(const char *progname) fprintf(stderr, " %s -i main.c.md -o main.c\n", progname); fprintf(stderr, " %s -e h -i file.md -o file.h -m file.map\n", progname); } +``` diff --git a/srcs/io/streams.c b/srcs/io/streams.c.md similarity index 98% rename from srcs/io/streams.c rename to srcs/io/streams.c.md index 9dec915..bf20c01 100644 --- a/srcs/io/streams.c +++ b/srcs/io/streams.c.md @@ -1,3 +1,4 @@ +```c #include #include "io.h" @@ -30,3 +31,4 @@ io_close(t_io *io) if (NULL != io->out && io->out != stdout) fclose(io->out); } +``` diff --git a/srcs/main.c b/srcs/main.c.md similarity index 97% rename from srcs/main.c rename to srcs/main.c.md index 4bf24d2..e4eb64f 100644 --- a/srcs/main.c +++ b/srcs/main.c.md @@ -1,4 +1,6 @@ +```c #include +#include #include "cli.h" #include "validator.h" @@ -65,3 +67,4 @@ run(t_args *args) io_close(&io); return (ret); } +``` diff --git a/srcs/map/core.c b/srcs/map/core.c.md similarity index 99% rename from srcs/map/core.c rename to srcs/map/core.c.md index d6b5206..58e2f31 100644 --- a/srcs/map/core.c +++ b/srcs/map/core.c.md @@ -1,3 +1,4 @@ +```c #include #include "map.h" @@ -53,3 +54,4 @@ map_grow(t_map *map) map->capacity = new_cap; return (0); } +``` diff --git a/srcs/map/io.c b/srcs/map/io.c.md similarity index 99% rename from srcs/map/io.c rename to srcs/map/io.c.md index 1732722..984f40e 100644 --- a/srcs/map/io.c +++ b/srcs/map/io.c.md @@ -1,3 +1,4 @@ +```c #include #include "map.h" @@ -53,3 +54,4 @@ map_write(t_map *map, const char *path, const char *source, const char *target) fclose(f); return (ret); } +``` diff --git a/srcs/transpile/code.c b/srcs/transpile/code.c.md similarity index 95% rename from srcs/transpile/code.c rename to srcs/transpile/code.c.md index c0d711b..e4d50f6 100644 --- a/srcs/transpile/code.c +++ b/srcs/transpile/code.c.md @@ -1,3 +1,4 @@ +```c #include #include "internal/transpile_internal.h" @@ -10,3 +11,4 @@ handle_code_line(t_state *s, char *line) s->dst_line++; return (0); } +``` diff --git a/srcs/transpile/core.c b/srcs/transpile/core.c.md similarity index 98% rename from srcs/transpile/core.c rename to srcs/transpile/core.c.md index 6dd3e54..99acea3 100644 --- a/srcs/transpile/core.c +++ b/srcs/transpile/core.c.md @@ -1,3 +1,4 @@ +```c #include #include "transpile.h" @@ -36,3 +37,4 @@ transpile(FILE *in, FILE *out, const char *ext, t_map *map) free(line); return (ret); } +``` diff --git a/srcs/transpile/fence.c b/srcs/transpile/fence.c.md similarity index 98% rename from srcs/transpile/fence.c rename to srcs/transpile/fence.c.md index b02722c..c7d42f6 100644 --- a/srcs/transpile/fence.c +++ b/srcs/transpile/fence.c.md @@ -1,3 +1,4 @@ +```c #include #include #include @@ -48,3 +49,4 @@ handle_fence_close(t_state *s) } return (0); } +``` diff --git a/srcs/transpile/state.c b/srcs/transpile/state.c.md similarity index 97% rename from srcs/transpile/state.c rename to srcs/transpile/state.c.md index ee45382..daaa767 100644 --- a/srcs/transpile/state.c +++ b/srcs/transpile/state.c.md @@ -1,3 +1,4 @@ +```c #include "internal/transpile_internal.h" void @@ -14,3 +15,4 @@ state_init(t_state *s, FILE *in, FILE *out, const char *ext, t_map *map) s->in_block = 0; s->first_block = 1; } +``` diff --git a/srcs/utils/io/read_line.c b/srcs/utils/io/read_line.c.md similarity index 97% rename from srcs/utils/io/read_line.c rename to srcs/utils/io/read_line.c.md index f91af4d..2e3d66e 100644 --- a/srcs/utils/io/read_line.c +++ b/srcs/utils/io/read_line.c.md @@ -1,3 +1,4 @@ +```c #define _POSIX_C_SOURCE 200809L #include @@ -22,3 +23,4 @@ read_line(FILE *f) } return (line); } +``` diff --git a/srcs/utils/string/extract_fence_ext.c b/srcs/utils/string/extract_fence_ext.c.md similarity index 98% rename from srcs/utils/string/extract_fence_ext.c rename to srcs/utils/string/extract_fence_ext.c.md index 0694dfa..072cdbc 100644 --- a/srcs/utils/string/extract_fence_ext.c +++ b/srcs/utils/string/extract_fence_ext.c.md @@ -1,3 +1,4 @@ +```c #include #include #include @@ -30,3 +31,4 @@ extract_fence_ext(const char *fence) ext[len] = '\0'; return (ext); } +``` diff --git a/srcs/utils/string/extract_file_ext.c b/srcs/utils/string/extract_file_ext.c.md similarity index 95% rename from srcs/utils/string/extract_file_ext.c rename to srcs/utils/string/extract_file_ext.c.md index 5192737..2aa7e4a 100644 --- a/srcs/utils/string/extract_file_ext.c +++ b/srcs/utils/string/extract_file_ext.c.md @@ -1,3 +1,4 @@ +```c #include #include "utils.h" @@ -12,3 +13,4 @@ extract_file_ext(const char *path) return (NULL); return (dot + 1); } +``` diff --git a/srcs/utils/string/infer_ext_from_filename.c b/srcs/utils/string/infer_ext_from_filename.c.md similarity index 98% rename from srcs/utils/string/infer_ext_from_filename.c rename to srcs/utils/string/infer_ext_from_filename.c.md index d23434a..f002a47 100644 --- a/srcs/utils/string/infer_ext_from_filename.c +++ b/srcs/utils/string/infer_ext_from_filename.c.md @@ -1,3 +1,4 @@ +```c #include #include "utils.h" @@ -35,3 +36,4 @@ infer_ext_from_filename(const char *path) ext_buffer[ext_len] = '\0'; return (ext_buffer); } +``` diff --git a/srcs/utils/string/starts_with.c b/srcs/utils/string/starts_with.c.md similarity index 96% rename from srcs/utils/string/starts_with.c rename to srcs/utils/string/starts_with.c.md index ec2ede9..0085224 100644 --- a/srcs/utils/string/starts_with.c +++ b/srcs/utils/string/starts_with.c.md @@ -1,3 +1,4 @@ +```c #include #include @@ -11,3 +12,4 @@ starts_with(const char *str, const char *prefix) prefix_len = strlen(prefix); return (0 == strncmp(str, prefix, prefix_len)); } +``` diff --git a/srcs/validator/validator.c b/srcs/validator/validator.c.md similarity index 97% rename from srcs/validator/validator.c rename to srcs/validator/validator.c.md index cf9734e..5d4746b 100644 --- a/srcs/validator/validator.c +++ b/srcs/validator/validator.c.md @@ -1,3 +1,4 @@ +```c #include #include "validator.h" @@ -20,3 +21,4 @@ validator_validate_args(t_args *args) } return (0); } +```