Merge branch 'claude/dependency-notes': a library's AGENTS.consumer.md in its consumers' AGENTS.md
All checks were successful
Flatpak / flatpak (push) Successful in 12m18s
All checks were successful
Flatpak / flatpak (push) Successful in 12m18s
Brings in dependency notes: a crafter-build project that ships an AGENTS.consumer.md beside its project.cpp has it kept in a marked section of each consumer's AGENTS.md, refreshed by `crafter-build` and `crafter-build test`, chosen by cfg.dependencyNotes (Transitive, Direct, None). The new partition is Crafter.Build:Agents; the loader records where each GitProject and LocalProject came from and what a project file's pragmas resolved to. The branch started before master's four external-fetch commits. Both touch External.cpp, the Clang partition, project.cpp, README.md and AGENTS.md, but in different places, so the merge needed no hand reconciliation; the merged tree passes build.sh, the self-host build, format --check and the test suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
cc0a6aed8c
29 changed files with 1238 additions and 19 deletions
72
interfaces/Crafter.Build-Agents.cppm
Normal file
72
interfaces/Crafter.Build-Agents.cppm
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
export module Crafter.Build:Agents;
|
||||
import std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Dependency notes. A crafter-build project ships an AGENTS.consumer.md beside
|
||||
// its project.cpp, written for the agents of the projects that depend on it,
|
||||
// and crafter-build keeps those files in each consumer's AGENTS.md. Without
|
||||
// that, an agent working on a consumer knows nothing about the libraries it
|
||||
// builds against unless someone copied the knowledge in by hand, and a copy
|
||||
// goes stale the moment the library changes.
|
||||
export namespace Crafter {
|
||||
struct Configuration;
|
||||
|
||||
// Which dependencies' notes a project's AGENTS.md carries: the value of
|
||||
// Configuration::dependencyNotes.
|
||||
enum class DependencyNotes {
|
||||
// The projects this one builds against, and the projects those build
|
||||
// against, all the way down.
|
||||
Transitive,
|
||||
// Only the projects this one's own configurations and
|
||||
// `#pragma crafter dependency` lines name.
|
||||
Direct,
|
||||
// None. A section an earlier build wrote is removed.
|
||||
None,
|
||||
};
|
||||
|
||||
// One dependency's AGENTS.consumer.md, ready to merge.
|
||||
struct DependencyNote {
|
||||
// What the comment above the note calls the dependency: its
|
||||
// repository's name, or a local project's directory name.
|
||||
std::string name;
|
||||
// The file's content, without leading blank lines or trailing
|
||||
// whitespace. Lines end in LF; the merge writes the line ending the
|
||||
// document uses.
|
||||
std::string text;
|
||||
};
|
||||
|
||||
// `agentsMd` with its dependency notes section holding `notes`, in order.
|
||||
// A document without a section gets one appended after a blank line; one
|
||||
// with a section has it rewritten in place, wherever it was moved to; and
|
||||
// empty `notes` remove it. Removing a section that ends the document takes
|
||||
// the blank line in front of it too, so a document that ended in a newline
|
||||
// before it gained the section gets its own bytes back. The section uses
|
||||
// the document's line ending, so a CRLF file stays CRLF.
|
||||
//
|
||||
// The two marker lines count only as whole lines outside fenced code
|
||||
// blocks, which is what lets a document quote them. Throws, naming the
|
||||
// line, when there are notes to write and the markers are unbalanced, or
|
||||
// when the section would have to be appended to a document that ends
|
||||
// inside a code block: guessing which part of someone's file is generated
|
||||
// would overwrite their text, and missing the section would add another on
|
||||
// every build. With no notes it never throws, since leaving the document
|
||||
// alone loses nothing. Also throws for a note that would break the section
|
||||
// when it is read back: one with a marker line of its own, or a code block
|
||||
// it never closes.
|
||||
std::string MergeDependencyNotes(std::string_view agentsMd, std::span<const DependencyNote> notes);
|
||||
|
||||
// Refresh the dependency notes section of the AGENTS.md beside
|
||||
// `projectFile` from the AGENTS.consumer.md of every dependency
|
||||
// project.dependencyNotes selects. `project` is the Configuration that
|
||||
// file returned, after LoadProject: loading is what found the
|
||||
// dependencies. Does nothing when there is no AGENTS.md, which is never
|
||||
// created. Writes only when the text changes, in place, so hard links,
|
||||
// ownership and a read-only AGENTS.md are respected; returns whether it
|
||||
// wrote. A read or write that fails, a section that cannot be placed and
|
||||
// a dependency's unusable notes are warnings: the notes never fail a
|
||||
// build, and nothing is reported when nothing would have been written.
|
||||
bool UpdateAgentsMd(const Configuration& project, const fs::path& projectFile);
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import :Interface;
|
|||
import :Implementation;
|
||||
import :External;
|
||||
import :Toolchain;
|
||||
import :Agents;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
export namespace Crafter {
|
||||
|
|
@ -494,6 +495,14 @@ export namespace Crafter {
|
|||
// }});
|
||||
// Not part of VariantId: a script changes nothing that is compiled.
|
||||
std::vector<Script> scripts;
|
||||
// Which dependencies' AGENTS.consumer.md `crafter-build` and
|
||||
// `crafter-build test` keep in the AGENTS.md beside project.cpp, when
|
||||
// there is one (see Crafter.Build:Agents):
|
||||
// cfg.dependencyNotes = DependencyNotes::Direct;
|
||||
// Only read on the Configuration project.cpp returns; a dependency's
|
||||
// setting decides nothing for its consumers. Not part of VariantId:
|
||||
// the notes change nothing that is compiled.
|
||||
DependencyNotes dependencyNotes = DependencyNotes::Transitive;
|
||||
void GetInterfacesAndImplementations(std::span<fs::path> interfaces, std::span<fs::path> implementations);
|
||||
// Retry the `import X;` names GetInterfacesAndImplementations could not
|
||||
// place, against the dependency DAG as it stands now. Sources are
|
||||
|
|
|
|||
|
|
@ -114,3 +114,30 @@ export namespace Crafter {
|
|||
// SHA for reproducible, offline-after-first-fetch builds.
|
||||
fs::path GitFetch(const GitSource& source);
|
||||
}
|
||||
|
||||
namespace Crafter {
|
||||
// Where GitProject or LocalProject loaded a Configuration from. The
|
||||
// loader keeps this beside the cache that owns the Configuration, since
|
||||
// the Configuration itself cannot say: only the one they return has its
|
||||
// path rebased, and the configurations a dependency's project file makes
|
||||
// for itself keep a path such as "./", which resolves against the
|
||||
// consumer's directory.
|
||||
struct LoadedProject {
|
||||
fs::path projectFile; // absolute
|
||||
// What a consumer's AGENTS.md calls it: the repository's name for a
|
||||
// git project, with the path below the clone's root for one that is
|
||||
// not at it, and the directory's name for a local one. The same on
|
||||
// every machine and for a --local checkout and its clone alike,
|
||||
// which a path into the cache or a sibling checkout is not, and free
|
||||
// of what a URL can carry, credentials included.
|
||||
std::string name;
|
||||
// Which library this is, whatever the revision: the repository and
|
||||
// the project file in it for git, the project file for a local one.
|
||||
// Two specs that pin one library differently are one library.
|
||||
std::string identity;
|
||||
};
|
||||
// The LoadedProject behind a Configuration that GitProject or
|
||||
// LocalProject returned; nullopt for any other: the project crafter-build
|
||||
// was pointed at, and every configuration a project file makes itself.
|
||||
std::optional<LoadedProject> LoadedProjectOf(const Configuration& cfg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,13 @@ namespace Crafter {
|
|||
// that library as a dependency, the test's GitProject loads crafter-build's
|
||||
// project file in turn and the two recurse until the cycle guard throws.
|
||||
export bool IsTopLevelProject();
|
||||
// What the `#pragma crafter dependency` lines of `projectFile` resolved
|
||||
// to the last time LoadProject loaded it in this process, in file order;
|
||||
// empty when it has none or was never loaded. Unlike ProjectDependency
|
||||
// this still answers once LoadProject has returned, which is when the
|
||||
// dependency notes of the project crafter-build was pointed at are
|
||||
// gathered (Crafter.Build:Agents).
|
||||
std::vector<Configuration*> PragmaDependencies(const fs::path& projectFile);
|
||||
// Resolves the directory holding distributed runtime assets (Crafter.Build
|
||||
// module sources, wasi-runtime/, etc). Honors CRAFTER_BUILD_HOME; otherwise
|
||||
// derives <prefix>/share/crafter-build from the running executable's path.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export import :Platform;
|
|||
export import :Implementation;
|
||||
export import :Shader;
|
||||
export import :External;
|
||||
export import :Agents;
|
||||
export import :Test;
|
||||
export import :Lint;
|
||||
export import :Script;
|
||||
|
|
|
|||
Loading…
Reference in a new issue