/** * XProject Tools Module ... * a module for handle MQL5 Projects Compile and Packing ... * * Maintainer * * Hadi Khazaee Asl (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 */ 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