Commit graph Crafter.Build/implementations/Crafter.Build-External.cpp
Author SHA1 Message Date
651720e494 feat(lint): const-local and constexpr-constant rules
Two rules the AST makes possible, plus the mutation analysis behind them.

const-local reports a local that is never written. It is restricted to SCALARS
— integers, bools, enums, floating types — and that restriction is what makes
the answer exact rather than a guess: a scalar has no member functions, so the
only ways to write one are assignment, ++/--, having its address taken, or
binding to a non-const reference. All four are now tracked in the walk:

  - assignment and compound assignment visit their LEFT operand in a write
    context, the right one normally;
  - ++/-- and & write their operand;
  - a call argument is checked against the callee's parameter type, so passing
    to `const int&` or by value is a read while `int&` is a write;
  - initialising a non-const reference writes what it binds to.

For a class type a non-const method call could mutate it, and deciding that is
the whole-program analysis clang-tidy does, so those are simply out of scope
rather than guessed at.

constexpr-constant promotes a const constant whose initialiser is made only of
literals and operators, so `const int A = 1 << 4;` qualifies and
`const int B = Compute();` does not.

On this repository const-local found 103 candidates, which was too many to be
useful, and the reason was informative: most were range-for bindings and
pointer locals. `for (T* const x : …)` and `T* const p` are not spellings
anybody writes, and the useful constness for a pointer is on the pointee, which
this rule cannot advise on. Excluding both leaves 36, all plain bool or enum
locals worth fixing — isWasm, isPe, exists, writes, isC and so on. Those 36 are
fixed in this commit; the compiler verified every one.

Both rules are report-only. The analysis is exact, but adding const is a
judgement about intent as much as mechanics, and a wrong suggestion should cost
a glance rather than a build. const-local also deliberately does not become a
transform: inserting `const` before a shared type would apply it to every
declarator in a multi-declarator statement, including any that IS written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 00:50:48 +02:00
91e4f59a94 feat(lint): no-char-pointer reads the AST, retiring the interop denylist
The rule was `\bchar\s*\*` over the text minus a substring denylist — argv,
getenv, setenv, dlerror, c_str, .data(, reinterpret_cast, extern ". Every entry
was a patch for one interop site, the list could only grow as libraries
arrived, and each entry disabled the rule for the whole LINE it appeared on.

It now walks declarations and asks the question the denylist was approximating:
whose header dictates this spelling? A declaration with C language linkage, or
one whose initialiser binds to an entity declared outside the project root, is
somebody else's API and keeps its spelling. getenv, c_str and friends are
exempt because of where they are declared, not because they are named here, so
a new external library needs no new entry.

Two bugs found while testing this, both of which had made the rule silently
pass over the entire repository:

clang_getCursorLanguage cannot be used to detect extern "C". Its default answer
is CXLanguage_C for a plain function, variable or parameter even in a C++
translation unit, so isExternC was true almost everywhere and exempted
everything. Replaced by tracking CXCursor_LinkageSpec depth during the walk,
reading the extent text to tell extern "C" from extern "C++".

Attributing any foreign reference in a subtree to the enclosing declaration was
too broad: a function that merely touched libc++ somewhere in its body would
exempt its own signature. Narrowed to initialiser contexts — a variable, field
or parameter — which is where a binding to a foreign API actually occurs.

Also: functions now carry their RESULT type rather than the whole function
type, since the parameters arrive as their own declarations and would otherwise
be reported twice. main's parameters are exempt structurally, its signature
being fixed by the language rather than chosen here.

Two sites keep an explicit lint-disable, both Crafter::Run taking main's argv
verbatim. That is two visible, reasoned suppressions in place of a denylist
that silently disabled the rule for every line mentioning one of eight tokens.

ExternalCloneDir and ExternalIncludeFlags are now exposed from :External, so a
source that includes an external dependency's headers can be parsed without
running a build to discover where they are. BuildExternal derives its own
working directory through the same function, so the two cannot drift.
Crafter.Build-Shader.cpp needed this to parse at all.

A file with no compile command — project.cpp, which LoadProject builds with its
own flags — is not a translation unit of the build graph, so AST rules skip it
the way a rule self-filters by extension. That is distinct from a file that
should have parsed and did not, which stays an error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 23:46:32 +02:00
8892154b28 linting
Some checks failed
CI / build-test-release (push) Failing after 7m15s
2026-07-23 01:24:42 +02:00
7ff426b2f0 SPDX license update
Some checks failed
CI / build-test-release (push) Failing after 5m36s
Canonical LGPL-3.0-only text, GPL-3.0 companion, SPDX headers on all
first-party sources, MIT for examples/. Vendored lib/ untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:19:17 +02:00
catbot
06cccc3921 perf: build external cmake deps in parallel
All checks were successful
CI / build-test-release (pull_request) Successful in 9m0s
cmake --build was invoked with no --parallel, so the default Unix
Makefiles generator compiled external deps (DPP, msquic, glslang, …)
one translation unit at a time, leaving all but one core idle.

Pass an explicit --parallel N using hardware_concurrency() so dep
builds use the available cores. An explicit count (not a bare
--parallel) avoids an unbounded make -j fork bomb on the Makefiles
generator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 20:38:00 +00:00
f0bdfbee77 fixed mingw warning
All checks were successful
CI / build-test-release (push) Successful in 18m54s
2026-05-27 05:43:56 +02:00
catbot
5ac7077dd0 fix external cache race on concurrent builds
Some checks failed
CI / build-test-release (pull_request) Has been cancelled
Two crafter-build invocations resolving to the same external dep both
cloned into <cache>/<name>-<hash>.tmp, corrupting each other's pack
tempfiles. Use a random per-invocation tmp suffix and treat a failed
rename whose destination now exists as the loser-of-the-race case —
discard the local clone and reuse the winner's.
2026-05-27 01:55:29 +00:00
f442caa888 asset changes
Some checks failed
CI / build-test-release (push) Failing after 15m11s
2026-05-19 00:50:06 +02:00
659103a123 external dep lib dir
Some checks failed
CI / build-test-release (push) Failing after 13m55s
2026-05-06 03:59:19 +02:00
d7a9c85ea6 fixes
All checks were successful
CI / build-test-release (push) Successful in 15m14s
2026-05-02 21:08:51 +02:00
730e763718 ShellQuote: use double quotes on Windows
All checks were successful
CI / build-test-release (push) Successful in 14m41s
cmd.exe treats single quotes as literal characters, so the existing
single-quote wrapping broke git/cmake invocations on a Windows host
("could not create leading directories of '<path>'").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 03:50:06 +02:00
0ab30a1d81 fixes
Some checks failed
CI / build-test-release (push) Failing after 8m31s
2026-04-30 02:20:19 +02:00
eaee502e8c V2: WASI, -r flag, CI pipeline, examples & tests cleanup
Some checks failed
CI / build-test-release (pull_request) Failing after 44s
WASI / wasm32 target support
- Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32")
- Skip -march/-mtune for wasm (clang rejects them)
- Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj
  -D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync)
- .wasm output extension in expectedOutputFor and link command
- EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html +
  runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL
  set in the templated index.html so a single shim handles any output name

-r run flag in the CLI: build then exec the artifact (host targets only;
  rejects libraries; auto .exe/.wasm extension handling)

CI pipeline (.forgejo/workflows/ci.yaml)
- Triggers: PR/push to master + manual dispatch
- Single arch-latest container job: install deps, bootstrap, self-rebuild,
  run tests, cross-compile mingw, package both archives, upload artifacts
- Rolling 'latest' release published only on push/dispatch to master

mingw cross-compile from Linux now works end-to-end:
- ExternalDependency cache key includes target so per-target glslang builds
  don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends
  'd' to lib names and breaks linking); cross-compile cmake flags
  (CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...)
- project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic
  and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh
  link pulls pthread which mingw doesn't link by default)
- mingw compile uses -femulated-tls so std::__once_callable etc reference
  the same emutls symbols libstdc++ provides
- mingw link auto-adds -lstdc++exp -lpthread

GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux
+ Windows) now both use it instead of duplicating the resolution.

Examples reorg: hello-world, library, with-module, wasi, tests — each with
its own README. Tests reorg: per-test directory with inner/ fixture, no
shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
f13671b2be v2 nearly done 2026-04-27 07:04:42 +02:00