Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance error handling to support third-party tools #509

Merged
merged 23 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c7cfa37
Enhance error handling to support third-party tools
rahulyadav-57 Jun 28, 2024
c46cc8a
Merge branch 'main' into enhance-error-handling
rahulyadav-57 Jun 28, 2024
17ce414
fix: remove unused consoleLogger and jest mocks
rahulyadav-57 Jun 28, 2024
f74838a
Merge branch 'main' into enhance-error-handling
rahulyadav-57 Jul 3, 2024
a457c81
Add TactErrorCollection type
rahulyadav-57 Jul 3, 2024
3029053
Address review comments
rahulyadav-57 Jul 5, 2024
f51875b
Merge branch 'main' into enhance-error-handling
rahulyadav-57 Jul 5, 2024
6d7e69f
feat: add `-q`/`--quiet` flags to suppress compiler log output
novusnota Jul 5, 2024
4e96bbd
chore: CHANGELOG
novusnota Jul 5, 2024
d8d1d2b
chore: formatting
novusnota Jul 5, 2024
0bb4e92
Update src/pipeline/build.ts
anton-trunov Jul 5, 2024
d10df50
Update src/pipeline/build.ts
anton-trunov Jul 5, 2024
9f7f119
Update scripts/prepare.ts
anton-trunov Jul 5, 2024
17999a7
Update src/pipeline/build.ts
anton-trunov Jul 5, 2024
701a340
Update src/pipeline/build.ts
anton-trunov Jul 5, 2024
824ac30
Update src/node.ts
anton-trunov Jul 5, 2024
f621b22
Update src/pipeline/build.ts
anton-trunov Jul 5, 2024
f1de083
Update src/pipeline/build.ts
anton-trunov Jul 5, 2024
94d8b94
Update scripts/prepare.ts
anton-trunov Jul 5, 2024
3d9970d
Merge branch 'main' into enhance-error-handling
anton-trunov Jul 5, 2024
576b354
fix prepare.ts
anton-trunov Jul 5, 2024
8d8b772
fix build.ts
anton-trunov Jul 5, 2024
b6dcbae
chore: simplified a bit more
novusnota Jul 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/tact
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ meowModule.then(
projectNames: cli.flags.projects ?? [],
additionalCliOptions: { mode },
})
.then((success) => {
.then((response) => {
novusnota marked this conversation as resolved.
Show resolved Hide resolved
// https://nodejs.org/docs/v20.12.1/api/process.html#exit-codes
process.exit(success ? 0 : 30);
process.exit(response.ok ? 0 : 30);
});
},
);
136 changes: 74 additions & 62 deletions scripts/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,82 +5,94 @@ import { FuncCompilationResult, funcCompile } from "../src/func/funcCompile";
import path from "path";
import { glob } from "glob";
import { verify } from "../src/verify";
import { consoleLogger } from "../src/logger";
import { Logger } from "../src/logger";
import { __DANGER__disableVersionNumber } from "../src/pipeline/version";

// Read cases
void (async () => {
// Disable version number in packages
__DANGER__disableVersionNumber();

// Compile projects
if (!(await run({ configPath: __dirname + "/../tact.config.json" }))) {
console.error("Tact projects compilation failed");
process.exit(1);
}
const logger = new Logger();

// Verify projects
for (const pkgPath of glob.sync(
path.normalize(
path.resolve(__dirname, "..", "examples", "output", "*.pkg"),
),
)) {
const res = await verify({ pkg: fs.readFileSync(pkgPath, "utf-8") });
if (!res.ok) {
console.error("Failed to verify " + pkgPath + ": " + res.error);
process.exit(1);
try {
// Compile projects
const compileResult = await run({
configPath: path.join(__dirname, "../tact.config.json"),
anton-trunov marked this conversation as resolved.
Show resolved Hide resolved
});
if (!compileResult.ok) {
throw new Error("Tact projects compilation failed");
}
}

// Compile func files
for (const p of [{ path: __dirname + "/../func/" }]) {
const recs = fs.readdirSync(p.path);
for (const r of recs) {
if (!r.endsWith(".fc")) {
continue;
// Verify projects
for (const pkgPath of glob.sync(
path.normalize(
path.resolve(__dirname, "..", "examples", "output", "*.pkg"),
),
)) {
const res = await verify({
pkg: fs.readFileSync(pkgPath, "utf-8"),
});
if (!res.ok) {
throw new Error(`Failed to verify ${pkgPath}: ${res.error}`);
}
}

// Precompile
console.log("Processing " + p.path + r);
let c: FuncCompilationResult;
try {
const stdlibPath = path.resolve(
__dirname,
"..",
"stdlib",
"stdlib.fc",
);
const stdlib = fs.readFileSync(stdlibPath, "utf-8");
const code = fs.readFileSync(p.path + r, "utf-8");
c = await funcCompile({
entries: [stdlibPath, p.path + r],
sources: [
{
path: stdlibPath,
content: stdlib,
},
{
path: p.path + r,
content: code,
},
],
logger: consoleLogger,
});
if (!c.ok) {
console.error(c.log);
process.exit(1);
// Compile func files
for (const p of [{ path: path.join(__dirname, "/../func/") }]) {
anton-trunov marked this conversation as resolved.
Show resolved Hide resolved
const recs = fs.readdirSync(p.path);
for (const r of recs) {
if (!r.endsWith(".fc")) {
continue;
}
} catch (e) {
console.error(e);
console.error("Failed");
process.exit(1);
}
fs.writeFileSync(p.path + r + ".fift", c.fift!);
fs.writeFileSync(p.path + r + ".cell", c.output!);

// Cell -> Fift decompiler
const source = decompileAll({ src: c.output! });
fs.writeFileSync(p.path + r + ".rev.fift", source);
// Precompile
logger.info(`Processing ${path.join(p.path + r)}`);
anton-trunov marked this conversation as resolved.
Show resolved Hide resolved
let c: FuncCompilationResult;
try {
const stdlibPath = path.resolve(
__dirname,
"..",
"stdlib",
"stdlib.fc",
);
const stdlib = fs.readFileSync(stdlibPath, "utf-8");
const code = fs.readFileSync(p.path + r, "utf-8");
c = await funcCompile({
entries: [stdlibPath, p.path + r],
sources: [
{
path: stdlibPath,
content: stdlib,
},
{
path: p.path + r,
content: code,
},
],
logger: logger,
});
if (!c.ok) {
logger.error(c.log);
throw new Error(
`FunC compilation failed for ${path.join(p.path, r)}`,
);
}
} catch (e) {
logger.error(e as Error);
logger.error(`Failed for ${path.join(p.path, r)}`);
throw e;
}
fs.writeFileSync(p.path + r + ".fift", c.fift!);
fs.writeFileSync(p.path + r + ".cell", c.output!);

// Cell -> Fift decompiler
const source = decompileAll({ src: c.output! });
fs.writeFileSync(p.path + r + ".rev.fift", source);
}
}
} catch (error) {
logger.error(error as Error);
process.exit(1);
}
})();
12 changes: 8 additions & 4 deletions src/browser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Config, verifyConfig } from "./config/parseConfig";
import { TactLogger } from "./logger";
import { Logger } from "./logger";
import { build } from "./pipeline/build";
import { createVirtualFileSystem } from "./vfs/createVirtualFileSystem";

export async function run(args: {
config: Config;
files: Record<string, string>;
logger?: TactLogger | null | undefined;
logger?: Logger;
}) {
// Verify config
const config = verifyConfig(args.config);
Expand All @@ -19,14 +19,18 @@ export async function run(args: {

// Compile
let success = true;
let errorCollection: Error[] = [];
for (const p of config.projects) {
const built = await build({
config: p,
project,
stdlib,
logger: args.logger,
});
success = success && built;
success = success && built.ok;
if (!built.ok) {
errorCollection = { ...errorCollection, ...built.error };
}
}
return success;
return { ok: success, error: errorCollection };
}
7 changes: 7 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,10 @@ export function idTextErr(
}
return `"${ident.text}"`;
}

export type TactErrorCollection =
| Error
| TactParseError
| TactCompilationError
| TactInternalCompilerError
| TactConstEvalError;
4 changes: 2 additions & 2 deletions src/func/funcCompile.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from "fs";
import path from "path";
import { consoleLogger } from "../logger";
import { Logger } from "../logger";
import { funcCompile } from "./funcCompile";
import files from "../imports/stdlib";

Expand All @@ -22,7 +22,7 @@ describe("funcCompile", () => {
},
{ path: "/small.fc", content: source },
],
logger: consoleLogger,
logger: new Logger(),
});
expect(res.ok).toBe(true);
});
Expand Down
7 changes: 3 additions & 4 deletions src/func/funcCompile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { TactLogger } from "../logger";
import { errorToString } from "../utils/errorToString";
import { Logger } from "../logger";

// Wasm Imports
// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand Down Expand Up @@ -60,7 +59,7 @@ type CompileResult =
export async function funcCompile(args: {
entries: string[];
sources: { path: string; content: string }[];
logger: TactLogger;
logger: Logger;
}): Promise<FuncCompilationResult> {
// Parameters
const files: string[] = args.entries;
Expand Down Expand Up @@ -181,7 +180,7 @@ export async function funcCompile(args: {
}
}
} catch (e) {
args.logger.error(errorToString(e));
args.logger.error(e as Error);
throw Error("Unexpected compiler response");
} finally {
for (const i of allocatedFunctions) {
Expand Down
92 changes: 85 additions & 7 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,87 @@
export type TactLogger = {
log: (message: string) => void;
error: (message: string) => void;
};
export enum LogLevel {
/** Logging is turned off */
NONE,
/** Logs only error messages */
ERROR,
/** Logs warning and error messages */
WARN,
/** Logs informational, warning, and error messages */
INFO,
/** Logs debugging, informational, warning, and error messages */
DEBUG,
}

type messageType = string | Error;

export const consoleLogger: TactLogger = {
log: console.log,
error: console.error,
export interface LogMethods {
debug: (message: messageType) => void;
info: (message: messageType) => void;
warn: (message: messageType) => void;
error: (message: messageType) => void;
}

const logLevelToMethodName: { [key in LogLevel]: keyof LogMethods | null } = {
[LogLevel.NONE]: null,
[LogLevel.ERROR]: "error",
[LogLevel.WARN]: "warn",
[LogLevel.INFO]: "info",
[LogLevel.DEBUG]: "debug",
};

function getLoggingMethod(level: LogLevel): keyof LogMethods | null {
return logLevelToMethodName[level];
}

export class Logger {
private level: LogLevel;
private logMethods: LogMethods;

constructor(level: LogLevel = LogLevel.INFO) {
this.level = level;
this.logMethods = {
debug: console.log,
info: console.log,
warn: console.warn,
error: console.error,
};
}

protected log(level: LogLevel, message: messageType) {
if (this.level === LogLevel.NONE) {
return;
}

if (message instanceof Error) {
message = message.stack ?? message.message;
} else {
message = message.toString();
}

if (level > this.level) return;

const loggingMethod = getLoggingMethod(level);
if (!loggingMethod) return;

this.logMethods[loggingMethod](message);
}

debug(message: messageType) {
this.log(LogLevel.DEBUG, message);
}

info(message: messageType) {
this.log(LogLevel.INFO, message);
}

warn(message: messageType) {
this.log(LogLevel.WARN, message);
}

error(message: messageType) {
this.log(LogLevel.ERROR, message);
}

setLevel(level: LogLevel) {
this.level = level;
}
}
Loading
Loading