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.
|
#Ignore byond config folder.
|
||||||
/cfg/**/*
|
/cfg/**/*
|
||||||
|
|
||||||
#Ignore rust-g and auxmos libraries which are compiled with scripts
|
# Ignore compiled linux libs in the root folder, e.g. librust_g.so
|
||||||
*.so
|
/*.so
|
||||||
/tools/build/binaries/**/*
|
|
||||||
|
|
||||||
#Ignore compiled files and other files generated during compilation.
|
#Ignore compiled files and other files generated during compilation.
|
||||||
*.mdme
|
*.mdme
|
||||||
|
*.mdme.*
|
||||||
*.dmb
|
*.dmb
|
||||||
*.rsc
|
*.rsc
|
||||||
|
*.m.dme
|
||||||
|
*.test.dme
|
||||||
*.lk
|
*.lk
|
||||||
*.int
|
*.int
|
||||||
*.backup
|
*.backup
|
||||||
@@ -53,27 +55,6 @@ __pycache__/
|
|||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$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
|
# PyInstaller
|
||||||
# Usually these files are written by a python script from a template
|
# 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.
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
@@ -81,8 +62,7 @@ var/
|
|||||||
*.spec
|
*.spec
|
||||||
|
|
||||||
# Installer logs
|
# Installer logs
|
||||||
pip-log.txt
|
pip-*.txt
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
# Unit test / coverage reports
|
||||||
htmlcov/
|
htmlcov/
|
||||||
@@ -92,7 +72,6 @@ htmlcov/
|
|||||||
.cache
|
.cache
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
*,cover
|
|
||||||
.hypothesis/
|
.hypothesis/
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
@@ -101,10 +80,6 @@ coverage.xml
|
|||||||
|
|
||||||
# Django stuff:
|
# Django stuff:
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
|
||||||
|
|
||||||
# Flask instance folder
|
|
||||||
instance/
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
# Scrapy stuff:
|
||||||
.scrapy
|
.scrapy
|
||||||
@@ -112,9 +87,6 @@ instance/
|
|||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
target/
|
|
||||||
|
|
||||||
# IPython Notebook
|
# IPython Notebook
|
||||||
.ipynb_checkpoints
|
.ipynb_checkpoints
|
||||||
|
|
||||||
@@ -127,10 +99,6 @@ celerybeat-schedule
|
|||||||
# dotenv
|
# dotenv
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# virtualenv
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
|
|
||||||
# IntelliJ IDEA / PyCharm (with plugin)
|
# IntelliJ IDEA / PyCharm (with plugin)
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
@@ -153,12 +121,6 @@ Desktop.ini
|
|||||||
# Recycle Bin used on file shares
|
# Recycle Bin used on file shares
|
||||||
$RECYCLE.BIN/
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
# Windows Installer files
|
|
||||||
#*.cab
|
|
||||||
#*.msi
|
|
||||||
#*.msm
|
|
||||||
#*.msp
|
|
||||||
|
|
||||||
# Windows shortcuts
|
# Windows shortcuts
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
@@ -199,11 +161,10 @@ Temporary Items
|
|||||||
|
|
||||||
#Visual studio stuff
|
#Visual studio stuff
|
||||||
*.vscode/*
|
*.vscode/*
|
||||||
!/.vscode/extensions.json
|
/tools/MapAtmosFixer/MapAtmosFixer/obj/*
|
||||||
tools/MapAtmosFixer/MapAtmosFixer/obj/*
|
/tools/MapAtmosFixer/MapAtmosFixer/bin/*
|
||||||
tools/MapAtmosFixer/MapAtmosFixer/bin/*
|
/tools/CreditsTool/bin/*
|
||||||
tools/CreditsTool/bin/*
|
/tools/CreditsTool/obj/*
|
||||||
tools/CreditsTool/obj/*
|
|
||||||
|
|
||||||
#GitHub Atom
|
#GitHub Atom
|
||||||
.atom-build.json
|
.atom-build.json
|
||||||
@@ -228,13 +189,10 @@ tools/CreditsTool/obj/*
|
|||||||
!/config/title_screens/images/exclude
|
!/config/title_screens/images/exclude
|
||||||
|
|
||||||
#Linux docker
|
#Linux docker
|
||||||
tools/LinuxOneShot/SetupProgram/obj/*
|
/tools/LinuxOneShot/SetupProgram/obj/*
|
||||||
tools/LinuxOneShot/SetupProgram/bin/*
|
/tools/LinuxOneShot/SetupProgram/bin/*
|
||||||
tools/LinuxOneShot/SetupProgram/.vs
|
/tools/LinuxOneShot/SetupProgram/.vs
|
||||||
tools/LinuxOneShot/Database
|
/tools/LinuxOneShot/Database
|
||||||
tools/LinuxOneShot/TGS_Config
|
/tools/LinuxOneShot/TGS_Config
|
||||||
tools/LinuxOneShot/TGS_Instances
|
/tools/LinuxOneShot/TGS_Instances
|
||||||
tools/LinuxOneShot/TGS_Logs
|
/tools/LinuxOneShot/TGS_Logs
|
||||||
|
|
||||||
# Common build tooling
|
|
||||||
!/tools/build
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
@call tools\build\build
|
@echo off
|
||||||
@pause
|
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.
|
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
|
## Dependencies
|
||||||
|
|
||||||
- On Windows, `BUILD.bat` will automatically install a private (vendored) copy of Node.
|
- 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?
|
## 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.
|
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
|
#!/bin/sh
|
||||||
|
|
||||||
#Build TGUI
|
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
exec ../bootstrap/node build.js "$@"
|
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
|
#!/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
|
* @file
|
||||||
* @copyright 2020 Aleksej Komarov
|
* @copyright 2021 Aleksej Komarov
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Change working directory to project root
|
import fs from 'fs';
|
||||||
process.chdir(require('path').resolve(__dirname, '../../'));
|
import { DreamDaemon, DreamMaker } from './lib/byond.js';
|
||||||
|
import { yarn } from './lib/yarn.js';
|
||||||
|
import Juke from './juke/index.js';
|
||||||
|
|
||||||
// Validate NodeJS version
|
Juke.chdir('../..', import.meta.url);
|
||||||
const NODE_VERSION = parseInt(process.versions.node.match(/(\d+)/)[1]);
|
Juke.setup({ file: import.meta.url }).then((code) => process.exit(code));
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const STANDARD_BUILD = "Standard Build"
|
const DME_NAME = 'tgstation';
|
||||||
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"
|
|
||||||
|
|
||||||
let BUILD_MODE = STANDARD_BUILD;
|
export const DefineParameter = new Juke.Parameter({
|
||||||
if (process.env.CBT_BUILD_MODE) {
|
type: 'string[]',
|
||||||
switch (process.env.CBT_BUILD_MODE) {
|
alias: 'D',
|
||||||
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.`)
|
|
||||||
|
|
||||||
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');
|
export const DmMapsIncludeTarget = new Juke.Target({
|
||||||
const { exec } = require('./cbt/process');
|
executes: async () => {
|
||||||
const { Task, runTasks } = require('./cbt/task');
|
const folders = [
|
||||||
const { regQuery } = require('./cbt/winreg');
|
...Juke.glob('_maps/RandomRuins/**/*.dmm'),
|
||||||
const fs = require('fs');
|
...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 => {
|
export const DmTarget = new Juke.Target({
|
||||||
const yarnPath = resolveGlob('./tgui/.yarn/releases/yarn-*.cjs')[0]
|
dependsOn: ({ get }) => [
|
||||||
.replace('/tgui/', '/');
|
get(DefineParameter).includes('ALL_MAPS') && DmMapsIncludeTarget,
|
||||||
return exec('node', [yarnPath, ...args], {
|
],
|
||||||
cwd: './tgui',
|
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 */
|
export const DmTestTarget = new Juke.Target({
|
||||||
const taskYarn = new Task('yarn')
|
dependsOn: ({ get }) => [
|
||||||
// The following dependencies skip what could be considered an important
|
get(DefineParameter).includes('ALL_MAPS') && DmMapsIncludeTarget,
|
||||||
// step in Yarn: it verifies the integrity of cache. With this setup, if
|
],
|
||||||
// cache ever becomes corrupted, your only option is to clean build.
|
executes: async ({ get }) => {
|
||||||
.depends('tgui/.yarn/+(cache|releases|plugins|sdks)/**/*')
|
const defines = get(DefineParameter);
|
||||||
.depends('tgui/**/package.json')
|
if (defines.length > 0) {
|
||||||
.depends('tgui/yarn.lock')
|
Juke.logger.info('Using defines:', defines.join(', '));
|
||||||
// Phony target (automatically created at the end of the task)
|
}
|
||||||
.provides('tgui/.yarn/install-target')
|
fs.copyFileSync(`${DME_NAME}.dme`, `${DME_NAME}.test.dme`);
|
||||||
.build(() => yarn(['install']));
|
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 */
|
export const YarnTarget = new Juke.Target({
|
||||||
const taskTgfont = new Task('tgfont')
|
inputs: [
|
||||||
.depends('tgui/.yarn/install-target')
|
'tgui/.yarn/+(cache|releases|plugins|sdks)/**/*',
|
||||||
.depends('tgui/packages/tgfont/**/*.+(js|cjs|svg)')
|
'tgui/**/package.json',
|
||||||
.depends('tgui/packages/tgfont/package.json')
|
'tgui/yarn.lock',
|
||||||
.provides('tgui/packages/tgfont/dist/tgfont.css')
|
],
|
||||||
.provides('tgui/packages/tgfont/dist/tgfont.eot')
|
outputs: [
|
||||||
.provides('tgui/packages/tgfont/dist/tgfont.woff2')
|
'tgui/.yarn/install-target',
|
||||||
.build(() => yarn(['workspace', 'tgfont', 'build']));
|
],
|
||||||
|
executes: async () => {
|
||||||
|
await yarn('install');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/** Builds tgui */
|
export const TgFontTarget = new Juke.Target({
|
||||||
const taskTgui = new Task('tgui')
|
dependsOn: [YarnTarget],
|
||||||
.depends('tgui/.yarn/install-target')
|
inputs: [
|
||||||
.depends('tgui/webpack.config.js')
|
'tgui/.yarn/install-target',
|
||||||
.depends('tgui/**/package.json')
|
'tgui/packages/tgfont/**/*.+(js|cjs|svg)',
|
||||||
.depends('tgui/packages/**/*.+(js|cjs|ts|tsx|scss)')
|
'tgui/packages/tgfont/package.json',
|
||||||
.provides('tgui/public/tgui.bundle.css')
|
],
|
||||||
.provides('tgui/public/tgui.bundle.js')
|
outputs: [
|
||||||
.provides('tgui/public/tgui-common.bundle.js')
|
'tgui/packages/tgfont/dist/tgfont.css',
|
||||||
.provides('tgui/public/tgui-panel.bundle.css')
|
'tgui/packages/tgfont/dist/tgfont.eot',
|
||||||
.provides('tgui/public/tgui-panel.bundle.js')
|
'tgui/packages/tgfont/dist/tgfont.woff2',
|
||||||
.build(async () => {
|
],
|
||||||
await yarn(['run', 'webpack-cli', '--mode=production']);
|
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.
|
* Prepends the defines to the .dme.
|
||||||
* Does not clean them up, as this is intended for TGS which
|
* Does not clean them up, as this is intended for TGS which
|
||||||
* clones new copies anyway.
|
* clones new copies anyway.
|
||||||
*/
|
*/
|
||||||
const taskPrependDefines = (...defines) => new Task('prepend-defines')
|
const prependDefines = (...defines) => {
|
||||||
.build(async () => {
|
const dmeContents = fs.readFileSync(`${DME_NAME}.dme`);
|
||||||
const dmeContents = fs.readFileSync(`${DME_NAME}.dme`);
|
const textToWrite = defines.map(define => `#define ${define}\n`);
|
||||||
const textToWrite = defines.map(define => `#define ${define}\n`);
|
fs.writeFileSync(`${DME_NAME}.dme`, `${textToWrite}\n${dmeContents}`);
|
||||||
fs.writeFileSync(`${DME_NAME}.dme`, `${textToWrite}\n${dmeContents}`);
|
};
|
||||||
});
|
|
||||||
|
|
||||||
const taskDm = (...injectedDefines) => new Task('dm')
|
export const TgsTarget = new Juke.Target({
|
||||||
.depends('_maps/map_files/generic/**')
|
dependsOn: [TguiTarget, TgFontTarget],
|
||||||
.depends('code/**')
|
executes: async () => {
|
||||||
.depends('goon/**')
|
Juke.logger.info('Prepending TGS define');
|
||||||
.depends('html/**')
|
prependDefines('TGS');
|
||||||
.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`]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Frontend
|
const TGS_MODE = process.env.CBT_BUILD_MODE === 'TGS';
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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`.
|
* Adapted from `tgui/packages/tgui-dev-server/winreg.js`.
|
||||||
*
|
*
|
||||||
* @file
|
* @file
|
||||||
* @copyright 2020 Aleksej Komarov
|
* @copyright 2021 Aleksej Komarov
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { exec } = require('child_process');
|
import { exec } from 'child_process';
|
||||||
const { promisify } = require('util');
|
import { promisify } from 'util';
|
||||||
|
|
||||||
const regQuery = async (path, key) => {
|
export const regQuery = async (path, key) => {
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,3 @@ const regQuery = async (path, key) => {
|
|||||||
return null;
|
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