494 lines
13 KiB
JavaScript
494 lines
13 KiB
JavaScript
/**
|
|
* XProject Tools Module ...
|
|
* a module for handle MQL5 Projects Compile and Packing ...
|
|
*
|
|
* Maintainer
|
|
*
|
|
* Hadi Khazaee Asl <https://saherelm.ir> (hadi_khazaee_asl@yahoo.com)
|
|
*/
|
|
|
|
//
|
|
// Import Requirements ...
|
|
const execSync = require("child_process").execSync;
|
|
const XFileTools = require("./Documents/JsModules/x-file.tools");
|
|
const XShellTools = require("./Documents/JsModules/x-shell.tools");
|
|
const XColorTools = require("./Documents/JsModules/x-color.tools");
|
|
const XTypeTools = require("./Documents/JsModules/x-type-detector.tools");
|
|
|
|
//
|
|
// Definitions ...
|
|
const packsPath = XFileTools.joinPath(".", "Packs");
|
|
const workspacePaths = [
|
|
"Classes", // Required Classes
|
|
"Experts", // Expert Advisers
|
|
"Helpers", // Indicator's Helpers Classes
|
|
"Indicators", // Required Indicators
|
|
"Libraries", // Required Libraries
|
|
"XCATBEA", // XCATBEA Projects ...
|
|
];
|
|
|
|
//
|
|
//#region Tools ...
|
|
|
|
/**
|
|
* Handle Compile and Pack Projects ...
|
|
*
|
|
* @param {string} projectName
|
|
* @param {string[]} resources
|
|
* @param {boolean} forceCleanup
|
|
* @param {boolean} forceCompileIndicators
|
|
*/
|
|
async function prepareProject(
|
|
projectName = "",
|
|
resources = [],
|
|
forceCleanup = true,
|
|
forceCompileIndicators = true
|
|
) {
|
|
//
|
|
// Check Resources Has Child ...
|
|
let isValidArgs =
|
|
resources.length > 0 &&
|
|
XTypeTools.isArray(resources);
|
|
if (!isValidArgs) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Invalid Resource List ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Check Project Name is Valid ...
|
|
isValidArgs =
|
|
projectName.length > 0 &&
|
|
XTypeTools.isString(projectName);
|
|
if (!isValidArgs) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Empty Project Name ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Prepare Pack Folder Exists ...
|
|
let isExists = XFileTools.isDirectoryExists(packsPath);
|
|
if (!isExists) {
|
|
XFileTools.createDirectory(packsPath);
|
|
}
|
|
|
|
//
|
|
// Check Folder Exists or not ...
|
|
// if Exists Remove it ...
|
|
// Create Project Folder on Packs ...
|
|
const prjFolderOnPacks = XFileTools.joinPath(packsPath, projectName);
|
|
isExists = XFileTools.isDirectoryExists(prjFolderOnPacks);
|
|
if (isExists) {
|
|
await XFileTools.removeDirectory(prjFolderOnPacks, true);
|
|
}
|
|
XFileTools.createDirectory(prjFolderOnPacks);
|
|
|
|
//
|
|
// Extract Required to Compile Resources List ...
|
|
let compileList = await extractResources(resources, true);
|
|
if (!compileList || compileList.length == 0) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Resource List Extraction to Compile Failed ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Compile Resources Files ...
|
|
for (const f of compileList) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("try to Compile: ", XColorTools.COLOR_NAMES.Yellow),
|
|
XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue)
|
|
);
|
|
|
|
//
|
|
await recursiveTriesToCompileFile(f, 0);
|
|
}
|
|
|
|
//
|
|
// Extract Required to Pack Resources List ...
|
|
let packList = await extractResources(resources, false);
|
|
if (!packList || packList.length == 0) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Resource List Extraction to Pack Failed ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop through Files ...
|
|
for (const f of packList) {
|
|
//
|
|
// Prepare Destination Path ...
|
|
const fBasePath = XFileTools.basename(f);
|
|
let fDestPath = f.replace(fBasePath, "");
|
|
fDestPath = fDestPath.substring(0, fDestPath.length - 1);
|
|
fDestPath = XFileTools.joinPath(prjFolderOnPacks, fDestPath);
|
|
|
|
//
|
|
const isFDestPathExists = XFileTools.isDirectoryExists(fDestPath);
|
|
if (!isFDestPathExists) {
|
|
XFileTools.createDirectory(fDestPath);
|
|
}
|
|
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("try to copy: ", XColorTools.COLOR_NAMES.Yellow),
|
|
XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue),
|
|
XColorTools.applyForegroundColor(" to : ", XColorTools.COLOR_NAMES.Yellow),
|
|
XColorTools.applyForegroundColor(fDestPath, XColorTools.COLOR_NAMES.Magenta)
|
|
);
|
|
|
|
//
|
|
let result = await XFileTools.copyFile(f, fDestPath);
|
|
if (result) {
|
|
//
|
|
const msg = "copy successfully ...";
|
|
console.log(
|
|
XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green)
|
|
);
|
|
} else {
|
|
//
|
|
const msg = "copy failed ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red));
|
|
throw msg;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Project: ", XColorTools.COLOR_NAMES.Green),
|
|
XColorTools.applyForegroundColor(projectName, XColorTools.COLOR_NAMES.Yellow),
|
|
XColorTools.applyForegroundColor(" Compiled and Packed Successfully ...", XColorTools.COLOR_NAMES.Green),
|
|
);
|
|
|
|
//
|
|
// Cleanup WorkSpace if Provided ...
|
|
if (forceCleanup) {
|
|
await cleanupWorkSpace();
|
|
}
|
|
|
|
//
|
|
// Compile Indicators ...
|
|
if (forceCompileIndicators) {
|
|
await runCommand("Indicators");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract required Files List ...
|
|
*
|
|
* @param {boolean} forCompile
|
|
*
|
|
* @returns Promise<string[]>
|
|
*/
|
|
async function extractResources(
|
|
resources = [],
|
|
forCompile = true
|
|
) {
|
|
//
|
|
let result = [];
|
|
|
|
//
|
|
// Loop Through Project Resources and Extract Files List to Compile ...
|
|
for (const resource of resources) {
|
|
//
|
|
// Prepare Normalized Resource Path ...
|
|
let resourcePath = XFileTools.joinPath(".", resource);
|
|
|
|
//
|
|
// Handle Wild Card Resources ...
|
|
if (resourcePath.endsWith("//*") ||
|
|
resourcePath.endsWith("\\*")) {
|
|
//
|
|
resourcePath = resourcePath.substring(0, resourcePath.length - 2);
|
|
const files = await XFileTools.extractFiles(
|
|
resourcePath,
|
|
forCompile ? [".mq5", ".mq4"] : [".ex5", ".ex4"]
|
|
);
|
|
|
|
//
|
|
result = result.concat(files);
|
|
}
|
|
//
|
|
// Handle Direct File Resources ...
|
|
else {
|
|
//
|
|
// Check MQL 5 Version ...
|
|
let fileResourcePath = resourcePath + (forCompile ? ".mq5" : ".ex5");
|
|
|
|
//
|
|
// Check File Exists ...
|
|
let isExists = XFileTools.isFileExists(fileResourcePath);
|
|
if (!isExists) {
|
|
//
|
|
// Check MQL 4 Version ...
|
|
fileResourcePath = resourcePath + (forCompile ? ".mq4" : ".ex4");
|
|
isExists = XFileTools.isFileExists(fileResourcePath);
|
|
}
|
|
|
|
//
|
|
// Add File Path to Files List if Exists ...
|
|
if (isExists) {
|
|
result.push(fileResourcePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* try to compile a file ...
|
|
* @param {string} f
|
|
* @param {number} numberOfTries
|
|
*/
|
|
async function recursiveTriesToCompileFile(f = "", numberOfTries = 0) {
|
|
//
|
|
let result = await handleFileCompile(f);
|
|
if (result) {
|
|
//
|
|
const msg = "compiled successfully ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green));
|
|
return result;
|
|
} else {
|
|
//
|
|
if (numberOfTries < 10) {
|
|
//
|
|
numberOfTries++;
|
|
|
|
//
|
|
const msg = "retry (" + (numberOfTries + 1) + ") ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.LightMagenta));
|
|
await recursiveTriesToCompileFile(f, numberOfTries);
|
|
} else {
|
|
//
|
|
const msg = "failed ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red));
|
|
throw msg;
|
|
|
|
//
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* execute compile task on Specific File ...
|
|
* @param {string} f file path to Execute Compile task ...
|
|
* @returns
|
|
*/
|
|
async function handleFileCompile(f = "") {
|
|
//
|
|
let result = false;
|
|
|
|
//
|
|
let exResult = await compileMQLFile(f);
|
|
if (exResult.includes("0 errors")) {
|
|
result = true;
|
|
} else {
|
|
result = false;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Compile Specific MQL File ...
|
|
*
|
|
* @param {string} path MQL file path ...
|
|
* @returns
|
|
*/
|
|
async function compileMQLFile(path = "") {
|
|
//
|
|
// Check File Exists ...
|
|
const isExists = XFileTools.isFileExists(path);
|
|
if (!isExists) {
|
|
//
|
|
const msg = "file not found ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red));
|
|
throw msg;
|
|
}
|
|
|
|
//
|
|
const ext = XFileTools.getFileExtension(path);
|
|
if (ext !== ".mq5" && ext !== "mq4") {
|
|
//
|
|
const msg = "invalid file extension ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red));
|
|
throw msg;
|
|
}
|
|
|
|
//
|
|
const cwd = __dirname;
|
|
const cmd = "compile.cmd " + path;
|
|
|
|
//
|
|
const result = await XShellTools.execute(cmd, cwd);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Run Specified Command in workspace ...
|
|
*
|
|
* @param {string} cmd
|
|
* @returns
|
|
*/
|
|
async function runCommand(cmd = "") {
|
|
//
|
|
// Validate Args ...
|
|
let isValidArgs = cmd.length > 0 &&
|
|
XTypeTools.isString(cmd);
|
|
|
|
//
|
|
// Validate Commands ...
|
|
if (isValidArgs) {
|
|
//
|
|
// Check Available Commands ...
|
|
isValidArgs =
|
|
cmd === "*" ||
|
|
cmd === "cleanup" ||
|
|
workspacePaths.includes(cmd) ||
|
|
workspacePaths.map(x => x.toLowerCase(x)).includes(cmd.toLowerCase());
|
|
}
|
|
if (!isValidArgs) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Invalid Command ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Compile ...
|
|
if (cmd == "*") {
|
|
await compileWorkSpace();
|
|
} else if (cmd == "cleanup") {
|
|
await cleanupWorkSpace();
|
|
} else {
|
|
//
|
|
// Select Workspace to Compile ...
|
|
const folder = workspacePaths
|
|
.find(x => x.toLowerCase() === cmd.toLowerCase());
|
|
isValidArgs =
|
|
XTypeTools.isString(folder) &&
|
|
folder.length > 0;
|
|
if (!isValidArgs) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Invalid Command ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Extract Required to Compile Resources List ...
|
|
let compileList = await XFileTools.extractFiles(
|
|
folder,
|
|
[".mq5", ".mq4"]
|
|
);
|
|
if (!compileList || compileList.length == 0) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("Extraction to Compile Failed ...", XColorTools.COLOR_NAMES.Red)
|
|
);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Compile Resources Files ...
|
|
for (const f of compileList) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("try to Compile: ", XColorTools.COLOR_NAMES.Yellow),
|
|
XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue)
|
|
);
|
|
|
|
//
|
|
await recursiveTriesToCompileFile(f, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup Workspace compiled files ...
|
|
*/
|
|
async function cleanupWorkSpace() {
|
|
//
|
|
for (const path of workspacePaths) {
|
|
//
|
|
const fSourcePath = XFileTools.joinPath(".", path);
|
|
const filesToRemove = await XFileTools.extractFiles(fSourcePath, [".ex5", ".ex4"]);
|
|
for (const f of filesToRemove) {
|
|
//
|
|
// Log ...
|
|
console.log(
|
|
XColorTools.applyForegroundColor("try to Remove: ", XColorTools.COLOR_NAMES.Yellow),
|
|
XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue)
|
|
);
|
|
|
|
//
|
|
let result = await XFileTools.removeFile(f);
|
|
if (result) {
|
|
//
|
|
const msg = "removed successfully ...";
|
|
console.log(
|
|
XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green)
|
|
);
|
|
} else {
|
|
//
|
|
const msg = "removed failed ...";
|
|
console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red));
|
|
throw msg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compile Workspace ...
|
|
*/
|
|
async function compileWorkSpace() {
|
|
//
|
|
for (const path of workspacePaths) {
|
|
await runCommand(path);
|
|
}
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region Module Exports ...
|
|
module.exports = {
|
|
extractResources,
|
|
recursiveTriesToCompileFile,
|
|
handleFileCompile,
|
|
compileMQLFile,
|
|
runCommand,
|
|
cleanupWorkSpace,
|
|
compileWorkSpace,
|
|
prepareProject
|
|
}
|
|
//#endregion
|