Skip to content

Commit

Permalink
Stop compiling DM if compiler outputs are locked. (tgstation#60022)
Browse files Browse the repository at this point in the history
Basically, saves developer's time by yelling that the compiler can't write to dmb/rsc, because they are locked by Dream Daemon.

Added myself as a code owner for /tools/build.
  • Loading branch information
stylemistake authored Jul 7, 2021
1 parent 704ff13 commit 6eacbde
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 260 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
/code/controllers/subsystem/timer.dm @MrStonedOne
/code/controllers/configuration/entries @MrStonedOne
/config/ @MrStonedOne
/tools/build/ @MrStonedOne

# ninjanomnom

Expand Down Expand Up @@ -84,6 +83,7 @@
/icons/ @Twaticus @ShizCalev @Krysonism
/code/controllers/subsystem/air.dm @LemonInTheDark @MrStonedOne
/_maps/ @EOBGames @ShizCalev @Maurukas
/tools/build/ @MrStonedOne @stylemistake
/tools/LinuxOneShot/ @Cyberboss @MrStonedOne
/tools/tgs4_scripts/ @Cyberboss @MrStonedOne

Expand Down
6 changes: 3 additions & 3 deletions code/_compile_options.dm
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
#define MAX_ATOM_OVERLAYS 100

#if !defined(CBT) && !defined(SPACEMAN_DMM)
#warn "Building with Dream Maker is no longer supported and will result in errors."
#warn "In order to build, run BUILD.bat in the root directory."
#warn "Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build."
#warn Building with Dream Maker is no longer supported and will result in errors.
#warn In order to build, run BUILD.bat in the root directory.
#warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build.
#endif
2 changes: 1 addition & 1 deletion tools/build/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ const DefaultTarget = Juke.createTarget({
* Does not clean them up, as this is intended for TGS which
* clones new copies anyway.
*/
const prependDefines = (...defines) => {
const prependDefines = (...defines) => {
const dmeContents = fs.readFileSync(`${DME_NAME}.dme`);
const textToWrite = defines.map(define => `#define ${define}\n`);
fs.writeFileSync(`${DME_NAME}.dme`, `${textToWrite}\n${dmeContents}`);
Expand Down
44 changes: 34 additions & 10 deletions tools/build/cbt/dm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { exec } = require('../juke');
const Juke = require('../juke');
const { stat } = require('./fs');
const { regQuery } = require('./winreg');
const fs = require('fs');
Expand All @@ -15,6 +15,7 @@ let dmPath;
* @param {{ defines?: string[] }} options
*/
const dm = async (dmeFile, options = {}) => {
// Get path to DM compiler
if (!dmPath) {
dmPath = await (async () => {
// Search in array of paths
Expand Down Expand Up @@ -60,22 +61,45 @@ const dm = async (dmeFile, options = {}) => {
);
})();
}
const { defines } = options;
// Get project basename
const dmeBaseName = dmeFile.replace(/\.dme$/, '');
// Make sure output files are writable
const testOutputFile = (name) => {
try {
fs.closeSync(fs.openSync(name, 'r+'));
}
catch (err) {
if (err && err.code === 'ENOENT') {
return;
}
if (err && err.code === 'EBUSY') {
Juke.logger.error(`File '${name}' is locked by the DreamDaemon process.`);
Juke.logger.error(`Stop the currently running server and try again.`);
throw new Juke.ExitCode(1);
}
throw err;
}
};
testOutputFile(`${dmeBaseName}.dmb`);
testOutputFile(`${dmeBaseName}.rsc`);
// Compile
const { defines } = options;
if (defines && defines.length > 0) {
const injectedContent = defines
.map(x => `#define ${x}\n`)
.join('');
fs.writeFileSync(`${dmeBaseName}.mdme`, injectedContent)
const dmeContent = fs.readFileSync(`${dmeBaseName}.dme`)
fs.appendFileSync(`${dmeBaseName}.mdme`, dmeContent)
await exec(dmPath, [`${dmeBaseName}.mdme`]);
fs.renameSync(`${dmeBaseName}.mdme.dmb`, `${dmeBaseName}.dmb`)
fs.renameSync(`${dmeBaseName}.mdme.rsc`, `${dmeBaseName}.rsc`)
fs.unlinkSync(`${dmeBaseName}.mdme`)
fs.writeFileSync(`${dmeBaseName}.mdme`, injectedContent);
const dmeContent = fs.readFileSync(`${dmeBaseName}.dme`);
fs.appendFileSync(`${dmeBaseName}.mdme`, dmeContent);
await Juke.exec(dmPath, [`${dmeBaseName}.mdme`]);
fs.writeFileSync(`${dmeBaseName}.dmb`, fs.readFileSync(`${dmeBaseName}.mdme.dmb`));
fs.writeFileSync(`${dmeBaseName}.rsc`, fs.readFileSync(`${dmeBaseName}.mdme.rsc`));
fs.unlinkSync(`${dmeBaseName}.mdme.dmb`);
fs.unlinkSync(`${dmeBaseName}.mdme.rsc`);
fs.unlinkSync(`${dmeBaseName}.mdme`);
}
else {
await exec(dmPath, dmeFile);
await Juke.exec(dmPath, [dmeFile]);
}
};

Expand Down
18 changes: 0 additions & 18 deletions tools/build/juke/argparse.d.ts

This file was deleted.

6 changes: 0 additions & 6 deletions tools/build/juke/exec.d.ts

This file was deleted.

30 changes: 0 additions & 30 deletions tools/build/juke/fs.d.ts

This file was deleted.

177 changes: 167 additions & 10 deletions tools/build/juke/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,180 @@
import chalk from 'chalk';
import glob from 'glob';
import { exec } from './exec';
import { logger } from './logger';
import { createParameter as _createParameter } from './parameter';
import { RunnerConfig } from './runner';
import { createTarget as _createTarget } from './target';
export { exec, chalk, glob, logger };
// Generated by dts-bundle-generator v5.9.0

/// <reference types="node" />

import _chalk from 'chalk';
import { SpawnOptionsWithoutStdio } from 'child_process';

export declare class ExitCode extends Error {
code: number | null;
signal: string | null;
constructor(code: number | null, signal?: string | null);
}
export declare type ExecOptions = SpawnOptionsWithoutStdio & {
/**
* If `true`, this exec call will not pipe its output to stdio.
* @default false
*/
silent?: boolean;
/**
* Throw an exception on non-zero exit code.
* @default true
*/
throw?: boolean;
};
export declare type ExecReturn = {
/** Exit code of the program. */
code: number | null;
/** Signal received by the program which caused it to exit. */
signal: NodeJS.Signals | null;
/** Output collected from `stdout` */
stdout: string;
/** Output collected from `stderr` */
stderr: string;
/** A combined output collected from `stdout` and `stderr`. */
combined: string;
};
export declare const exec: (executable: string, args?: string[], options?: ExecOptions) => Promise<ExecReturn>;
export declare const logger: {
log: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
action: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
info: (...args: unknown[]) => void;
debug: (...args: unknown[]) => void;
};
export declare type ParameterType = (string | string[] | number | number[] | boolean | boolean[]);
export declare type ParameterStringType = ("string" | "string[]" | "number" | "number[]" | "boolean" | "boolean[]");
export declare type ParameterTypeByString<T extends ParameterStringType> = (T extends "string" ? string : T extends "string[]" ? string[] : T extends "number" ? number : T extends "number[]" ? number[] : T extends "boolean" ? boolean : T extends "boolean[]" ? boolean[] : never);
export declare type ParameterConfig<T extends ParameterStringType> = {
/**
* Parameter name, in "camelCase".
*/
readonly name: string;
/**
* Parameter type, one of:
* - `string`
* - `string[]`
* - `number`
* - `number[]`
* - `boolean`
* - `boolean[]`
*/
readonly type: T;
/**
* Short flag for use in CLI, can only be a single character.
*/
readonly alias?: string;
};
export declare type ParameterCreator = <T extends ParameterStringType>(config: ParameterConfig<T>) => Parameter<ParameterTypeByString<T>>;
declare class Parameter<T extends ParameterType = any> {
readonly name: string;
readonly type: ParameterStringType;
readonly alias?: string | undefined;
constructor(name: string, type: ParameterStringType, alias?: string | undefined);
isString(): T extends string | string[] ? true : false;
isNumber(): T extends number | number[] ? true : false;
isBoolean(): T extends boolean | boolean[] ? true : false;
isArray(): T extends Array<unknown> ? true : false;
toKebabCase(): string;
toConstCase(): string;
toCamelCase(): string;
}
export declare type ExecutionContext = {
/** Get parameter value. */
get: <T extends ParameterType>(parameter: Parameter<T>) => (T extends Array<unknown> ? T : T | null);
};
export declare type BooleanLike = boolean | null | undefined;
export declare type WithExecutionContext<R> = (context: ExecutionContext) => R | Promise<R>;
export declare type WithOptionalExecutionContext<R> = R | WithExecutionContext<R>;
export declare type DependsOn = WithOptionalExecutionContext<(Target | BooleanLike)[]>;
export declare type ExecutesFn = WithExecutionContext<unknown>;
export declare type OnlyWhenFn = WithExecutionContext<BooleanLike>;
export declare type FileIo = WithOptionalExecutionContext<(string | BooleanLike)[]>;
export declare type Target = {
name: string;
dependsOn: DependsOn;
executes?: ExecutesFn;
inputs: FileIo;
outputs: FileIo;
parameters: Parameter[];
onlyWhen?: OnlyWhenFn;
};
export declare type TargetConfig = {
/**
* Target name. This parameter is required.
*/
name: string;
/**
* Dependencies for this target. They will be ran before executing this
* target, and may run in parallel.
*/
dependsOn?: DependsOn;
/**
* Function that is delegated to the execution engine for building this
* target. It is normally an async function, which accepts a single
* argument - execution context (contains `get` for interacting with
* parameters).
*
* @example
* executes: async ({ get }) => {
* console.log(get(Parameter));
* },
*/
executes?: ExecutesFn;
/**
* Files that are consumed by this target.
*/
inputs?: FileIo;
/**
* Files that are produced by this target. Additionally, they are also
* touched every time target finishes executing in order to stop
* this target from re-running.
*/
outputs?: FileIo;
/**
* Parameters that are local to this task. Can be retrieved via `get`
* in the executor function.
*/
parameters?: Parameter[];
/**
* Target will run only when this function returns true. It accepts a
* single argument - execution context.
*/
onlyWhen?: OnlyWhenFn;
};
export declare type TargetCreator = (target: TargetConfig) => Target;
export declare type RunnerConfig = {
targets?: Target[];
default?: Target;
parameters?: Parameter[];
};
export declare const chalk: _chalk.Chalk & _chalk.ChalkFunction & {
supportsColor: false | _chalk.ColorSupport;
Level: _chalk.Level;
Color: ("black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright") | ("bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright");
ForegroundColor: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright";
BackgroundColor: "bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright";
Modifiers: "bold" | "reset" | "dim" | "italic" | "underline" | "inverse" | "hidden" | "strikethrough" | "visible";
stderr: _chalk.Chalk & {
supportsColor: false | _chalk.ColorSupport;
};
};
export declare const glob: typeof import("glob");
/**
* Configures Juke Build and starts executing targets.
*
* @param config Juke Build configuration.
* @returns Exit code of the whole runner process.
*/
export declare const setup: (config?: RunnerConfig) => Promise<number>;
export declare const createTarget: typeof _createTarget;
export declare const createParameter: typeof _createParameter;
export declare const createTarget: TargetCreator;
export declare const createParameter: ParameterCreator;
export declare const sleep: (time: number) => Promise<unknown>;
/**
* Resolves a glob pattern and returns files that are safe
* to call `stat` on.
*/
export declare const resolveGlob: (globPath: string) => string[];

export {};
Loading

0 comments on commit 6eacbde

Please sign in to comment.