feat(build): add build instructions in README and convert sources to .c.md format

- Add detailed build and bootstrap instructions to README.md.
 - Convert all source and header files from .c/.h to .c.md/.h.md.
 - Add bootstrap.sh script for automated building across version history.
 - Update Makefile and sources.mk to reflect new markdown-based source organization.
This commit is contained in:
lohhiiccc 2026-01-12 14:51:34 +01:00
parent 2cf16ff109
commit b1bcdd7586
28 changed files with 236 additions and 31 deletions

View file

@ -1,32 +1,48 @@
NAME = c-md NAME = c-md
VERSION = 1.0.0 VERSION = 2.0.0
TEST_BIN= c-md.test
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
TEST_BIN= c-md.test
CMD = ./$(NAME)
MAKEFLAGS += --no-print-directory MAKEFLAGS += --no-print-directory
include sources.mk include sources.mk
CC = clang CC = clang
CPPFLAGS = -std=c99 -I includes CPPFLAGS = -std=c99
CFLAGS = -Wall -Wextra -Werror -pipe CFLAGS = -Wall -Wextra -Werror -pipe
LDFLAGS = LDFLAGS =
TEST_LDFLAGS = -lcriterion 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) DEPS = $(OBJS:.o=.d)
.SECONDARY: $(GEN_SRCS) $(GEN_HDRS)
.PHONY: all .PHONY: all
all: $(NAME) all: $(NAME)
$(NAME): $(OBJS) $(NAME): $(GEN_HDRS) $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(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 $@) @mkdir -p $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@ $(CC) $(CPPFLAGS) $(CFLAGS) -I $(BUILD_DIR)/$(INC_DIR) -MMD -MP -c $< -o $@
-include $(DEPS) -include $(DEPS)
@ -37,13 +53,12 @@ test: $(TEST_BIN)
$(TEST_BIN): $(TESTS) $(filter-out $(OBJ_DIR)/main.o,$(OBJS)) $(TEST_BIN): $(TESTS) $(filter-out $(OBJ_DIR)/main.o,$(OBJS))
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) $(TEST_LDFLAGS) $^ -o $@ $(CC) $(CPPFLAGS) $(CFLAGS) -I $(BUILD_DIR)/$(INC_DIR) $(TEST_LDFLAGS) $^ -o $@
.PHONY: clean .PHONY: clean
clean: clean:
$(RM) -r $(OBJ_DIR) $(RM) -r $(BUILD_DIR)
.PHONY: fclean .PHONY: fclean
fclean: clean fclean: clean
@ -53,4 +68,3 @@ fclean: clean
.PHONY: re .PHONY: re
re: fclean re: fclean
$(MAKE) all $(MAKE) all

View file

@ -106,6 +106,35 @@ For file `main.c.md` with `-e c`:
Multiple matching code blocks are concatenated with a blank line separator. 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 ## Line Mapping
The transpiler generates `.map` files that track the correspondence between 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 ## Status
**v1.0.0** - Initial stable release **v2.0.0** - Initial stable release self-hosted.
## License ## License

103
bootstrap.sh Executable file
View file

@ -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."

View file

@ -1,3 +1,4 @@
```c
#ifndef CLI_H #ifndef CLI_H
# define CLI_H # define CLI_H
@ -19,3 +20,4 @@ void
cli_print_help(const char *progname); cli_print_help(const char *progname);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef MAP_INTERNAL_H #ifndef MAP_INTERNAL_H
# define MAP_INTERNAL_H # define MAP_INTERNAL_H
@ -17,3 +18,4 @@ int8_t
write_ranges(FILE *f, t_map *map); write_ranges(FILE *f, t_map *map);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef TRANSPILE_INTERNAL_H #ifndef TRANSPILE_INTERNAL_H
# define TRANSPILE_INTERNAL_H # define TRANSPILE_INTERNAL_H
@ -35,3 +36,4 @@ int8_t
handle_code_line(t_state *s, char *line); handle_code_line(t_state *s, char *line);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef IO_H #ifndef IO_H
# define IO_H # define IO_H
@ -17,3 +18,4 @@ void
io_close(t_io *io); io_close(t_io *io);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef MAP_H #ifndef MAP_H
# define MAP_H # define MAP_H
@ -33,3 +34,4 @@ void
map_free(t_map *map); map_free(t_map *map);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef TRANSPILE_H #ifndef TRANSPILE_H
# define TRANSPILE_H # define TRANSPILE_H
@ -9,3 +10,4 @@ int8_t
transpile(FILE *in, FILE *out, const char *ext, t_map *map); transpile(FILE *in, FILE *out, const char *ext, t_map *map);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef UTILS_H #ifndef UTILS_H
# define UTILS_H # define UTILS_H
@ -20,3 +21,4 @@ const char *
infer_ext_from_filename(const char *path); infer_ext_from_filename(const char *path);
#endif #endif
```

View file

@ -1,3 +1,4 @@
```c
#ifndef VALIDATOR_H #ifndef VALIDATOR_H
# define VALIDATOR_H # define VALIDATOR_H
@ -8,3 +9,4 @@ int8_t
validator_validate_args(t_args *args); validator_validate_args(t_args *args);
#endif #endif
```

View file

@ -1,21 +1,31 @@
SRC_DIR = srcs SRC_DIR = srcs
SRCS = $(SRC_DIR)/main.c \ MD_SRCS = $(SRC_DIR)/main.c.md \
$(SRC_DIR)/io/streams.c \ $(SRC_DIR)/io/streams.c.md \
$(SRC_DIR)/utils/io/read_line.c \ $(SRC_DIR)/utils/io/read_line.c.md \
$(SRC_DIR)/utils/string/starts_with.c \ $(SRC_DIR)/utils/string/starts_with.c.md \
$(SRC_DIR)/utils/string/extract_fence_ext.c \ $(SRC_DIR)/utils/string/extract_fence_ext.c.md \
$(SRC_DIR)/utils/string/extract_file_ext.c \ $(SRC_DIR)/utils/string/extract_file_ext.c.md \
$(SRC_DIR)/utils/string/infer_ext_from_filename.c \ $(SRC_DIR)/utils/string/infer_ext_from_filename.c.md \
$(SRC_DIR)/transpile/state.c \ $(SRC_DIR)/transpile/state.c.md \
$(SRC_DIR)/transpile/fence.c \ $(SRC_DIR)/transpile/fence.c.md \
$(SRC_DIR)/transpile/code.c \ $(SRC_DIR)/transpile/code.c.md \
$(SRC_DIR)/transpile/core.c \ $(SRC_DIR)/transpile/core.c.md \
$(SRC_DIR)/map/core.c \ $(SRC_DIR)/map/core.c.md \
$(SRC_DIR)/map/io.c \ $(SRC_DIR)/map/io.c.md \
$(SRC_DIR)/cli/cli.c \ $(SRC_DIR)/cli/cli.c.md \
$(SRC_DIR)/cli/help.c \ $(SRC_DIR)/cli/help.c.md \
$(SRC_DIR)/validator/validator.c $(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_DIR = tests
TESTS = $(TESTS_DIR)/test_integration.c \ TESTS = $(TESTS_DIR)/test_integration.c \

View file

@ -1,3 +1,4 @@
```c
#include <stddef.h> #include <stddef.h>
#include <getopt.h> #include <getopt.h>
#include <string.h> #include <string.h>
@ -110,3 +111,4 @@ handle_map(t_args *args, const char *value)
{ {
args->map_path = value; args->map_path = value;
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdio.h> #include <stdio.h>
#include "cli.h" #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 -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); fprintf(stderr, " %s -e h -i file.md -o file.h -m file.map\n", progname);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdio.h> #include <stdio.h>
#include "io.h" #include "io.h"
@ -30,3 +31,4 @@ io_close(t_io *io)
if (NULL != io->out && io->out != stdout) if (NULL != io->out && io->out != stdout)
fclose(io->out); fclose(io->out);
} }
```

View file

@ -1,4 +1,6 @@
```c
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include "cli.h" #include "cli.h"
#include "validator.h" #include "validator.h"
@ -65,3 +67,4 @@ run(t_args *args)
io_close(&io); io_close(&io);
return (ret); return (ret);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdlib.h> #include <stdlib.h>
#include "map.h" #include "map.h"
@ -53,3 +54,4 @@ map_grow(t_map *map)
map->capacity = new_cap; map->capacity = new_cap;
return (0); return (0);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdio.h> #include <stdio.h>
#include "map.h" #include "map.h"
@ -53,3 +54,4 @@ map_write(t_map *map, const char *path, const char *source, const char *target)
fclose(f); fclose(f);
return (ret); return (ret);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdio.h> #include <stdio.h>
#include "internal/transpile_internal.h" #include "internal/transpile_internal.h"
@ -10,3 +11,4 @@ handle_code_line(t_state *s, char *line)
s->dst_line++; s->dst_line++;
return (0); return (0);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdlib.h> #include <stdlib.h>
#include "transpile.h" #include "transpile.h"
@ -36,3 +37,4 @@ transpile(FILE *in, FILE *out, const char *ext, t_map *map)
free(line); free(line);
return (ret); return (ret);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -48,3 +49,4 @@ handle_fence_close(t_state *s)
} }
return (0); return (0);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include "internal/transpile_internal.h" #include "internal/transpile_internal.h"
void 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->in_block = 0;
s->first_block = 1; s->first_block = 1;
} }
```

View file

@ -1,3 +1,4 @@
```c
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <stdlib.h> #include <stdlib.h>
@ -22,3 +23,4 @@ read_line(FILE *f)
} }
return (line); return (line);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
@ -30,3 +31,4 @@ extract_fence_ext(const char *fence)
ext[len] = '\0'; ext[len] = '\0';
return (ext); return (ext);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <string.h> #include <string.h>
#include "utils.h" #include "utils.h"
@ -12,3 +13,4 @@ extract_file_ext(const char *path)
return (NULL); return (NULL);
return (dot + 1); return (dot + 1);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <string.h> #include <string.h>
#include "utils.h" #include "utils.h"
@ -35,3 +36,4 @@ infer_ext_from_filename(const char *path)
ext_buffer[ext_len] = '\0'; ext_buffer[ext_len] = '\0';
return (ext_buffer); return (ext_buffer);
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
@ -11,3 +12,4 @@ starts_with(const char *str, const char *prefix)
prefix_len = strlen(prefix); prefix_len = strlen(prefix);
return (0 == strncmp(str, prefix, prefix_len)); return (0 == strncmp(str, prefix, prefix_len));
} }
```

View file

@ -1,3 +1,4 @@
```c
#include <stddef.h> #include <stddef.h>
#include "validator.h" #include "validator.h"
@ -20,3 +21,4 @@ validator_validate_args(t_args *args)
} }
return (0); return (0);
} }
```