mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 07:48:55 +00:00
cbt => juke
This commit is contained in:
76
.gitignore
vendored
76
.gitignore
vendored
@@ -7,14 +7,16 @@
|
||||
#Ignore byond config folder.
|
||||
/cfg/**/*
|
||||
|
||||
#Ignore rust-g and auxmos libraries which are compiled with scripts
|
||||
*.so
|
||||
/tools/build/binaries/**/*
|
||||
# Ignore compiled linux libs in the root folder, e.g. librust_g.so
|
||||
/*.so
|
||||
|
||||
#Ignore compiled files and other files generated during compilation.
|
||||
*.mdme
|
||||
*.mdme.*
|
||||
*.dmb
|
||||
*.rsc
|
||||
*.m.dme
|
||||
*.test.dme
|
||||
*.lk
|
||||
*.int
|
||||
*.backup
|
||||
@@ -53,27 +55,6 @@ __pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
#*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
@@ -81,8 +62,7 @@ var/
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
pip-*.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
@@ -92,7 +72,6 @@ htmlcov/
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
@@ -101,10 +80,6 @@ coverage.xml
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask instance folder
|
||||
instance/
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
@@ -112,9 +87,6 @@ instance/
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# IPython Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
@@ -127,10 +99,6 @@ celerybeat-schedule
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# IntelliJ IDEA / PyCharm (with plugin)
|
||||
.idea
|
||||
|
||||
@@ -153,12 +121,6 @@ Desktop.ini
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
#*.cab
|
||||
#*.msi
|
||||
#*.msm
|
||||
#*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
@@ -199,11 +161,10 @@ Temporary Items
|
||||
|
||||
#Visual studio stuff
|
||||
*.vscode/*
|
||||
!/.vscode/extensions.json
|
||||
tools/MapAtmosFixer/MapAtmosFixer/obj/*
|
||||
tools/MapAtmosFixer/MapAtmosFixer/bin/*
|
||||
tools/CreditsTool/bin/*
|
||||
tools/CreditsTool/obj/*
|
||||
/tools/MapAtmosFixer/MapAtmosFixer/obj/*
|
||||
/tools/MapAtmosFixer/MapAtmosFixer/bin/*
|
||||
/tools/CreditsTool/bin/*
|
||||
/tools/CreditsTool/obj/*
|
||||
|
||||
#GitHub Atom
|
||||
.atom-build.json
|
||||
@@ -228,13 +189,10 @@ tools/CreditsTool/obj/*
|
||||
!/config/title_screens/images/exclude
|
||||
|
||||
#Linux docker
|
||||
tools/LinuxOneShot/SetupProgram/obj/*
|
||||
tools/LinuxOneShot/SetupProgram/bin/*
|
||||
tools/LinuxOneShot/SetupProgram/.vs
|
||||
tools/LinuxOneShot/Database
|
||||
tools/LinuxOneShot/TGS_Config
|
||||
tools/LinuxOneShot/TGS_Instances
|
||||
tools/LinuxOneShot/TGS_Logs
|
||||
|
||||
# Common build tooling
|
||||
!/tools/build
|
||||
/tools/LinuxOneShot/SetupProgram/obj/*
|
||||
/tools/LinuxOneShot/SetupProgram/bin/*
|
||||
/tools/LinuxOneShot/SetupProgram/.vs
|
||||
/tools/LinuxOneShot/Database
|
||||
/tools/LinuxOneShot/TGS_Config
|
||||
/tools/LinuxOneShot/TGS_Instances
|
||||
/tools/LinuxOneShot/TGS_Logs
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
@call tools\build\build
|
||||
@pause
|
||||
@echo off
|
||||
call "%~dp0\tools\build\build.bat" %*
|
||||
pause
|
||||
|
||||
3
CLEAN.bat
Normal file
3
CLEAN.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
call "%~dp0\tools\build\build.bat" dist-clean
|
||||
pause
|
||||
3
RUN_SERVER.bat
Normal file
3
RUN_SERVER.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
call "%~dp0\tools\build\build.bat" server %*
|
||||
pause
|
||||
@@ -13,6 +13,14 @@ This build script is the recommended way to compile the game, including not only
|
||||
|
||||
The script will skip build steps whose inputs have not changed since the last run.
|
||||
|
||||
## Getting list of available targets
|
||||
|
||||
You can get a list of all targets that you can build by running the following command:
|
||||
|
||||
```
|
||||
tools/build/build --help
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- On Windows, `BUILD.bat` will automatically install a private (vendored) copy of Node.
|
||||
@@ -22,3 +30,5 @@ The script will skip build steps whose inputs have not changed since the last ru
|
||||
## Why?
|
||||
|
||||
We used to include compiled versions of the tgui JavaScript code in the Git repository so that the project could be compiled using BYOND only. These pre-compiled files tended to have merge conflicts for no good reason. Using a build script lets us avoid this problem, while keeping builds convenient for people who are not modifying tgui.
|
||||
|
||||
This build script is based on [Juke Build](https://github.com/stylemistake/juke-build) - please follow the link and read the documentation for the project to understand how it works and how to contribute to this build script.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
This directory is used to store temporary files to create binaries on linux
|
||||
@@ -1,6 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
#Build TGUI
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
exec ../bootstrap/node build.js "$@"
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
@"%~dp0\..\bootstrap\node" "%~dp0\build.js"
|
||||
@echo off
|
||||
"%~dp0\..\bootstrap\node.bat" --experimental-modules "%~dp0\build.js" %*
|
||||
|
||||
@@ -1,225 +1,293 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Build script for /tg/station 13 codebase.
|
||||
*
|
||||
* This script uses Juke Build, read the docs here:
|
||||
* https://github.com/stylemistake/juke-build
|
||||
*
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @copyright 2021 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// Change working directory to project root
|
||||
process.chdir(require('path').resolve(__dirname, '../../'));
|
||||
import fs from 'fs';
|
||||
import { DreamDaemon, DreamMaker } from './lib/byond.js';
|
||||
import { yarn } from './lib/yarn.js';
|
||||
import Juke from './juke/index.js';
|
||||
|
||||
// Validate NodeJS version
|
||||
const NODE_VERSION = parseInt(process.versions.node.match(/(\d+)/)[1]);
|
||||
const NODE_VERSION_TARGET = parseInt(require('fs')
|
||||
.readFileSync('dependencies.sh', 'utf-8')
|
||||
.match(/NODE_VERSION=(\d+)/)[1]);
|
||||
if (NODE_VERSION < NODE_VERSION_TARGET) {
|
||||
console.error('Your current Node.js version is out of date.');
|
||||
console.error('You have two options:');
|
||||
console.error(' a) Go to https://nodejs.org/ and install the latest LTS release of Node.js');
|
||||
console.error(' b) Uninstall Node.js (our build system automatically downloads one)');
|
||||
process.exit(1);
|
||||
}
|
||||
Juke.chdir('../..', import.meta.url);
|
||||
Juke.setup({ file: import.meta.url }).then((code) => process.exit(code));
|
||||
|
||||
const STANDARD_BUILD = "Standard Build"
|
||||
const TGS_BUILD = "TGS Build"
|
||||
const ALL_MAPS_BUILD = "CI All Maps Build"
|
||||
const TEST_RUN_BUILD = "CI Integration Tests Build"
|
||||
const NO_DM_BUILD = "Except DM Build"
|
||||
const DME_NAME = 'tgstation';
|
||||
|
||||
let BUILD_MODE = STANDARD_BUILD;
|
||||
if (process.env.CBT_BUILD_MODE) {
|
||||
switch (process.env.CBT_BUILD_MODE) {
|
||||
case "ALL_MAPS":
|
||||
BUILD_MODE = ALL_MAPS_BUILD
|
||||
break;
|
||||
case "TEST_RUN":
|
||||
BUILD_MODE = TEST_RUN_BUILD
|
||||
break;
|
||||
case "TGS":
|
||||
BUILD_MODE = TGS_BUILD
|
||||
break;
|
||||
case "NO_DM":
|
||||
BUILD_MODE = NO_DM_BUILD
|
||||
break;
|
||||
default:
|
||||
BUILD_MODE = process.env.CBT_BUILD_MODE
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log(`Starting CBT in ${BUILD_MODE} mode.`)
|
||||
export const DefineParameter = new Juke.Parameter({
|
||||
type: 'string[]',
|
||||
alias: 'D',
|
||||
});
|
||||
|
||||
const DME_NAME = 'tgstation'
|
||||
export const PortParameter = new Juke.Parameter({
|
||||
type: 'string',
|
||||
alias: 'p',
|
||||
});
|
||||
|
||||
// Main
|
||||
// --------------------------------------------------------
|
||||
export const CiParameter = new Juke.Parameter({
|
||||
type: 'boolean',
|
||||
});
|
||||
|
||||
const { resolveGlob, stat } = require('./cbt/fs');
|
||||
const { exec } = require('./cbt/process');
|
||||
const { Task, runTasks } = require('./cbt/task');
|
||||
const { regQuery } = require('./cbt/winreg');
|
||||
const fs = require('fs');
|
||||
export const DmMapsIncludeTarget = new Juke.Target({
|
||||
executes: async () => {
|
||||
const folders = [
|
||||
...Juke.glob('_maps/RandomRuins/**/*.dmm'),
|
||||
...Juke.glob('_maps/RandomZLevels/**/*.dmm'),
|
||||
...Juke.glob('_maps/shuttles/**/*.dmm'),
|
||||
...Juke.glob('_maps/templates/**/*.dmm'),
|
||||
];
|
||||
const content = folders
|
||||
.map((file) => file.replace('_maps/', ''))
|
||||
.map((file) => `#include "${file}"`)
|
||||
.join('\n') + '\n';
|
||||
fs.writeFileSync('_maps/templates.dm', content);
|
||||
},
|
||||
});
|
||||
|
||||
const yarn = args => {
|
||||
const yarnPath = resolveGlob('./tgui/.yarn/releases/yarn-*.cjs')[0]
|
||||
.replace('/tgui/', '/');
|
||||
return exec('node', [yarnPath, ...args], {
|
||||
cwd: './tgui',
|
||||
});
|
||||
};
|
||||
export const DmTarget = new Juke.Target({
|
||||
dependsOn: ({ get }) => [
|
||||
get(DefineParameter).includes('ALL_MAPS') && DmMapsIncludeTarget,
|
||||
],
|
||||
inputs: [
|
||||
'_maps/map_files/generic/**',
|
||||
'code/**',
|
||||
'goon/**',
|
||||
'html/**',
|
||||
'icons/**',
|
||||
'interface/**',
|
||||
`${DME_NAME}.dme`,
|
||||
],
|
||||
outputs: [
|
||||
`${DME_NAME}.dmb`,
|
||||
`${DME_NAME}.rsc`,
|
||||
],
|
||||
parameters: [DefineParameter],
|
||||
executes: async ({ get }) => {
|
||||
const defines = get(DefineParameter);
|
||||
if (defines.length > 0) {
|
||||
Juke.logger.info('Using defines:', defines.join(', '));
|
||||
}
|
||||
await DreamMaker(`${DME_NAME}.dme`, {
|
||||
defines: ['CBT', ...defines],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/** Installs all tgui dependencies */
|
||||
const taskYarn = new Task('yarn')
|
||||
// The following dependencies skip what could be considered an important
|
||||
// step in Yarn: it verifies the integrity of cache. With this setup, if
|
||||
// cache ever becomes corrupted, your only option is to clean build.
|
||||
.depends('tgui/.yarn/+(cache|releases|plugins|sdks)/**/*')
|
||||
.depends('tgui/**/package.json')
|
||||
.depends('tgui/yarn.lock')
|
||||
// Phony target (automatically created at the end of the task)
|
||||
.provides('tgui/.yarn/install-target')
|
||||
.build(() => yarn(['install']));
|
||||
export const DmTestTarget = new Juke.Target({
|
||||
dependsOn: ({ get }) => [
|
||||
get(DefineParameter).includes('ALL_MAPS') && DmMapsIncludeTarget,
|
||||
],
|
||||
executes: async ({ get }) => {
|
||||
const defines = get(DefineParameter);
|
||||
if (defines.length > 0) {
|
||||
Juke.logger.info('Using defines:', defines.join(', '));
|
||||
}
|
||||
fs.copyFileSync(`${DME_NAME}.dme`, `${DME_NAME}.test.dme`);
|
||||
await DreamMaker(`${DME_NAME}.test.dme`, {
|
||||
defines: ['CBT', 'CIBUILDING', ...defines],
|
||||
});
|
||||
Juke.rm('data/logs/ci', { recursive: true });
|
||||
await DreamDaemon(
|
||||
`${DME_NAME}.test.dmb`,
|
||||
'-close', '-trusted', '-verbose',
|
||||
'-params', 'log-directory=ci'
|
||||
);
|
||||
Juke.rm('*.test.*');
|
||||
try {
|
||||
const cleanRun = fs.readFileSync('data/logs/ci/clean_run.lk', 'utf-8');
|
||||
console.log(cleanRun);
|
||||
}
|
||||
catch (err) {
|
||||
Juke.logger.error('Test run was not clean, exiting');
|
||||
throw new Juke.ExitCode(1);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** Builds svg fonts */
|
||||
const taskTgfont = new Task('tgfont')
|
||||
.depends('tgui/.yarn/install-target')
|
||||
.depends('tgui/packages/tgfont/**/*.+(js|cjs|svg)')
|
||||
.depends('tgui/packages/tgfont/package.json')
|
||||
.provides('tgui/packages/tgfont/dist/tgfont.css')
|
||||
.provides('tgui/packages/tgfont/dist/tgfont.eot')
|
||||
.provides('tgui/packages/tgfont/dist/tgfont.woff2')
|
||||
.build(() => yarn(['workspace', 'tgfont', 'build']));
|
||||
export const YarnTarget = new Juke.Target({
|
||||
inputs: [
|
||||
'tgui/.yarn/+(cache|releases|plugins|sdks)/**/*',
|
||||
'tgui/**/package.json',
|
||||
'tgui/yarn.lock',
|
||||
],
|
||||
outputs: [
|
||||
'tgui/.yarn/install-target',
|
||||
],
|
||||
executes: async () => {
|
||||
await yarn('install');
|
||||
},
|
||||
});
|
||||
|
||||
/** Builds tgui */
|
||||
const taskTgui = new Task('tgui')
|
||||
.depends('tgui/.yarn/install-target')
|
||||
.depends('tgui/webpack.config.js')
|
||||
.depends('tgui/**/package.json')
|
||||
.depends('tgui/packages/**/*.+(js|cjs|ts|tsx|scss)')
|
||||
.provides('tgui/public/tgui.bundle.css')
|
||||
.provides('tgui/public/tgui.bundle.js')
|
||||
.provides('tgui/public/tgui-common.bundle.js')
|
||||
.provides('tgui/public/tgui-panel.bundle.css')
|
||||
.provides('tgui/public/tgui-panel.bundle.js')
|
||||
.build(async () => {
|
||||
await yarn(['run', 'webpack-cli', '--mode=production']);
|
||||
});
|
||||
export const TgFontTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
inputs: [
|
||||
'tgui/.yarn/install-target',
|
||||
'tgui/packages/tgfont/**/*.+(js|cjs|svg)',
|
||||
'tgui/packages/tgfont/package.json',
|
||||
],
|
||||
outputs: [
|
||||
'tgui/packages/tgfont/dist/tgfont.css',
|
||||
'tgui/packages/tgfont/dist/tgfont.eot',
|
||||
'tgui/packages/tgfont/dist/tgfont.woff2',
|
||||
],
|
||||
executes: async () => {
|
||||
await yarn('workspace', 'tgfont', 'build');
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
inputs: [
|
||||
'tgui/.yarn/install-target',
|
||||
'tgui/webpack.config.js',
|
||||
'tgui/**/package.json',
|
||||
'tgui/packages/**/*.+(js|cjs|ts|tsx|scss)',
|
||||
],
|
||||
outputs: [
|
||||
'tgui/public/tgui.bundle.css',
|
||||
'tgui/public/tgui.bundle.js',
|
||||
'tgui/public/tgui-panel.bundle.css',
|
||||
'tgui/public/tgui-panel.bundle.js',
|
||||
],
|
||||
executes: async () => {
|
||||
await yarn('webpack-cli', '--mode=production');
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiEslintTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: async ({ args }) => {
|
||||
await yarn(
|
||||
'eslint', 'packages',
|
||||
'--fix', '--ext', '.js,.cjs,.ts,.tsx',
|
||||
...args
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiTscTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: async () => {
|
||||
await yarn('tsc');
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiTestTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: async ({ args }) => {
|
||||
await yarn('jest', ...args);
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiLintTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget, TguiEslintTarget, TguiTscTarget, TguiTestTarget],
|
||||
});
|
||||
|
||||
export const TguiDevTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: async ({ args }) => {
|
||||
await yarn('node', 'packages/tgui-dev-server/index.js', ...args);
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiAnalyzeTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: async () => {
|
||||
await yarn('webpack-cli', '--mode=production', '--analyze');
|
||||
},
|
||||
});
|
||||
|
||||
export const TestTarget = new Juke.Target({
|
||||
dependsOn: [DmTestTarget, TguiTestTarget],
|
||||
});
|
||||
|
||||
export const LintTarget = new Juke.Target({
|
||||
dependsOn: [TguiLintTarget],
|
||||
});
|
||||
|
||||
export const BuildTarget = new Juke.Target({
|
||||
dependsOn: [TguiTarget, TgFontTarget, DmTarget],
|
||||
});
|
||||
|
||||
export const ServerTarget = new Juke.Target({
|
||||
dependsOn: [BuildTarget],
|
||||
executes: async ({ get }) => {
|
||||
const port = get(PortParameter) || '1337';
|
||||
await DreamDaemon(`${DME_NAME}.dmb`, port, '-trusted');
|
||||
},
|
||||
});
|
||||
|
||||
export const AllTarget = new Juke.Target({
|
||||
dependsOn: [TestTarget, LintTarget, BuildTarget],
|
||||
});
|
||||
|
||||
/**
|
||||
* Removes the immediate build junk to produce clean builds.
|
||||
*/
|
||||
export const CleanTarget = new Juke.Target({
|
||||
executes: async () => {
|
||||
Juke.rm('*.dmb');
|
||||
Juke.rm('*.rsc');
|
||||
Juke.rm('*.mdme');
|
||||
Juke.rm('*.mdme*');
|
||||
Juke.rm('*.m.*');
|
||||
Juke.rm('_maps/templates.dm');
|
||||
Juke.rm('tgui/public/.tmp', { recursive: true });
|
||||
Juke.rm('tgui/public/*.map');
|
||||
Juke.rm('tgui/public/*.chunk.*');
|
||||
Juke.rm('tgui/public/*.bundle.*');
|
||||
Juke.rm('tgui/public/*.hot-update.*');
|
||||
Juke.rm('tgui/packages/tgfont/dist', { recursive: true });
|
||||
Juke.rm('tgui/.yarn/cache', { recursive: true });
|
||||
Juke.rm('tgui/.yarn/unplugged', { recursive: true });
|
||||
Juke.rm('tgui/.yarn/webpack', { recursive: true });
|
||||
Juke.rm('tgui/.yarn/build-state.yml');
|
||||
Juke.rm('tgui/.yarn/install-state.gz');
|
||||
Juke.rm('tgui/.yarn/install-target');
|
||||
Juke.rm('tgui/.pnp.*');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Removes more junk at expense of much slower initial builds.
|
||||
*/
|
||||
export const DistCleanTarget = new Juke.Target({
|
||||
dependsOn: [CleanTarget],
|
||||
executes: async () => {
|
||||
Juke.logger.info('Cleaning up data/logs');
|
||||
Juke.rm('data/logs', { recursive: true });
|
||||
Juke.logger.info('Cleaning up bootstrap cache');
|
||||
Juke.rm('tools/bootstrap/.cache', { recursive: true });
|
||||
Juke.logger.info('Cleaning up global yarn cache');
|
||||
await yarn('cache', 'clean', '--all');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Prepends the defines to the .dme.
|
||||
* Does not clean them up, as this is intended for TGS which
|
||||
* clones new copies anyway.
|
||||
*/
|
||||
const taskPrependDefines = (...defines) => new Task('prepend-defines')
|
||||
.build(async () => {
|
||||
const dmeContents = fs.readFileSync(`${DME_NAME}.dme`);
|
||||
const textToWrite = defines.map(define => `#define ${define}\n`);
|
||||
fs.writeFileSync(`${DME_NAME}.dme`, `${textToWrite}\n${dmeContents}`);
|
||||
});
|
||||
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}`);
|
||||
};
|
||||
|
||||
const taskDm = (...injectedDefines) => new Task('dm')
|
||||
.depends('_maps/map_files/generic/**')
|
||||
.depends('code/**')
|
||||
.depends('goon/**')
|
||||
.depends('html/**')
|
||||
.depends('icons/**')
|
||||
.depends('interface/**')
|
||||
.depends(process.platform === 'win32' ? 'auxmos.*' : 'libauxmos.*')
|
||||
.depends('tgui/public/tgui.html')
|
||||
.depends('tgui/public/*.bundle.*')
|
||||
.depends(`${DME_NAME}.dme`)
|
||||
.provides(`${DME_NAME}.dmb`)
|
||||
.provides(`${DME_NAME}.rsc`)
|
||||
.build(async () => {
|
||||
const dmPath = await (async () => {
|
||||
// Search in array of paths
|
||||
const paths = [
|
||||
...((process.env.DM_EXE && process.env.DM_EXE.split(',')) || []),
|
||||
'C:\\Program Files\\BYOND\\bin\\dm.exe',
|
||||
'C:\\Program Files (x86)\\BYOND\\bin\\dm.exe',
|
||||
['reg', 'HKLM\\Software\\Dantom\\BYOND', 'installpath'],
|
||||
['reg', 'HKLM\\SOFTWARE\\WOW6432Node\\Dantom\\BYOND', 'installpath'],
|
||||
];
|
||||
const isFile = path => {
|
||||
try {
|
||||
const fstat = stat(path);
|
||||
return fstat && fstat.isFile();
|
||||
}
|
||||
catch (err) {}
|
||||
return false;
|
||||
};
|
||||
for (let path of paths) {
|
||||
// Resolve a registry key
|
||||
if (Array.isArray(path)) {
|
||||
const [type, ...args] = path;
|
||||
path = await regQuery(...args);
|
||||
}
|
||||
if (!path) {
|
||||
continue;
|
||||
}
|
||||
// Check if path exists
|
||||
if (isFile(path)) {
|
||||
return path;
|
||||
}
|
||||
if (isFile(path + '/dm.exe')) {
|
||||
return path + '/dm.exe';
|
||||
}
|
||||
if (isFile(path + '/bin/dm.exe')) {
|
||||
return path + '/bin/dm.exe';
|
||||
}
|
||||
}
|
||||
// Default paths
|
||||
return (
|
||||
process.platform === 'win32' && 'dm.exe'
|
||||
|| 'DreamMaker'
|
||||
);
|
||||
})();
|
||||
if (injectedDefines.length) {
|
||||
const injectedContent = injectedDefines
|
||||
.map(x => `#define ${x}\n`)
|
||||
.join('')
|
||||
// Create mdme file
|
||||
fs.writeFileSync(`${DME_NAME}.mdme`, injectedContent)
|
||||
// Add the actual dme content
|
||||
const dme_content = fs.readFileSync(`${DME_NAME}.dme`)
|
||||
fs.appendFileSync(`${DME_NAME}.mdme`, dme_content)
|
||||
await exec(dmPath, [`${DME_NAME}.mdme`]);
|
||||
// Rename dmb
|
||||
fs.renameSync(`${DME_NAME}.mdme.dmb`, `${DME_NAME}.dmb`)
|
||||
// Rename rsc
|
||||
fs.renameSync(`${DME_NAME}.mdme.rsc`, `${DME_NAME}.rsc`)
|
||||
// Remove mdme
|
||||
fs.unlinkSync(`${DME_NAME}.mdme`)
|
||||
}
|
||||
else {
|
||||
await exec(dmPath, [`${DME_NAME}.dme`]);
|
||||
}
|
||||
});
|
||||
export const TgsTarget = new Juke.Target({
|
||||
dependsOn: [TguiTarget, TgFontTarget],
|
||||
executes: async () => {
|
||||
Juke.logger.info('Prepending TGS define');
|
||||
prependDefines('TGS');
|
||||
},
|
||||
});
|
||||
|
||||
// Frontend
|
||||
let tasksToRun = [
|
||||
taskYarn,
|
||||
taskTgfont,
|
||||
taskTgui,
|
||||
];
|
||||
switch (BUILD_MODE) {
|
||||
case STANDARD_BUILD:
|
||||
tasksToRun.push(taskDm('CBT'));
|
||||
break;
|
||||
case TGS_BUILD:
|
||||
tasksToRun.push(taskPrependDefines('TGS'));
|
||||
break;
|
||||
case ALL_MAPS_BUILD:
|
||||
tasksToRun.push(taskDm('CBT','CIBUILDING','CITESTING','ALL_MAPS'));
|
||||
break;
|
||||
case TEST_RUN_BUILD:
|
||||
tasksToRun.push(taskDm('CBT','CIBUILDING'));
|
||||
break;
|
||||
case NO_DM_BUILD:
|
||||
break;
|
||||
default:
|
||||
console.error(`Unknown build mode : ${BUILD_MODE}`)
|
||||
break;
|
||||
}
|
||||
const TGS_MODE = process.env.CBT_BUILD_MODE === 'TGS';
|
||||
|
||||
runTasks(tasksToRun);
|
||||
export default TGS_MODE ? TgsTarget : BuildTarget;
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#Detect OS and use corresponding package manager for dependencies. Currently only works for arch, debian/ubuntu, and RHEL/fedora/CentOS
|
||||
if [[ -f '/etc/arch-release' ]]; then
|
||||
echo -ne '\n y' | sudo pacman --needed -Sy base-devel git curl nodejs unzip
|
||||
fi
|
||||
if [[ -f '/etc/debian version' ]]; then
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential git curl lib32z1 pkg-config libssl-dev:i386 libssl-dev nodejs unzip g++-multilib libc6-i386 libstdc++6:i386
|
||||
fi
|
||||
if [[ -f '/etc/centos-release' ]] || [[ -f '/etc/fedora-release' ]]; then #DNF should work for both of these
|
||||
sudo dnf --refresh install make automake gcc gcc-c++ kernel-devel git curl unzip glibc-devel.i686 openssl-devel.i686 libgcc.i686 libstdc++-devel.i686
|
||||
fi
|
||||
|
||||
cd binaries
|
||||
|
||||
#Install rust if not present
|
||||
if ! [ -x "$has_cargo" ]; then
|
||||
echo "Installing rust..."
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
. ~/.profile
|
||||
fi
|
||||
|
||||
#Download/update rust-g repo
|
||||
if [ ! -d "rust-g" ]; then
|
||||
echo "Cloning rust-g..."
|
||||
git clone https://github.com/tgstation/rust-g
|
||||
cd rust-g
|
||||
~/.cargo/bin/rustup target add i686-unknown-linux-gnu
|
||||
else
|
||||
echo "Fetching rust-g..."
|
||||
cd rust-g
|
||||
git fetch
|
||||
~/.cargo/bin/rustup target add i686-unknown-linux-gnu
|
||||
fi
|
||||
|
||||
#Compile and move rust-g binary to repo root
|
||||
echo "Deploying rust-g..."
|
||||
git checkout "$RUST_G_VERSION"
|
||||
env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --release --target=i686-unknown-linux-gnu
|
||||
mv target/i686-unknown-linux-gnu/release/librust_g.so ../../../../librust_g.so
|
||||
cd ..
|
||||
|
||||
#Download/update auxmos repo
|
||||
if [ ! -d "auxmos" ]; then
|
||||
echo "Cloning auxmos..."
|
||||
git clone https://github.com/Putnam3145/auxmos
|
||||
cd auxmos
|
||||
~/.cargo/bin/rustup target add i686-unknown-linux-gnu
|
||||
else
|
||||
echo "Fetching auxmos..."
|
||||
cd auxmos
|
||||
git fetch
|
||||
~/.cargo/bin/rustup target add i686-unknown-linux-gnu
|
||||
fi
|
||||
|
||||
#Compile and move auxmos binary to repo root
|
||||
echo "Deploying auxmos..."
|
||||
git checkout "$AUXMOS_VERSION"
|
||||
env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo rustc --release --target=i686-unknown-linux-gnu --features all_reaction_hooks,explosive_decompression -- -C target-cpu=native
|
||||
mv target/i686-unknown-linux-gnu/release/libauxmos.so ../../../../libauxmos.so
|
||||
cd ../..
|
||||
|
||||
#Install BYOND
|
||||
cd ../..
|
||||
./tools/ci/install_byond.sh
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
|
||||
cd tools/build
|
||||
|
||||
#Build TGUI
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
exec ../bootstrap/node build.js "$@"
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const glob = require('./glob');
|
||||
|
||||
class File {
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
get stat() {
|
||||
if (this._stat === undefined) {
|
||||
this._stat = stat(this.path);
|
||||
}
|
||||
return this._stat;
|
||||
}
|
||||
|
||||
exists() {
|
||||
return this.stat !== null;
|
||||
}
|
||||
|
||||
get mtime() {
|
||||
return this.stat && this.stat.mtime;
|
||||
}
|
||||
|
||||
touch() {
|
||||
const time = new Date();
|
||||
try {
|
||||
fs.utimesSync(this.path, time, time);
|
||||
}
|
||||
catch (err) {
|
||||
fs.closeSync(fs.openSync(this.path, 'w'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Glob {
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
toFiles() {
|
||||
const paths = glob.sync(this.path, {
|
||||
strict: false,
|
||||
silent: true,
|
||||
});
|
||||
return paths
|
||||
.map(path => new File(path))
|
||||
.filter(file => file.exists());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, source is newer than target.
|
||||
* @param {File[]} sources
|
||||
* @param {File[]} targets
|
||||
*/
|
||||
const compareFiles = (sources, targets) => {
|
||||
let bestSource = null;
|
||||
let bestTarget = null;
|
||||
for (const file of sources) {
|
||||
if (!bestSource || file.mtime > bestSource.mtime) {
|
||||
bestSource = file;
|
||||
}
|
||||
}
|
||||
for (const file of targets) {
|
||||
if (!file.exists()) {
|
||||
return `target '${file.path}' is missing`;
|
||||
}
|
||||
if (!bestTarget || file.mtime < bestTarget.mtime) {
|
||||
bestTarget = file;
|
||||
}
|
||||
}
|
||||
// Doesn't need rebuild if there is no source, but target exists.
|
||||
if (!bestSource) {
|
||||
if (bestTarget) {
|
||||
return false;
|
||||
}
|
||||
return 'no known sources or targets';
|
||||
}
|
||||
// Always needs a rebuild if no targets were specified (e.g. due to GLOB).
|
||||
if (!bestTarget) {
|
||||
return 'no targets were specified';
|
||||
}
|
||||
// Needs rebuild if source is newer than target
|
||||
if (bestSource.mtime > bestTarget.mtime) {
|
||||
return `source '${bestSource.path}' is newer than target '${bestTarget.path}'`;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns file stats for the provided path, or null if file is
|
||||
* not accessible.
|
||||
*/
|
||||
const stat = path => {
|
||||
try {
|
||||
return fs.statSync(path);
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a glob pattern and returns files that are safe
|
||||
* to call `stat` on.
|
||||
*/
|
||||
const resolveGlob = globPath => {
|
||||
const unsafePaths = glob.sync(globPath, {
|
||||
strict: false,
|
||||
silent: true,
|
||||
});
|
||||
const safePaths = [];
|
||||
for (let path of unsafePaths) {
|
||||
try {
|
||||
fs.statSync(path);
|
||||
safePaths.push(path);
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
return safePaths;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
File,
|
||||
Glob,
|
||||
compareFiles,
|
||||
stat,
|
||||
resolveGlob,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const { resolve: resolvePath } = require('path');
|
||||
const { stat } = require('./fs');
|
||||
|
||||
/**
|
||||
* @type {import('child_process').ChildProcessWithoutNullStreams}
|
||||
*/
|
||||
const children = new Set();
|
||||
|
||||
const killChildren = () => {
|
||||
for (const child of children) {
|
||||
child.kill('SIGTERM');
|
||||
children.delete(child);
|
||||
console.log('killed child process');
|
||||
}
|
||||
};
|
||||
|
||||
const trap = (signals, handler) => {
|
||||
let readline;
|
||||
if (process.platform === 'win32') {
|
||||
readline = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
}
|
||||
for (const signal of signals) {
|
||||
const handleSignal = () => handler(signal);
|
||||
if (signal === 'EXIT') {
|
||||
process.on('exit', handleSignal);
|
||||
continue;
|
||||
}
|
||||
if (readline) {
|
||||
readline.on('SIG' + signal, handleSignal);
|
||||
}
|
||||
process.on('SIG' + signal, handleSignal);
|
||||
}
|
||||
};
|
||||
|
||||
trap(['EXIT', 'BREAK', 'HUP', 'INT', 'TERM'], signal => {
|
||||
if (signal !== 'EXIT') {
|
||||
console.log('Received', signal);
|
||||
}
|
||||
killChildren();
|
||||
if (signal !== 'EXIT') {
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
const exceptionHandler = err => {
|
||||
console.log(err);
|
||||
killChildren();
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
process.on('unhandledRejection', exceptionHandler);
|
||||
process.on('uncaughtException', exceptionHandler);
|
||||
|
||||
class ExitError extends Error {}
|
||||
|
||||
/**
|
||||
* @param {string} executable
|
||||
* @param {string[]} args
|
||||
* @param {import('child_process').SpawnOptionsWithoutStdio} options
|
||||
*/
|
||||
const exec = (executable, args, options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// If executable exists relative to the current directory,
|
||||
// use that executable, otherwise spawn should fall back to
|
||||
// running it from PATH.
|
||||
if (stat(executable)) {
|
||||
executable = resolvePath(executable);
|
||||
}
|
||||
const child = spawn(executable, args, options);
|
||||
children.add(child);
|
||||
child.stdout.pipe(process.stdout, { end: false });
|
||||
child.stderr.pipe(process.stderr, { end: false });
|
||||
child.stdin.end();
|
||||
child.on('error', err => reject(err));
|
||||
child.on('exit', code => {
|
||||
children.delete(child);
|
||||
if (code !== 0) {
|
||||
const error = new ExitError('Process exited with code: ' + code);
|
||||
error.code = code;
|
||||
reject(error);
|
||||
}
|
||||
else {
|
||||
resolve(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
exec,
|
||||
ExitError,
|
||||
};
|
||||
@@ -1,107 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { compareFiles, Glob, File } = require('./fs');
|
||||
|
||||
class Task {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
this.sources = [];
|
||||
this.targets = [];
|
||||
this.script = null;
|
||||
}
|
||||
|
||||
depends(path) {
|
||||
if (path.includes('*')) {
|
||||
this.sources.push(new Glob(path));
|
||||
}
|
||||
else {
|
||||
this.sources.push(new File(path));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
provides(path) {
|
||||
if (path.includes('*')) {
|
||||
this.targets.push(new Glob(path));
|
||||
}
|
||||
else {
|
||||
this.targets.push(new File(path));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
build(script) {
|
||||
this.script = script;
|
||||
return this;
|
||||
}
|
||||
|
||||
async run() {
|
||||
/**
|
||||
* @returns {File[]}
|
||||
*/
|
||||
const getFiles = files => files
|
||||
.flatMap(file => {
|
||||
if (file instanceof Glob) {
|
||||
return file.toFiles();
|
||||
}
|
||||
if (file instanceof File) {
|
||||
return file;
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
// Normalize all our dependencies by converting globs to files
|
||||
const fileSources = getFiles(this.sources);
|
||||
const fileTargets = getFiles(this.targets);
|
||||
// Consider dependencies first, and skip the task if it
|
||||
// doesn't need a rebuild.
|
||||
let needsRebuild = 'no targets';
|
||||
if (fileTargets.length > 0) {
|
||||
needsRebuild = compareFiles(fileSources, fileTargets);
|
||||
if (!needsRebuild) {
|
||||
console.warn(` => Skipping '${this.name}' (up to date)`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!this.script) {
|
||||
return;
|
||||
}
|
||||
if (process.env.DEBUG && needsRebuild) {
|
||||
console.debug(` Reason: ${needsRebuild}`);
|
||||
}
|
||||
console.warn(` => Starting '${this.name}'`);
|
||||
const startedAt = Date.now();
|
||||
// Run the script
|
||||
await this.script();
|
||||
// Touch all targets so that they don't rebuild again
|
||||
if (fileTargets.length > 0) {
|
||||
for (const file of fileTargets) {
|
||||
file.touch();
|
||||
}
|
||||
}
|
||||
const time = ((Date.now() - startedAt) / 1000) + 's';
|
||||
console.warn(` => Finished '${this.name}' in ${time}`);
|
||||
}
|
||||
}
|
||||
|
||||
const runTasks = async tasks => {
|
||||
const startedAt = Date.now();
|
||||
// Run all if none of the tasks were specified in command line
|
||||
const runAll = !tasks.some(task => process.argv.includes(task.name));
|
||||
for (const task of tasks) {
|
||||
if (runAll || process.argv.includes(task.name)) {
|
||||
await task.run();
|
||||
}
|
||||
}
|
||||
const time = ((Date.now() - startedAt) / 1000) + 's';
|
||||
console.log(` => Done in ${time}`);
|
||||
process.exit();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Task,
|
||||
runTasks,
|
||||
};
|
||||
248
tools/build/juke/index.d.ts
vendored
Normal file
248
tools/build/juke/index.d.ts
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Generated by dts-bundle-generator v5.9.0
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import _chalk from 'chalk';
|
||||
import { SpawnOptionsWithoutStdio } from 'child_process';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
/**
|
||||
* Change the current working directory of the Node.js process.
|
||||
*
|
||||
* Second argument is a file (or directory), relative to which chdir will be
|
||||
* performed. This is usually `import.meta.url`.
|
||||
*/
|
||||
export declare const chdir: (directory: string, relativeTo?: string | undefined) => void;
|
||||
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 StringType = ("string" | "string[]" | "number" | "number[]" | "boolean" | "boolean[]");
|
||||
export declare type TypeByString<T extends StringType> = (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 StringType> = {
|
||||
/**
|
||||
* Parameter name, as it would be used in CLI.
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Parameter type, one of:
|
||||
* - `string`
|
||||
* - `string[]`
|
||||
* - `number`
|
||||
* - `number[]`
|
||||
* - `boolean`
|
||||
* - `boolean[]`
|
||||
*/
|
||||
type: T;
|
||||
/**
|
||||
* Short flag for use in CLI, can only be a single character.
|
||||
*/
|
||||
alias?: string;
|
||||
};
|
||||
export interface Parameter<T extends ParameterType = ParameterType> {
|
||||
type: StringType;
|
||||
name?: string;
|
||||
alias?: string;
|
||||
__internalType?: T;
|
||||
isString(): this is Parameter<string | string[]>;
|
||||
isNumber(): this is Parameter<number | number[]>;
|
||||
isBoolean(): this is Parameter<boolean | boolean[]>;
|
||||
isArray(): this is Parameter<string[] | number[] | boolean[]>;
|
||||
toKebabCase(): string | undefined;
|
||||
toConstCase(): string | undefined;
|
||||
toCamelCase(): string | undefined;
|
||||
}
|
||||
export declare type ParameterCtor = {
|
||||
new <T extends StringType>(config: ParameterConfig<T>): Parameter<TypeByString<T>>;
|
||||
};
|
||||
export declare const Parameter: ParameterCtor;
|
||||
export declare type ParameterCreator = <T extends StringType>(config: ParameterConfig<T>) => Parameter<TypeByString<T>>;
|
||||
export declare const createParameter: ParameterCreator;
|
||||
export declare type ExecutionContext = {
|
||||
/** Get parameter value. */
|
||||
get: <T extends ParameterType>(parameter: Parameter<T>) => (T extends Array<unknown> ? T : T | null);
|
||||
args: string[];
|
||||
};
|
||||
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 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 class Target {
|
||||
name?: string;
|
||||
dependsOn: DependsOn;
|
||||
executes?: ExecutesFn;
|
||||
inputs: FileIo;
|
||||
outputs: FileIo;
|
||||
parameters: Parameter[];
|
||||
onlyWhen?: OnlyWhenFn;
|
||||
constructor(target: TargetConfig);
|
||||
}
|
||||
export declare type TargetCreator = (target: TargetConfig) => Target;
|
||||
export declare const createTarget: TargetCreator;
|
||||
export declare type RunnerConfig = {
|
||||
targets?: Target[];
|
||||
default?: Target;
|
||||
parameters?: Parameter[];
|
||||
singleTarget?: boolean;
|
||||
};
|
||||
export declare const runner: {
|
||||
config: RunnerConfig;
|
||||
targets: Target[];
|
||||
parameters: Parameter[];
|
||||
workers: Worker[];
|
||||
configure(config: RunnerConfig): void;
|
||||
start(): Promise<number>;
|
||||
};
|
||||
declare class Worker {
|
||||
readonly target: Target;
|
||||
readonly context: ExecutionContext;
|
||||
readonly dependsOn: Target[];
|
||||
dependencies: Set<Target>;
|
||||
generator?: AsyncGenerator;
|
||||
emitter: EventEmitter;
|
||||
hasFailed: boolean;
|
||||
constructor(target: Target, context: ExecutionContext, dependsOn: Target[]);
|
||||
resolveDependency(target: Target): void;
|
||||
rejectDependency(target: Target): void;
|
||||
start(): void;
|
||||
onFinish(fn: () => void): void;
|
||||
onFail(fn: () => void): void;
|
||||
private debugLog;
|
||||
private 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>;
|
||||
/**
|
||||
* Unix style pathname pattern expansion.
|
||||
*
|
||||
* Perform a search matching a specified pattern according to the rules of
|
||||
* the `glob` npm package. Path can be either absolute or relative, and can
|
||||
* contain shell-style wildcards. Broken symlinks are included in the results
|
||||
* (as in the shell). Whether or not the results are sorted depends on the
|
||||
* file system.
|
||||
*
|
||||
* @returns A possibly empty list of file paths.
|
||||
*/
|
||||
export declare const glob: (globPath: string) => string[];
|
||||
export declare type RmOptions = {
|
||||
/**
|
||||
* If true, perform a recursive directory removal.
|
||||
*/
|
||||
recursive?: boolean;
|
||||
/**
|
||||
* If true, exceptions will be ignored if file or directory does not exist.
|
||||
*/
|
||||
force?: boolean;
|
||||
};
|
||||
/**
|
||||
* Removes files and directories (synchronously). Supports globs.
|
||||
*/
|
||||
export declare const rm: (path: string, options?: RmOptions) => void;
|
||||
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 type SetupConfig = {
|
||||
file: string;
|
||||
/**
|
||||
* If true, CLI will only accept a single target to run and will receive all
|
||||
* passed arguments as is (not only flags).
|
||||
*/
|
||||
singleTarget?: boolean;
|
||||
};
|
||||
/**
|
||||
* 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: SetupConfig) => Promise<number>;
|
||||
export declare const sleep: (time: number) => Promise<unknown>;
|
||||
|
||||
export {};
|
||||
5128
tools/build/juke/index.js
Normal file
5128
tools/build/juke/index.js
Normal file
File diff suppressed because it is too large
Load Diff
4
tools/build/juke/package.json
Normal file
4
tools/build/juke/package.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "commonjs"
|
||||
}
|
||||
115
tools/build/lib/byond.js
Normal file
115
tools/build/lib/byond.js
Normal file
@@ -0,0 +1,115 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import Juke from '../juke/index.js';
|
||||
import { regQuery } from './winreg.js';
|
||||
|
||||
/**
|
||||
* Cached path to DM compiler
|
||||
*/
|
||||
let dmPath;
|
||||
|
||||
const getDmPath = async () => {
|
||||
if (dmPath) {
|
||||
return dmPath;
|
||||
}
|
||||
dmPath = await (async () => {
|
||||
// Search in array of paths
|
||||
const paths = [
|
||||
...((process.env.DM_EXE && process.env.DM_EXE.split(',')) || []),
|
||||
'C:\\Program Files\\BYOND\\bin\\dm.exe',
|
||||
'C:\\Program Files (x86)\\BYOND\\bin\\dm.exe',
|
||||
['reg', 'HKLM\\Software\\Dantom\\BYOND', 'installpath'],
|
||||
['reg', 'HKLM\\SOFTWARE\\WOW6432Node\\Dantom\\BYOND', 'installpath'],
|
||||
];
|
||||
const isFile = path => {
|
||||
try {
|
||||
return fs.statSync(path).isFile();
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
for (let path of paths) {
|
||||
// Resolve a registry key
|
||||
if (Array.isArray(path)) {
|
||||
const [type, ...args] = path;
|
||||
path = await regQuery(...args);
|
||||
}
|
||||
if (!path) {
|
||||
continue;
|
||||
}
|
||||
// Check if path exists
|
||||
if (isFile(path)) {
|
||||
return path;
|
||||
}
|
||||
if (isFile(path + '/dm.exe')) {
|
||||
return path + '/dm.exe';
|
||||
}
|
||||
if (isFile(path + '/bin/dm.exe')) {
|
||||
return path + '/bin/dm.exe';
|
||||
}
|
||||
}
|
||||
// Default paths
|
||||
return (
|
||||
process.platform === 'win32' && 'dm.exe'
|
||||
|| 'DreamMaker'
|
||||
);
|
||||
})();
|
||||
return dmPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} dmeFile
|
||||
* @param {{ defines?: string[] }} options
|
||||
*/
|
||||
export const DreamMaker = async (dmeFile, options = {}) => {
|
||||
const dmPath = await getDmPath();
|
||||
// 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}.m.dme`, injectedContent);
|
||||
const dmeContent = fs.readFileSync(`${dmeBaseName}.dme`);
|
||||
fs.appendFileSync(`${dmeBaseName}.m.dme`, dmeContent);
|
||||
await Juke.exec(dmPath, [`${dmeBaseName}.m.dme`]);
|
||||
fs.writeFileSync(`${dmeBaseName}.dmb`, fs.readFileSync(`${dmeBaseName}.m.dmb`));
|
||||
fs.writeFileSync(`${dmeBaseName}.rsc`, fs.readFileSync(`${dmeBaseName}.m.rsc`));
|
||||
fs.unlinkSync(`${dmeBaseName}.m.dmb`);
|
||||
fs.unlinkSync(`${dmeBaseName}.m.rsc`);
|
||||
fs.unlinkSync(`${dmeBaseName}.m.dme`);
|
||||
}
|
||||
else {
|
||||
await Juke.exec(dmPath, [dmeFile]);
|
||||
}
|
||||
};
|
||||
|
||||
export const DreamDaemon = async (dmbFile, ...args) => {
|
||||
const dmPath = await getDmPath();
|
||||
const baseDir = path.dirname(dmPath);
|
||||
const ddExeName = process.platform === 'win32' ? 'dd.exe' : 'DreamDaemon';
|
||||
const ddExePath = baseDir === '.' ? ddExeName : path.join(baseDir, ddExeName);
|
||||
return Juke.exec(ddExePath, [dmbFile, ...args]);
|
||||
};
|
||||
@@ -4,14 +4,14 @@
|
||||
* Adapted from `tgui/packages/tgui-dev-server/winreg.js`.
|
||||
*
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @copyright 2021 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { exec } = require('child_process');
|
||||
const { promisify } = require('util');
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const regQuery = async (path, key) => {
|
||||
export const regQuery = async (path, key) => {
|
||||
if (process.platform !== 'win32') {
|
||||
return null;
|
||||
}
|
||||
@@ -40,7 +40,3 @@ const regQuery = async (path, key) => {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
regQuery,
|
||||
};
|
||||
13
tools/build/lib/yarn.js
Normal file
13
tools/build/lib/yarn.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import Juke from '../juke/index.js';
|
||||
|
||||
let yarnPath;
|
||||
|
||||
export const yarn = (...args) => {
|
||||
if (!yarnPath) {
|
||||
yarnPath = Juke.glob('./tgui/.yarn/releases/*.cjs')[0]
|
||||
.replace('/tgui/', '/');
|
||||
}
|
||||
return Juke.exec('node', [yarnPath, ...args], {
|
||||
cwd: './tgui',
|
||||
});
|
||||
};
|
||||
4
tools/build/package.json
Normal file
4
tools/build/package.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
Reference in New Issue
Block a user