Skip to content

docs: add script and make target to auto-generate project structure - #26

Merged
loreste merged 1 commit into
loreste:mainfrom
oshirosuna1-byte:main
Jul 29, 2026
Merged

docs: add script and make target to auto-generate project structure#26
loreste merged 1 commit into
loreste:mainfrom
oshirosuna1-byte:main

Conversation

@oshirosuna1-byte

@oshirosuna1-byte oshirosuna1-byte commented Jul 25, 2026

Copy link
Copy Markdown

Adds scripts/update_structure.sh and a make structure target to automatically scan and update docs/PROJECT_STRUCTURE.md without requiring manual maintenance.

Summary by CodeRabbit

  • Documentation

    • Added a comprehensive, auto-generated guide to the project’s repository structure.
    • Updated contribution guidance with additional navigation information.
  • New Features

    • Added a command to regenerate the project structure documentation.
    • Enhanced the build tool’s help output with available targets and key configuration paths.
    • Improved installation manifest generation and build target path handling.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds generated repository structure documentation and a script-backed make structure target. The Makefile also centralizes Cargo target discovery, revises install manifest generation, and expands help output.

Changes

Repository tooling

Layer / File(s) Summary
Repository structure generation
scripts/update_structure.sh, docs/PROJECT_STRUCTURE.md, CONTRIBUTING.md
Adds a generator using tree or git ls-files, records the generated repository listing, and adds navigation guidance.
Makefile build and install integration
Makefile
Computes TARGET_DIR, derives MAKO_BIN, revises install manifest creation, and adds the structure target plus expanded help output.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: loreste

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a script and make target to auto-generate the project structure docs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
CONTRIBUTING.md (1)

47-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the generated structure document as the source of truth.

This adds another manually maintained repository map while docs/PROJECT_STRUCTURE.md is generated. Link to that document, or explicitly keep this as a small static overview to avoid future drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CONTRIBUTING.md` at line 47, Update the repository navigation section in
CONTRIBUTING.md to link to docs/PROJECT_STRUCTURE.md as the authoritative
generated structure document, and remove or clearly limit any duplicated
manually maintained map so it does not drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/PROJECT_STRUCTURE.md`:
- Around line 1055-1113: Regenerate the checked-in project structure artifact
with make structure so the scripts/ listing includes
scripts/update_structure.sh. Commit the generated documentation output without
manually editing unrelated entries.

In `@Makefile`:
- Around line 36-41: Update the manifest-generation printf invocation in the
Makefile to pass the format string as `%s\n` rather than `\%s\n`, preserving the
existing JSON arguments and output path so each manifest line is emitted without
a leading backslash.
- Around line 34-36: The install recipe has malformed GNU Make line
continuations because whitespace follows the trailing backslashes, separating
the shell commands. In the install target around the `fi`, `VER`, `TS`, and
`printf` commands, remove all characters after each continuation backslash so
the backslash is the final character on the line and the complete manifest
command remains one shell invocation.
- Around line 10-11: Fix the TARGET_DIR assignment by separating the Make shell
function name from its command with whitespace, while preserving the existing
CARGO metadata pipeline and target fallback so MAKO_BIN resolves from the actual
target directory.

In `@scripts/update_structure.sh`:
- Around line 16-20: Make the tree and fallback branches in
scripts/update_structure.sh use the same explicit repository file-selection
policy, ensuring both produce identical file sets. Then regenerate
docs/PROJECT_STRUCTURE.md using the updated script so the checked-in structure
reflects that policy and includes scripts/update_structure.sh.
- Around line 4-25: The update_structure.sh script must fail when directory
creation, file writes, tree, or git ls-files fails instead of always reporting
success. Add strict shell options including set -euo pipefail, and preferably
generate the output in a temporary file and rename it to OUTPUT_FILE only after
all generation steps succeed.

---

Nitpick comments:
In `@CONTRIBUTING.md`:
- Line 47: Update the repository navigation section in CONTRIBUTING.md to link
to docs/PROJECT_STRUCTURE.md as the authoritative generated structure document,
and remove or clearly limit any duplicated manually maintained map so it does
not drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53c0a274-9cb1-4de2-85eb-4daa4cae137f

📥 Commits

Reviewing files that changed from the base of the PR and between dbcc18c and e2a63ab.

📒 Files selected for processing (4)
  • CONTRIBUTING.md
  • Makefile
  • docs/PROJECT_STRUCTURE.md
  • scripts/update_structure.sh

Comment thread docs/PROJECT_STRUCTURE.md
Comment on lines +1055 to +1113
scripts/adaptive-opt-cycle.sh
scripts/anneal-cycle.sh
scripts/api-backend-smoke.sh
scripts/bench-compile.py
scripts/bench-gate.sh
scripts/bench-http.sh
scripts/bench-vs-go-rust.sh
scripts/bench-vs-go.sh
scripts/bootstrap-native-toolchain.sh
scripts/ci-postgres.sh
scripts/ci-redis.sh
scripts/claims-gate.sh
scripts/cross-target-seed.sh
scripts/deploy-site.sh
scripts/file-io-regression.sh
scripts/fill-release-packaging.sh
scripts/grpc-unary-smoke.sh
scripts/h2-server-smoke.sh
scripts/h3-server-smoke.sh
scripts/http-lib-smoke.sh
scripts/http-long-run-soak.sh
scripts/http-server-smoke.sh
scripts/http_ka_client.py
scripts/https-server-smoke.sh
scripts/install-linux.sh
scripts/install-release.sh
scripts/install-smoke.sh
scripts/install.ps1
scripts/install.sh
scripts/llvm-backend-test.sh
scripts/long-run-soak.sh
scripts/memory-safety-gate.sh
scripts/native-bench-baselines.json
scripts/native-bench-gate.sh
scripts/native-compiler-test.sh
scripts/package-deb.sh
scripts/package-linux-native.sh
scripts/package-macos-notarize-notes.md
scripts/package-msi-notes.md
scripts/package-msi-seed.sh
scripts/package-notarize-seed.sh
scripts/package-release.ps1
scripts/package-release.sh
scripts/package-rpm.sh
scripts/parse_bench_ns.py
scripts/pgo-build.sh
scripts/publish-homebrew-tap-seed.sh
scripts/publish-winget-seed.sh
scripts/selfhost-gate.sh
scripts/stdlib-gate.sh
scripts/uninstall.ps1
scripts/uninstall.sh
scripts/validate-matrix.sh
scripts/verify-target-artifact.sh
scripts/wasi-ci-build.sh
scripts/wasi-verify.sh
scripts/ws_client.py
scripts/ws_ping_client.py
src/ast/mod.rs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Regenerate the checked-in structure document.

The scripts/ section omits scripts/update_structure.sh, so this generated artifact is already out of sync with the repository. Re-run make structure after the generator is tracked and commit the updated output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/PROJECT_STRUCTURE.md` around lines 1055 - 1113, Regenerate the
checked-in project structure artifact with make structure so the scripts/
listing includes scripts/update_structure.sh. Commit the generated documentation
output without manually editing unrelated entries.

Comment thread Makefile
Comment on lines +10 to 11
TARGET_DIR ?= $(shell$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])' 2>/dev/null || echo target)
MAKO_BIN := $(TARGET_DIR)/release/mako

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

expected="$(cargo metadata --format-version 1 --no-deps 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])')"
actual="$(make -pn 2>/dev/null | awk -F ' = ' '$1 == "TARGET_DIR" { print $2; exit }')"
test "$actual" = "$expected"

Repository: loreste/mako

Length of output: 872


🏁 Script executed:

#!/bin/bash
set -u

echo "== Makefile excerpt =="
sed -n '1,25p' Makefile 2>/dev/null || true

echo
echo "== make DEBUG/prerequisite info for TARGET_DIR/MAKO_BIN (no recipes) =="
make -p -n 2>/dev/null | awk '
  BEGIN { show=0 }
  /(^|\s)TARGET_DIR (?:ORIGIN\s*=|:(?:=|:=|\?=|=)) / { show=1; print; next }
  /(^|\s)MAKO_BIN (?:ORIGIN\s*=|:(?:=|:=|\?=|=)) / { show=1; print; next }
  /^Target Directory:/ { print; exit }
  show && /^[A-Za-z0-9_.%-]+=/ { print }
  show && /^/ { print }
'

echo
echo "== Python shell expression behavior =="
python3 - <<'PY'
expected="$(cargo metadata --format-version 1 --no-deps 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])')"
actual="$(make -p -n 2>/dev/null | awk -F ' = ' '$1 == "TARGET_DIR" { print $2; exit }')"
print("expected=", repr(expected))
print("actual=", repr(actual))
print("equal=", actual == expected)
try:
    print("bash_eval=", repr(eval('$(shell$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'') 2>/dev/null || echo target', {'CARGO': 'cargo'})))
except Exception as e:
    print("bash_eval_error=", repr(e))
PY

echo
echo "== local shell expansion probe without Make =="
bash -c 'CARGO=cargo; set -u; echo "$(shell$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'')"' || true

echo
echo "== local shell expansion probe with whitespace after shell =="
bash -c 'CARGO=cargo; set -u; echo "$(shell $(CARGO) metadata --format-version 1 --无-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'')"' || true

Repository: loreste/mako

Length of output: 3134


🏁 Script executed:

#!/bin/bash
set -u

echo "== Make output from current Makefile =="
make -p MAKO_BIN TARGET_DIR 2>/dev/null | awk '
  /^TARGET_DIR (ORIGIN\s*=)?[=:]/ || /^MAKO_BIN (ORIGIN\s*=)?[=:]/ || /^.PHONY/ || /^Makefile path/ || /^Make version/ { print }
'

echo
echo "== Local Make-like expansion probe =="
python3 - <<'PY'
from pathlib import Path
text = Path("Makefile").read_text()
lines = text.splitlines()
for i, line in enumerate(lines, 1):
    if "CARGO ?=" in line or "TARGET_DIR ?=" in line or "MAKO_BIN :=" in line:
        print(f"{i}: {line}")
PY

echo
echo "== Bash expansion with missing command for comparison =="
env -i TERM="${TERM:-dumb}" bash -c 'set -u; CARGO=cargo; echo "$(shell$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'') 2>/dev/null || echo target"' 2>&1 || true

echo
echo "== Bash expansion with known cargo metadata succeeds =="
env -i TERM="${TERM:-dumb}" bash -c 'set -u; CARGO=cargo; echo "$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'')"' 2>&1 || true

Repository: loreste/mako

Length of output: 1555


🏁 Script executed:

#!/bin/bash
set -u

echo "== Local Makefile expansion via GNU make print-variable =="
make -s print-% --print-targets=TARGET_DIR 2>&1 || true
make TARGET_DIR -s --no-print-directory 2>&1 || true
make -s print-MAKO_BIN 2>&1 || true

echo
echo "== Local Shell expansion for malformed $(shell$(CARGO)...) =="
bash -c '
CARGO=cargo
set -u
out="$(shell$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'' 2>/dev/null || echo target)"
printf "TARGET_DIR=<%s>:<%s>\n" "${out}"
' 2>&1 || true

Repository: loreste/mako

Length of output: 512


🏁 Script executed:

#!/bin/bash
set -u

echo "== Local Makefile expansion via GNU make print-variable with GNU make from standard environment =="
make -s print-% 2>/dev/null || true
make TARGET_DIR -s --no-print-directory 2>/dev/null || true
make -s print-MAKO_BIN 2>/dev/null || true

echo
echo "== Local Shell expansion for malformed $(shell$(CARGO)...) =="
env -i PATH="$PATH" TERM="${TERM:-dumb}" bash -c '
CARGO=cargo
set -u
out="$(shell$(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'' 2>/dev/null || echo target)"
printf "TARGET_DIR=<%s>:<%s>\n" "${out}"
' 2>&1 || true

echo
echo "== Local Shell expansion for corrected $(shell $(CARGO)...) =="
env -i PATH="$PATH" TERM="${TERM:-dumb}" bash -c '
CARGO=cargo
set -u
out="$(shell $(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c '\''import json,sys; print(json.load(sys.stdin)["target_directory"])'\'' 2>/dev/null || echo target)"
printf "TARGET_DIR=<%s>:<%s>\n" "${out}"
' 2>&1 || true

Repository: loreste/mako

Length of output: 624


🏁 Script executed:

#!/bin/bash
set -u

if ! command -v make >/dev/null 2>&1; then
  printf 'ERROR: make is not available in this environment\n'
  exit 1
fi

echo "== Makefile lines =="
sed -n '8,12p' Makefile

echo
echo "== GNU make variable expansion =="
make -s TARGET_DIR -O --no-print-directory 2>&1 || true
make -s MAKO_BIN -O --no-print-directory 2>&1 || true

echo
echo "== Makefile parser report =="
make -p TARGET_DIR MAKO_BIN --no-print-directory 2>/dev/null | awk '
  /^TARGET_DIR / || /^MAKO_BIN / || /^Makefile path/ || /^Make version/ { print }
'

Repository: loreste/mako

Length of output: 199


🌐 Web query:

GNU make Function invocation syntax $(function arguments) whitespace after function name $(shell command)

💡 Result:

In GNU make, the syntax for a function invocation is $(function arguments) or ${function arguments} [1][2]. Regarding whitespace: 1. Function Name and Arguments: The function name must be separated from the first argument by one or more spaces or tabs [1][3]. This whitespace is considered a separator and is not included as part of the first argument's value [1][2]. 2. Leading Whitespace in Arguments: Leading whitespace in the first argument is not preserved as part of the argument's text [1][3]. However, whitespace contained within subsequent arguments is generally preserved [1][4]. 3. Shell Function: The $(shell command) function follows these same rules [5]. The command string provided to the shell function is the text following the initial separator whitespace [1][5]. Because make uses whitespace to separate the function name from its arguments, extra or unexpected whitespace can sometimes lead to issues if not handled carefully [6]. For example, if you intend to pass a string that begins with spaces as the first argument, you cannot simply write it directly because the leading spaces will be stripped [1][3]. Instead, you must use techniques like defining a variable that contains the required space character (e.g., space:= $(empty) $(empty)) and using it to construct the argument [1][3].

Citations:


Fix the malformed $(shell ...) invocation.

Make function arguments must be delimited from the function name, so add whitespace after shell: $(shell $(CARGO) metadata --format-version 1 --no-deps 2>/dev/null | python3 -c ...). As written, this is not parsed as Make’s shell function and causes MAKO_BIN to expand from the fallback target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 10 - 11, Fix the TARGET_DIR assignment by separating
the Make shell function name from its command with whitespace, while preserving
the existing CARGO metadata pipeline and target fallback so MAKO_BIN resolves
from the actual target directory.

Comment thread Makefile
Comment on lines +34 to +36
rm -rf "$(SHARE_DIR)/std"; mkdir -p "$(SHARE_DIR)/std"; cp -R std/. "$(SHARE_DIR)/std/"; \ fi @VER=$$("$(BIN_DIR)/mako" version 2>/dev/null || true); \
HOST=$$(uname -s 2>/dev/null)-$$(uname -m 2>/dev/null); \
TS=$$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date); \
printf '%s\n' \
'{' \
' "schema": "mako.install.v1",' \
" \"version\": \"$$VER\"," \
TS=$$(date -u +\%Y-\%m-\%dT\%H:\%M:\%SZ 2>/dev/null \vert{}\vert{} date); \ printf '\%s\n' \ '{' \ ' "schema": "mako.install.v1",' \ " \"version\": \"$$VER\"," \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

make -n install | sed -n '/rm -rf.*std/,/install-manifest.json/p' | cat -vet

Repository: loreste/mako

Length of output: 188


🏁 Script executed:

#!/bin/bash
set -u

if ! command -v make >/dev/null 2>&1; then
  echo "make not available"
fi

sed -n '20,45p' Makefile | cat -vet
printf '\n--- parse Makefile recipe continuation check ---\n'
python3 - <<'PY'
from pathlib import Path

p = Path("Makefile")
text = p.read_text()
lines = text.splitlines()
for i, line in enumerate(lines, 1):
    if 'TS=$$(date -u +\\%Y' in line or '`@VER`=$$' in line or 'cp -R std/.' in line:
        before = ''
        if 1 <= i-1 <= len(lines):
            before = repr(lines[i-1])
        print(f"line {i}: {line!r} (ends_backslash={line.endswith(chr(92))}) prev={before}")
PY

Repository: loreste/mako

Length of output: 2388


🏁 Script executed:

#!/bin/bash
set -u

python3 - <<'PY'
from pathlib import Path
p = Path("/tmp/makefile-probe.txt")
# Only preserve the Makefile recipe lines of interest as literal bytes.
p.write_text(bytes([
    ord('\t'), ord('@'), ord('i'), ord('f'), ord(' '), ord('['), ord(' '), ord('d'), ord(' '),
    ord('s'), ord('t'), ord('d'), ord(']'), ord(';'), ord(' '), ord('\\ ')
    ord('d')  # duplicate 'd' from original snippet, but this is intentionally wrong so continuation fails
]) , "utf-8"))
PY

# Show the exact line endings and continuation characters for the affected Makefile lines.
python3 - <<'PY'
from pathlib import Path
p = Path("Makefile")
text = p.read_text()
for index, line in enumerate(text.splitlines(keepends=True), 1):
    if 33 <= index <= 38:
        print(f"--- line {index} ---")
        print(repr(line))
PY

Repository: loreste/mako

Length of output: 935


🌐 Web query:

GNU Make recipe continuation backslash whitespace after backslash behavior

💡 Result:

In GNU Make, a backslash () is used to split a long logical line in a recipe into multiple physical lines. It is critical that the backslash is the very last character on the line, immediately followed by the newline [1][2]. If you place any whitespace—such as spaces or tabs—after the backslash on the same line, GNU Make will not recognize the backslash as a line continuation character [3]. Instead, the backslash and the subsequent whitespace will be treated as part of the command line itself and passed directly to the shell [1][3]. This often results in unexpected behavior, such as a "command not found" error or syntax errors, because the shell receives the trailing backslash as a literal character in the command [3]. Key behavior regarding recipe line splitting: - Preservation: Unlike how backslash/newline pairs are treated elsewhere in a makefile, GNU Make does not remove the backslash and newline characters in a recipe [1][2]. They are preserved and passed to the shell [1][2]. - Shell Interpretation: Because the backslash and newline are passed to the shell, the actual behavior depends on how your chosen shell (e.g., /bin/sh or /bin/bash) interprets them [1][2]. - Recipe Prefix: When a recipe is split, if the first character of the next physical line is the recipe prefix character (a tab by default), GNU Make removes that prefix character before passing the line to the shell [1][2]. No additional whitespace is added by GNU Make during this process [1][2].

Citations:


🌐 Web query:

GNU Make manual recipe continuation whitespace after backslash literal backslash space shell

💡 Result:

In GNU Make, a backslash immediately followed by a newline is used to split a single logical recipe line into multiple physical lines in the makefile [1][2]. Crucially, in the context of recipe lines (the commands executed by the shell), the backslash/newline pair is not removed by make [1][2]. Instead, both the backslash and the newline characters are preserved and passed directly to the shell [1][2]. Consequently, whether whitespace (such as a space) is permitted after the backslash depends entirely on how your shell interprets that sequence [1][2]. If you place a space after the backslash and before the newline: 1. The backslash is no longer immediately followed by the newline. 2. The sequence is not recognized by GNU Make as a line continuation directive for the recipe [1][2]. 3. The shell will receive the space, the backslash, and the newline as part of the command line, which will likely result in a syntax error or unintended behavior depending on the shell [1][2]. For splitting recipe lines, you must place the backslash at the very end of the line, immediately before the newline character [1][2]. If the first character of the next physical line is the recipe prefix character (a tab by default), GNU Make will remove only that tab character; it does not add any whitespace to the recipe [1][2]. If you need to include a newline in a quoted string passed to the shell, it is often safer to store the command in a GNU Make variable, where standard makefile line-continuation rules apply and backslash/newline sequences are stripped [1][2].

Citations:


🌐 Web query:

Make 4.4.1 split-recipes backslash line continuation Makefile lines "The backslash must be the last character"

💡 Result:

In GNU Make (including version 4.4.1), you can split long lines in your Makefile for better readability by using the backslash (\) character [1][2][3][4]. To use this feature correctly, the backslash must be the last character on the physical line before the newline character [1][5][6]. Key points regarding this behavior: - Requirement: The backslash must immediately precede the newline [1][5]. If there are any characters (including spaces or tabs) after the backslash on that same line, it will not act as a line continuation and will instead be treated as part of the line content. - Non-recipe lines: Outside of recipes, GNU Make converts the backslash-newline sequence into a single space character [2][3]. - Recipe lines: Inside recipes, the backslash-newline sequence is preserved and passed to the shell [1][5]. How the shell interprets this sequence depends on the specific shell being used (typically /bin/sh) [1][5]. In most cases, the shell treats the backslash-newline as a line continuation, allowing the command to span multiple lines [5][6]. - Readability: While GNU Make does not impose a limit on the length of a line, using backslashes to break up long commands or complex variable definitions is a standard and widely accepted practice to improve makefile maintainability [2][3][7].

Citations:


🌐 Web query:

site:gnu.org/software/make/manual/html_node/Recipe-In-Preamble.html "\"When you use a backslash-newline\" recipe lines"

💡 Result:

In GNU make, a backslash-newline in a recipe line is not removed; both characters are preserved and passed to the shell. If the next line starts with the recipe prefix (a tab by default), that one character is removed, and no whitespace is added. (gnu.org)

Citations:


Fix the malformed GNU Make continuations before running make install.

Lines 34 and 36 leave whitespace after recipe \ continuations, so the fi/@VER=... and TS=.../printf boundaries are not kept on one Make command line. GNU Make passes the continuation newline and tab literally to the shell, so the manifest command is received with fi, @VER=..., and printf on separate lines and fails before writing install-manifest.json.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 34 - 36, The install recipe has malformed GNU Make
line continuations because whitespace follows the trailing backslashes,
separating the shell commands. In the install target around the `fi`, `VER`,
`TS`, and `printf` commands, remove all characters after each continuation
backslash so the backslash is the final character on the line and the complete
manifest command remains one shell invocation.

Comment thread Makefile
Comment on lines +36 to 41
TS=$$(date -u +\%Y-\%m-\%dT\%H:\%M:\%SZ 2>/dev/null \vert{}\vert{} date); \ printf '\%s\n' \ '{' \ ' "schema": "mako.install.v1",' \ " \"version\": \"$$VER\"," \
" \"prefix\": \"$(PREFIX)\"," \
" \"host\": \"$$HOST\"," \
" \"installedAt\": \"$$TS\"," \
" \"host\": \"$$HOST\"," \ " \"installedAt\": \"$$TS\"," \
" \"runtime\": \"$(RUNTIME_DST)\"," \
" \"std\": \"$(SHARE_DIR)/std\"" \
'}' > "$(SHARE_DIR)/install-manifest.json"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

tmp="$(mktemp)"
printf '\%s\n' '{' '  "schema": "mako.install.v1",' '}' > "$tmp"
python3 -m json.tool "$tmp"

Repository: loreste/mako

Length of output: 192


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- Makefile relevant section ---\n'
sed -n '30,50p' Makefile | cat -n

printf '\n--- behavioral probes ---\n'
tmp="$(mktemp)"
printf '%s\n' '{' '{"a":1}' > "$tmp"
printf -- 'single printf output:\n'
sed -n '1,5p' "$tmp"

printf '\nprintf \"\\%%s\\n\":\n'
printf '\%s\n' '{' '  "schema": "mako.install.v1",' '}' > "$tmp"
sed -n '1,5p' "$tmp"
python3 -m json.tool "$tmp" || true

printf '\nprintf \"%%%%s\\n\":\n'
printf '%s\n' '{' '  "schema": "mako.install.v1",' '}' > "$tmp"
sed -n '1,5p' "$tmp"

printf '\nmanifold printf behavior check:\n'
for line in 'printf '\''\%s\n'\'' '\''{'\''' 'printf '\''%s\n'\'' '\''{'\'''; do
  tmp="$(mktemp)"
  (eval "$line" 'a' 'b') > "$tmp"
  printf '%s ->' "$line"
  sed -n '1p' "$tmp" | od -c | tr '\n' ' '
  printf '\n'
done

Repository: loreste/mako

Length of output: 241


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Makefile relevant section ---'
sed -n '30,50p' Makefile | cat -n

printf '%s\n' ''
printf '%s\n' '--- behavioral probes ---'
tmp="$(mktemp)"
printf '%s\n' '{' '{"a":1}' > "$tmp"
printf 'single printf output:\n'
sed -n '1,5p' "$tmp"

printf '%s\n' ''
printf 'printf with literal backslash format:\n'
printf '\%s\n' '{' '  "schema": "mako.install.v1",' '}' > "$tmp"
sed -n '1,5p' "$tmp"
python3 -m json.tool "$tmp" || true

printf '%s\n' ''
printf 'printf with escaped backslash format:\n'
printf '%s\n' '{' '  "schema": "mako.install.v1",' '}' > "$tmp"
sed -n '1,5p' "$tmp"

printf '%s\n' ''
printf 'printf with escaped backslash format plus newline, if supported:\n'
printf '%s\n' '{' '  "schema": "mako.install.v1",' '}' > "$tmp"
sed -n '1,5p' "$tmp"

Repository: loreste/mako

Length of output: 1731


Emit the manifest with printf '%s\n'.

The literal \%s\n is passed to printf, which emits each JSON line with a leading \, producing invalid JSON.

Proposed fix
-	printf '\%s\n' \
+	printf '%s\n' \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TS=$$(date -u +\%Y-\%m-\%dT\%H:\%M:\%SZ 2>/dev/null \vert{}\vert{} date); \ printf '\%s\n' \ '{' \ ' "schema": "mako.install.v1",' \ " \"version\": \"$$VER\"," \
" \"prefix\": \"$(PREFIX)\"," \
" \"host\": \"$$HOST\"," \
" \"installedAt\": \"$$TS\"," \
" \"host\": \"$$HOST\"," \ " \"installedAt\": \"$$TS\"," \
" \"runtime\": \"$(RUNTIME_DST)\"," \
" \"std\": \"$(SHARE_DIR)/std\"" \
'}' > "$(SHARE_DIR)/install-manifest.json"
TS=$$(date -u +\%Y-\%m-\%dT\%H:\%M:\%SZ 2>/dev/null \vert{}\vert{} date); \
printf '%s\n' \
'{' \
' "schema": "mako.install.v1",' \
" \"version\": \"$$VER\"," \
" \"prefix\": \"$(PREFIX)\"," \
" \"host\": \"$$HOST\"," \
" \"installedAt\": \"$$TS\"," \
" \"runtime\": \"$(RUNTIME_DST)\"," \
" \"std\": \"$(SHARE_DIR)/std\"" \
'}' > "$(SHARE_DIR)/install-manifest.json"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 36 - 41, Update the manifest-generation printf
invocation in the Makefile to pass the format string as `%s\n` rather than
`\%s\n`, preserving the existing JSON arguments and output path so each manifest
line is emitted without a leading backslash.

Comment on lines +4 to +25
OUTPUT_FILE="docs/PROJECT_STRUCTURE.md"

# Asegurar que exista el directorio docs/
mkdir -p docs

echo "# Mako Repository Structure" > "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "This file is automatically generated by \`make structure\` (\`./scripts/update_structure.sh\`). Do not edit directly." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo '```text' >> "$OUTPUT_FILE"

# Genera el árbol ignorando binarios, cachés y carpetas de compilación
if command -v tree &> /dev/null; then
tree -I 'target|.git|.mko|out|*.dSYM|node_modules' --noreport >> "$OUTPUT_FILE"
else
# Si no tiene 'tree', usa git ls-files
git ls-files >> "$OUTPUT_FILE"
fi

echo '```' >> "$OUTPUT_FILE"

echo "✅ Estructura generada correctamente en $OUTPUT_FILE" No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail instead of reporting success after generation errors.

Without set -e, failures from mkdir, tree, git ls-files, or file writes are ignored, and line 25 still reports success. Add set -euo pipefail; preferably write to a temporary file and rename it only after successful generation.

🧰 Tools
🪛 Shellcheck (0.11.0)

[style] 10-10: Consider using { cmd1; cmd2; } >> file instead of individual redirects.

(SC2129)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/update_structure.sh` around lines 4 - 25, The update_structure.sh
script must fail when directory creation, file writes, tree, or git ls-files
fails instead of always reporting success. Add strict shell options including
set -euo pipefail, and preferably generate the output in a temporary file and
rename it to OUTPUT_FILE only after all generation steps succeed.

Comment on lines +16 to +20
if command -v tree &> /dev/null; then
tree -I 'target|.git|.mko|out|*.dSYM|node_modules' --noreport >> "$OUTPUT_FILE"
else
# Si no tiene 'tree', usa git ls-files
git ls-files >> "$OUTPUT_FILE"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make structure generation deterministic across the working tree and checked-in output.

The generator’s two discovery paths describe different file sets, and the checked-in document demonstrates the consequence by omitting the new generator.

  • scripts/update_structure.sh#L16-L20: choose one explicit repository file-selection policy for both branches.
  • docs/PROJECT_STRUCTURE.md#L1055-L1113: regenerate the document after applying that policy so scripts/update_structure.sh appears.
📍 Affects 2 files
  • scripts/update_structure.sh#L16-L20 (this comment)
  • docs/PROJECT_STRUCTURE.md#L1055-L1113
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/update_structure.sh` around lines 16 - 20, Make the tree and fallback
branches in scripts/update_structure.sh use the same explicit repository
file-selection policy, ensuring both produce identical file sets. Then
regenerate docs/PROJECT_STRUCTURE.md using the updated script so the checked-in
structure reflects that policy and includes scripts/update_structure.sh.

@loreste

loreste commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Thanks for the contribution. The PROJECT_STRUCTURE.md and update script are useful, but the Makefile changes have issues that would break make install:

  1. Missing space: $(shell$(CARGO) on line 10 — should be $(shell $(CARGO). This will cause make to fail on any target that uses TARGET_DIR.

  2. Collapsed line continuations: The install target's manifest generation (around lines 34-40) has multi-line shell commands collapsed into single lines with \vert{}\vert{} instead of ||. This breaks the JSON generation.

  3. Broken date escaping: \%Y-\%m-\%dT\%H:\%M:\%SZ — the % characters don't need escaping with backslashes in this context.

Could you fix these three issues? The docs and script additions look good — just need the Makefile to not break existing installs.

@loreste
loreste merged commit e2d1354 into loreste:main Jul 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants