Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | 3x 3x 24x 3x 24x 8x 8x 24x 17x 17x 17x 17x 17x 7x 17x 7x 7x 17x 17x 17x 17x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 15x 15x 8x 1x 7x 7x 1x 1x 11x 11x 15x 15x 6x 13x 2x 11x 1x 1x 1x 1x 11x 15x 11x 3x 3x 3x 3x 1x 1x 1x 2x 3x 3x 7x 7x 7x 4x 3x 2x 2x 6x 6x 6x 6x 2x 9x 9x 11x 11x 11x 6x 6x 6x 5x 7x 7x 7x 5x 1x | /**
* ResultPrinter
* Prints transpiler compilation results
*/
import DeclarationSite from "../utils/DeclarationSite";
import ITranspilerResult from "../transpiler/types/ITranspilerResult";
import ToolchainRequirementUtils from "../utils/ToolchainRequirementUtils";
import type IRecordedRequirement from "../transpiler/types/IRecordedRequirement";
import type IToolchainRequirement from "../transpiler/types/IToolchainRequirement";
import type TOutputMode from "../transpiler/types/TOutputMode";
/** Column width for the requirement label in the toolchain report. */
const LABEL_WIDTH = 21;
/** Source sites shown per requirement before collapsing to a count. */
const SITE_LIMIT = 3;
/**
* Print transpiler compilation results
*/
class ResultPrinter {
/**
* Print pipeline compilation result
* @param result - Transpiler result to print
*/
static print(result: ITranspilerResult): void {
// Print warnings
for (const warning of result.warnings) {
console.warn(`Warning: ${warning}`);
}
// Print errors
for (const error of result.errors) {
// Format error with source path if available
// Through DeclarationSite, like the diagnostic BODY beside it. A conflict
// rendered its locations cwd-relative here and its header as a raw absolute
// path, so one diagnostic spelled the same file two ways, one line apart.
// DeclarationSite's docblock claims to be the single source of truth for
// `sourceFile:line`; the header was the last renderer bypassing it.
const location = error.sourcePath
? `${DeclarationSite.displayPath(error.sourcePath)}:${error.line}:${error.column}`
: `${error.line}:${error.column}`;
console.error(`Error: ${location} ${error.message}`);
}
// Summary
if (result.success) {
console.log("");
console.log(`Compiled ${result.filesProcessed} files`);
console.log(`Collected ${result.symbolsCollected} symbols`);
console.log(`Generated ${result.outputFiles.length} output files:`);
for (const file of result.outputFiles) {
console.log(` ${file}`);
}
ResultPrinter.printRequirements(result);
} else {
console.error("");
console.error("Compilation failed");
}
}
/**
* Issue #1143: Report what THIS project needs from its toolchain.
*
* A static compatibility matrix can only say what C-Next might need. This
* says what the code just transpiled actually needs, and where each cost came
* from -- the number someone choosing a toolchain is really asking for.
* Printed only when something exceeds the mode's baseline, so an ordinary
* C99 project sees nothing.
*/
private static printRequirements(result: ITranspilerResult): void {
const recorded = result.requirements ?? [];
const mode = ToolchainRequirementUtils.modeOf(recorded);
const reportable = ToolchainRequirementUtils.reportable(recorded, mode);
if (reportable.length === 0) return;
const baseline = ToolchainRequirementUtils.lookup(
ToolchainRequirementUtils.baselineKey(mode),
);
console.log("");
console.log("Toolchain requirements for this project:");
console.log("");
ResultPrinter.printStandardSection(reportable, mode, baseline.standard);
ResultPrinter.printCompilerSection(reportable, mode);
ResultPrinter.printPlatformSection(reportable);
console.log("");
console.log(" See docs/compatibility.md for the full matrix.");
}
/**
* Requirements whose cost is a later language standard than the baseline.
*
* A requirement that also names an extension gets a second line: GCC and
* Clang accept it below the standard, so a reader already on that standard
* must not be told they need an extension.
*/
private static printStandardSection(
reportable: readonly IRecordedRequirement[],
mode: TOutputMode,
baselineStandard: string,
): void {
console.log(" Language standard");
console.log(` ${baselineStandard.padEnd(LABEL_WIDTH)}baseline`);
for (const entry of reportable) {
const requirement = ToolchainRequirementUtils.lookup(entry.key);
if (requirement.platformLib !== null) continue;
if (!ToolchainRequirementUtils.exceedsBaselineStandard(entry.key, mode)) {
continue;
}
ResultPrinter.printLeaf(requirement.standard, requirement, entry);
if (requirement.extensions.length > 0) {
const note = `(or a GNU/Clang extension below ${requirement.standard})`;
console.log(` ${"".padEnd(LABEL_WIDTH)} ${note}`);
}
}
}
/**
* Requirements whose cost is an unconditional compiler extension.
*
* Extensions belonging to a requirement that also names a platform library
* are deliberately left out: those live inside one arm of a #if chain, so
* they are only needed when that arm is selected. Listing them here would
* tell an AVR user they need ARM inline assembly.
*/
private static printCompilerSection(
reportable: readonly IRecordedRequirement[],
mode: TOutputMode,
): void {
// `unconditionalExtensions` owns the "does every target need this?"
// decision, so this report and the per-file banner cannot answer it
// differently -- they are the same question asked twice.
const unconditionalNames = new Set<string>(
ToolchainRequirementUtils.unconditionalExtensions(reportable),
);
const unconditional = reportable.filter((entry) => {
const requirement = ToolchainRequirementUtils.lookup(entry.key);
if (
!requirement.extensions.some((name) => unconditionalNames.has(name))
) {
return false;
}
// Already reported under Language standard, with its extension fallback
// noted there -- no standard removes the need for the ones left here.
//
// Compared against the report's mode, not one inferred from the
// requirement. Inferring it from `modes` is right only while every entry
// reaching here is single-mode; one dual-mode entry with an extension
// would be judged against the C++ baseline during a C transpile.
return !ToolchainRequirementUtils.exceedsBaselineStandard(
entry.key,
mode,
);
});
if (unconditional.length === 0) return;
console.log("");
console.log(" Compiler");
for (const entry of unconditional) {
ResultPrinter.printLeaf(
"GNU/Clang extension",
ToolchainRequirementUtils.lookup(entry.key),
entry,
);
}
}
/**
* Requirements that need a platform library.
*
* Sibling arms of one feature are printed as alternatives rather than as
* separate costs: a critical section needs ARMv7-M *or* Arduino *or*
* avr-libc *or* CMSIS, decided by the compiler, and listing them as four
* independent requirements would misstate what the user has to provide.
*/
private static printPlatformSection(
reportable: readonly IRecordedRequirement[],
): void {
const withPlatform = reportable.filter(
(entry) =>
ToolchainRequirementUtils.lookup(entry.key).platformLib !== null,
);
if (withPlatform.length === 0) return;
console.log("");
console.log(" Platform library");
for (const [feature, group] of ResultPrinter.groupByFeature(withPlatform)) {
if (group.length === 1) {
const requirement = ToolchainRequirementUtils.lookup(group[0]!.key);
ResultPrinter.printLeaf(
requirement.platformLib ?? "",
requirement,
group[0]!,
);
continue;
}
ResultPrinter.printAlternatives(feature, group);
}
}
/** Bucket requirements by the feature they belong to, preserving order. */
private static groupByFeature(
entries: readonly IRecordedRequirement[],
): ReadonlyMap<string, IRecordedRequirement[]> {
const byFeature = new Map<string, IRecordedRequirement[]>();
for (const entry of entries) {
const feature = ToolchainRequirementUtils.lookup(entry.key).feature;
const group = byFeature.get(feature);
if (group === undefined) byFeature.set(feature, [entry]);
else group.push(entry);
}
return byFeature;
}
/**
* One feature satisfied by any of several platform libraries, chosen by the
* compiler rather than by the user. Printing them as separate requirements
* would tell an AVR user they need CMSIS.
*/
private static printAlternatives(
feature: string,
group: readonly IRecordedRequirement[],
): void {
console.log(` ${"one of, by target".padEnd(LABEL_WIDTH)}${feature}`);
for (const entry of group) {
const requirement = ToolchainRequirementUtils.lookup(entry.key);
const when =
requirement.condition === null
? "always"
: `when ${requirement.condition}`;
const extensions =
requirement.extensions.length > 0 ? " [GNU/Clang extension]" : "";
console.log(
` ${(requirement.platformLib ?? "").padEnd(LABEL_WIDTH - 2)}${when}${extensions}`,
);
}
ResultPrinter.printSites(group[0]!);
}
/** One requirement row plus the source sites that incurred it. */
private static printLeaf(
label: string,
requirement: IToolchainRequirement,
entry: IRecordedRequirement,
): void {
console.log(
` ${label.padEnd(LABEL_WIDTH)}${requirement.reason} - ${requirement.feature}`,
);
ResultPrinter.printSites(entry);
}
/**
* Where a requirement came from: up to three source locations, or what the
* user did to incur it when it has no single location.
*
* Some requirements are genuinely file-scoped rather than sited -- the
* `--debug` panic helpers are incurred by a compiler flag plus any clamp
* type, and the C++ initializer forms by a whole transpile mode. Attributing
* those to an arbitrary line would invent precision the requirement does not
* have, so the registry's `incurredBy` is shown instead. Reported in review
* of #1153, where the absence of any explanation was the real gap.
*/
private static printSites(entry: IRecordedRequirement): void {
const indent = " ".repeat(LABEL_WIDTH + 6);
const located = entry.sites.filter((site) => site.sourcePath.length > 0);
if (located.length === 0) {
const requirement = ToolchainRequirementUtils.lookup(entry.key);
console.log(`${indent}from ${requirement.incurredBy}`);
return;
}
for (const site of located.slice(0, SITE_LIMIT)) {
const path = DeclarationSite.displayPath(site.sourcePath);
const where = site.line === null ? path : `${path}:${site.line}`;
console.log(`${indent}${where}`);
}
if (located.length > SITE_LIMIT) {
console.log(`${indent}(+${located.length - SITE_LIMIT} more)`);
}
}
}
export default ResultPrinter;
|