This commit is contained in:
parent
a25b0a1ded
commit
8892154b28
70 changed files with 2780 additions and 596 deletions
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
#pragma once
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
@ -96,12 +96,91 @@ export namespace Crafter {
|
|||
struct TestResult {
|
||||
std::string name;
|
||||
TestOutcome outcome = TestOutcome::Pass;
|
||||
int exitCode = 0;
|
||||
int signal = 0;
|
||||
std::int32_t exitCode = 0;
|
||||
std::int32_t signal = 0;
|
||||
std::chrono::milliseconds duration{0};
|
||||
std::string output;
|
||||
};
|
||||
|
||||
// One lint diagnostic. Produced by LintContext::Report — or derived by
|
||||
// the driver when a transform rule's SetContent output differs from the
|
||||
// file on disk ("would reformat") — collected and printed compiler-style
|
||||
// by RunLint (Crafter.Build:Lint):
|
||||
// <file>:<line>: warning: <message> [<rule>]
|
||||
struct LintFinding {
|
||||
fs::path file;
|
||||
std::size_t line = 0; // 1-based; 0 = whole-file finding
|
||||
std::string rule; // name of the rule that produced it
|
||||
std::string message;
|
||||
};
|
||||
|
||||
// Parsed `// lint-disable-*` suppression directives for one file (see
|
||||
// LintContext::Suppressed). Line keys are 1-based and refer to the line a
|
||||
// next-line directive TARGETS (the line after the comment).
|
||||
struct LintSuppressions {
|
||||
bool fileAll = false;
|
||||
std::unordered_set<std::string> fileRules;
|
||||
std::unordered_set<std::size_t> lineAll;
|
||||
std::unordered_map<std::size_t, std::unordered_set<std::string>> lineRules;
|
||||
};
|
||||
|
||||
// Per-file view handed to each LintRule's check callback. Every member
|
||||
// function is out-of-line and CRAFTER_API (defined in Crafter.Build:Lint's
|
||||
// implementation unit) because rule lambdas execute from the user's
|
||||
// project DLL on Windows — clang does not emit module-attached in-class
|
||||
// inline bodies into consumers (see ArgQuery below).
|
||||
struct LintContext {
|
||||
fs::path file; // absolute path of the file under lint
|
||||
std::string content; // whole file as read from disk
|
||||
std::vector<std::string_view> lines; // views into `content`, one per line, no '\n'
|
||||
|
||||
CRAFTER_API std::string Extension() const; // ".cppm", ".cpp", ".h", ...
|
||||
CRAFTER_API std::string_view Line(std::size_t n) const; // 1-based; empty if out of range
|
||||
// `content` with //-comments, /*...*/ comments and string/char literal
|
||||
// bodies blanked to spaces, newlines preserved — offsets and line
|
||||
// numbers stay valid. Built on first call, cached per file. Raw string
|
||||
// literals are not recognized (v1 limitation).
|
||||
CRAFTER_API const std::string& CommentStripped();
|
||||
// Record a finding at `line` (1-based; pass 0 for a whole-file finding).
|
||||
CRAFTER_API void Report(std::size_t line, std::string message);
|
||||
// Replace the file's content. Makes this rule a *transform*:
|
||||
// `crafter-build format` writes the result back to disk; `lint`
|
||||
// derives would-reformat findings from it (dry — never writes).
|
||||
// `lines` is re-split and CommentStripped() re-derives on next call;
|
||||
// string_views taken before this call are invalidated. Recommended
|
||||
// pattern: build the new string, call SetContent once at the end. Do
|
||||
// not assign `content` directly — that bypasses the re-split. May be
|
||||
// combined with Report() in the same rule.
|
||||
CRAFTER_API void SetContent(std::string newContent);
|
||||
// True when `rule` is suppressed at `line` (1-based; 0 = whole-file,
|
||||
// which only file-level directives cover) by a suppression comment:
|
||||
// // lint-disable-next-line [rules...] applies to the following line
|
||||
// // lint-disable-file [rules...] applies to the whole file
|
||||
// Rule names are space- or comma-separated; none = all rules. The
|
||||
// driver already filters Report()
|
||||
// findings and reverts line-preserving transform edits on suppressed
|
||||
// lines; a transform that MERGES or SPLITS lines must consult this
|
||||
// itself for every line its edit touches (the driver cannot map lines
|
||||
// across a count-changing rewrite). Parsed lazily from the raw lines;
|
||||
// re-parsed after SetContent.
|
||||
CRAFTER_API bool Suppressed(std::string_view rule, std::size_t line);
|
||||
|
||||
// Driver wiring — set by RunLint before each check call. Not for rules.
|
||||
std::string activeRule;
|
||||
std::vector<LintFinding>* sink = nullptr;
|
||||
std::optional<std::string> commentStrippedCache;
|
||||
std::optional<LintSuppressions> suppressionsCache;
|
||||
};
|
||||
|
||||
// A named lint rule: `check` runs once per (rule, file) over the project's
|
||||
// own sources. Rules self-filter by ctx.Extension() / ctx.file. A rule
|
||||
// that calls ctx.SetContent is a transform — defined once, it both gates
|
||||
// `crafter-build lint` and fixes under `crafter-build format`.
|
||||
struct LintRule {
|
||||
std::string name;
|
||||
std::function<void(LintContext&)> check;
|
||||
};
|
||||
|
||||
// The host target triple, detected once per process by running
|
||||
// `clang++ -print-target-triple` and cached. Used as the default for
|
||||
// Configuration::target so projects don't have to hardcode it for the
|
||||
|
|
@ -172,6 +251,8 @@ export namespace Crafter {
|
|||
// wasmVariants instead.
|
||||
std::vector<std::string> wasmVariantFlags;
|
||||
std::vector<Test> tests;
|
||||
// Lint rules for `crafter-build lint`. Populate via AddLintRule.
|
||||
std::vector<LintRule> lintRules;
|
||||
CRAFTER_API void GetInterfacesAndImplementations(std::span<fs::path> interfaces, std::span<fs::path> implementations);
|
||||
// Declare a test. Sources default to `tests/<name>/main.cpp` resolved
|
||||
// against this Configuration's path; target/march/mtune/sysroot/debug
|
||||
|
|
@ -188,9 +269,14 @@ export namespace Crafter {
|
|||
// `tests/<name>/main.cpp` source and the same interface set, each
|
||||
// compiled with the tier's `-march`/`-mtune`. Test names are
|
||||
// `<name>-<march>`.
|
||||
CRAFTER_API void AddMarchVariants(std::string_view name,
|
||||
std::span<fs::path> interfaces,
|
||||
std::span<const struct MarchTier> tiers);
|
||||
CRAFTER_API void AddMarchVariants(std::string_view name, std::span<fs::path> interfaces, std::span<const struct MarchTier> tiers);
|
||||
// Register a lint rule for `crafter-build lint` / `format`. Rules
|
||||
// registered on any Configuration whose path lies inside the project
|
||||
// root are collected (deduplicated by name, root-first) — attach them
|
||||
// to the lib or the exe config, either works. Rules that call
|
||||
// ctx.SetContent are transforms (see LintContext::SetContent).
|
||||
// Defined in Crafter.Build:Lint.
|
||||
CRAFTER_API void AddLintRule(std::string name, std::function<void(LintContext&)> check);
|
||||
// Suffix that uniquely identifies this Configuration's compile state.
|
||||
// target+march+mtune are spelled out for readability; the rest
|
||||
// (type, debug, sysroot, defines, compileFlags) collapse into a short
|
||||
|
|
@ -198,7 +284,7 @@ export namespace Crafter {
|
|||
// compile state can't clobber each other's outputs.
|
||||
std::string VariantId() const {
|
||||
std::string compileKey;
|
||||
compileKey += std::to_string(static_cast<int>(type));
|
||||
compileKey += std::to_string(static_cast<std::int32_t>(type));
|
||||
compileKey += '|';
|
||||
compileKey += debug ? '1' : '0';
|
||||
compileKey += '|';
|
||||
|
|
@ -297,4 +383,4 @@ export namespace Crafter {
|
|||
// can query their own flags (`--timing`, ...) without re-rolling the
|
||||
// for-arg-in-args loop.
|
||||
CRAFTER_API ArgQuery ApplyStandardArgs(Configuration& cfg, std::span<const std::string_view> args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
@ -44,10 +44,7 @@ export namespace Crafter {
|
|||
fs::file_time_type latestArtifact = fs::file_time_type::min();
|
||||
};
|
||||
|
||||
CRAFTER_API ExternalBuildResult BuildExternal(
|
||||
const ExternalDependency& dep,
|
||||
std::string_view target,
|
||||
std::atomic<bool>& cancelled);
|
||||
CRAFTER_API ExternalBuildResult BuildExternal(const ExternalDependency& dep, std::string_view target, std::atomic<bool>& cancelled);
|
||||
|
||||
// Specification for a sibling crafter-build project to fetch and depend on.
|
||||
// GitSource picks the revision: leave branch + commit empty for the
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
|
|||
59
interfaces/Crafter.Build-Lint.cppm
Normal file
59
interfaces/Crafter.Build-Lint.cppm
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
export module Crafter.Build:Lint;
|
||||
import std;
|
||||
import :Clang;
|
||||
|
||||
export namespace Crafter {
|
||||
enum class LintMode {
|
||||
// `lint`: diagnose only; transform output becomes would-reformat
|
||||
// findings. Never writes. The default.
|
||||
Report,
|
||||
// `format --check`: dry run; record the files that would change.
|
||||
Check,
|
||||
// `format`: rewrite changed files in place.
|
||||
Apply,
|
||||
};
|
||||
|
||||
struct RunLintOptions {
|
||||
// Rule-name globs ('*', '?'); empty = every registered rule.
|
||||
std::vector<std::string> globs;
|
||||
// Enumerate matching rule names without running them.
|
||||
bool listOnly = false;
|
||||
LintMode mode = LintMode::Report;
|
||||
// Absolute path of the loaded project.cpp. It is linted too, and its
|
||||
// parent directory is the project root that decides which dependency
|
||||
// Configurations contribute rules/files (GitProject / cache-dir deps
|
||||
// are foreign code and skipped). Empty => fall back to cfg.path.
|
||||
std::filesystem::path projectFile;
|
||||
};
|
||||
|
||||
struct LintSummary {
|
||||
std::vector<LintFinding> findings; // sorted by (file, line); filled in every mode
|
||||
// Apply: files rewritten on disk. Report/Check: files a transform
|
||||
// would change. The `format` verb's exit code keys off this in Check
|
||||
// mode; formatting files in Apply mode is success.
|
||||
std::vector<std::filesystem::path> changedFiles;
|
||||
std::size_t filesLinted = 0;
|
||||
std::size_t rulesRun = 0; // rules remaining after glob filter
|
||||
std::size_t errors = 0; // rule exceptions + write failures
|
||||
bool noRulesDefined = false; // project registered no rules at all
|
||||
// Host-side only (like TestSummary::AllPassed), safe as in-class inline.
|
||||
bool Clean() const { return findings.empty() && !noRulesDefined; }
|
||||
};
|
||||
|
||||
// Run the project's lint rules over its own sources: module interfaces
|
||||
// (+ partitions), implementations, cFiles, cuda, shaders, declared tests'
|
||||
// sources, and project.cpp itself — for the root Configuration plus every
|
||||
// transitive dependency whose path lies inside the project root. Rules
|
||||
// run in registration order per file, each seeing the previous rule's
|
||||
// transform output; a rule that throws is reverted and surfaced as a
|
||||
// finding + error. Only Apply mode writes to disk, and only files whose
|
||||
// final content differs from the original. Prints per-mode output:
|
||||
// findings compiler-style (Report), would-change paths (Check), or
|
||||
// formatted paths (Apply), plus a summary line.
|
||||
CRAFTER_API LintSummary RunLint(Configuration& projectCfg, const RunLintOptions& opts);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
@ -10,11 +10,11 @@ namespace fs = std::filesystem;
|
|||
namespace Crafter {
|
||||
struct Configuration;
|
||||
struct CommandResult {
|
||||
int exitCode = 0;
|
||||
std::int32_t exitCode = 0;
|
||||
std::string output;
|
||||
bool crashed = false;
|
||||
bool timedOut = false;
|
||||
int signal = 0;
|
||||
std::int32_t signal = 0;
|
||||
};
|
||||
std::string BuildStdPcm(const Configuration& config, fs::path stdPcm);
|
||||
fs::path GetCacheDir();
|
||||
|
|
@ -27,4 +27,8 @@ namespace Crafter {
|
|||
// module sources, wasi-runtime/, etc). Honors CRAFTER_BUILD_HOME; otherwise
|
||||
// derives <prefix>/share/crafter-build from the running executable's path.
|
||||
export CRAFTER_API fs::path GetCrafterBuildHome();
|
||||
}
|
||||
// Wildcard name matching ('*', '?') shared by the test and lint verbs.
|
||||
bool MatchGlob(std::string_view glob, std::string_view name);
|
||||
// Empty `globs` matches everything.
|
||||
bool MatchAny(std::span<const std::string> globs, std::string_view name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "Crafter.Build-Api.h"
|
||||
|
|
@ -32,7 +32,7 @@ export namespace Crafter {
|
|||
Configuration* parent;
|
||||
std::size_t index;
|
||||
|
||||
Test& test() const { return parent->tests[index]; }
|
||||
Test& Ref() const { return parent->tests[index]; }
|
||||
|
||||
// Override the path the test's sources resolve against. Defaults to
|
||||
// "./" (project root, where tests/<name>/main.cpp lives). Override
|
||||
|
|
@ -59,7 +59,7 @@ export namespace Crafter {
|
|||
|
||||
struct RunTestsOptions {
|
||||
std::vector<std::string> globs;
|
||||
int jobs = 0;
|
||||
std::int32_t jobs = 0;
|
||||
std::optional<std::chrono::seconds> timeoutOverride;
|
||||
bool listOnly = false;
|
||||
// Single-target run: only tests whose Configuration::target matches
|
||||
|
|
@ -76,11 +76,11 @@ export namespace Crafter {
|
|||
};
|
||||
|
||||
struct TestSummary {
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
int crashed = 0;
|
||||
int timedOut = 0;
|
||||
int skipped = 0;
|
||||
std::int32_t passed = 0;
|
||||
std::int32_t failed = 0;
|
||||
std::int32_t crashed = 0;
|
||||
std::int32_t timedOut = 0;
|
||||
std::int32_t skipped = 0;
|
||||
std::vector<TestResult> results;
|
||||
bool AllPassed() const { return failed == 0 && crashed == 0 && timedOut == 0; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//SPDX-License-Identifier: LGPL-3.0-only
|
||||
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
export module Crafter.Build;
|
||||
export import :Clang;
|
||||
|
|
@ -8,5 +8,6 @@ export import :Implementation;
|
|||
export import :Shader;
|
||||
export import :External;
|
||||
export import :Test;
|
||||
export import :Lint;
|
||||
export import :Progress;
|
||||
export import :Asset;
|
||||
export import :Asset;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue