From 6788516ef6949552cfb9eb68aeccc99299927542 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak Date: Thu, 12 Feb 2026 12:31:42 +0530 Subject: [PATCH] allow tool path overrides via concore.tools config and env vars --- README.md | 20 +++++++ mkconcore.py | 120 +++++++++++++++++++++++--------------- tests/test_tool_config.py | 76 ++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 47 deletions(-) create mode 100644 tests/test_tool_config.py diff --git a/README.md b/README.md index 103ecb2..3730550 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,26 @@ concore stop For detailed CLI documentation, see [concore_cli/README.md](concore_cli/README.md). +## Configuration + +_concore_ supports customization through configuration files in the `CONCOREPATH` directory (defaults to the _concore_ installation directory): + +- **concore.tools** - Override tool paths (one per line, `KEY=value` format): + ``` + CPPEXE=/usr/local/bin/g++-12 + PYTHONEXE=/usr/bin/python3.11 + VEXE=/opt/iverilog/bin/iverilog + OCTAVEEXE=/snap/bin/octave + ``` + Supported keys: `CPPWIN`, `CPPEXE`, `VWIN`, `VEXE`, `PYTHONEXE`, `PYTHONWIN`, `MATLABEXE`, `MATLABWIN`, `OCTAVEEXE`, `OCTAVEWIN` + +- **concore.octave** - Treat `.m` files as Octave instead of MATLAB (presence = enabled) +- **concore.mcr** - MATLAB Compiler Runtime path (single line) +- **concore.sudo** - Docker command override (e.g., `docker` instead of `sudo docker`) +- **concore.repo** - Docker repository override + +Tool paths can also be set via environment variables (e.g., `CONCORE_CPPEXE=/usr/bin/g++`). Priority: config file > env var > defaults. + For a detailed and more scientific documentation, please read our extensive [open-access research paper on CONTROL-CORE](https://doi.org/10.1109/ACCESS.2022.3161471). This paper has a complete discussion on the CONTROL-CORE architecture and deployment, together with the commands to execute the studies in different programming languages and programming environments (Ubuntu, Windows, MacOS, Docker, and distributed execution). diff --git a/mkconcore.py b/mkconcore.py index 3b20f8e..5674997 100644 --- a/mkconcore.py +++ b/mkconcore.py @@ -75,50 +75,63 @@ import shlex # Added for POSIX shell escaping # input validation helper -def safe_name(value, context, allow_path=False): - """ - Validates that the input string does not contain characters dangerous - for filesystem paths or shell command injection. - """ - if not value: - raise ValueError(f"{context} cannot be empty") - # blocks control characters and shell metacharacters - # allow path separators and drive colons for full paths when needed - if allow_path: - pattern = r'[\x00-\x1F\x7F*?"<>|;&`$\'()]' - else: - # blocks path traversal (/, \, :) in addition to shell metacharacters - pattern = r'[\x00-\x1F\x7F\\/:*?"<>|;&`$\'()]' - if re.search(pattern, value): - raise ValueError(f"Unsafe {context}: '{value}' contains illegal characters.") - return value - -MKCONCORE_VER = "22-09-18" - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) - -def _resolve_concore_path(): - script_concore = os.path.join(SCRIPT_DIR, "concore.py") - if os.path.exists(script_concore): - return SCRIPT_DIR - cwd_concore = os.path.join(os.getcwd(), "concore.py") - if os.path.exists(cwd_concore): - return os.getcwd() - return SCRIPT_DIR - -GRAPHML_FILE = sys.argv[1] -TRIMMED_LOGS = True -CONCOREPATH = _resolve_concore_path() -CPPWIN = "g++" #Windows C++ 6/22/21 -CPPEXE = "g++" #Ubuntu/macOS C++ 6/22/21 -VWIN = "iverilog" #Windows verilog 6/25/21 -VEXE = "iverilog" #Ubuntu/macOS verilog 6/25/21 -PYTHONEXE = "python3" #Ubuntu/macOS python3 -PYTHONWIN = "python" #Windows python3 -MATLABEXE = "matlab" #Ubuntu/macOS matlab -MATLABWIN = "matlab" #Windows matlab -OCTAVEEXE = "octave" #Ubuntu/macOS octave -OCTAVEWIN = "octave" #Windows octave +def safe_name(value, context, allow_path=False): + """ + Validates that the input string does not contain characters dangerous + for filesystem paths or shell command injection. + """ + if not value: + raise ValueError(f"{context} cannot be empty") + # blocks control characters and shell metacharacters + # allow path separators and drive colons for full paths when needed + if allow_path: + pattern = r'[\x00-\x1F\x7F*?"<>|;&`$\'()]' + else: + # blocks path traversal (/, \, :) in addition to shell metacharacters + pattern = r'[\x00-\x1F\x7F\\/:*?"<>|;&`$\'()]' + if re.search(pattern, value): + raise ValueError(f"Unsafe {context}: '{value}' contains illegal characters.") + return value + +MKCONCORE_VER = "22-09-18" + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + +def _load_tool_config(filepath): + tools = {} + with open(filepath, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + k, v = line.split("=", 1) + k, v = k.strip(), v.strip() + if v: + tools[k] = v + return tools + +def _resolve_concore_path(): + script_concore = os.path.join(SCRIPT_DIR, "concore.py") + if os.path.exists(script_concore): + return SCRIPT_DIR + cwd_concore = os.path.join(os.getcwd(), "concore.py") + if os.path.exists(cwd_concore): + return os.getcwd() + return SCRIPT_DIR + +GRAPHML_FILE = sys.argv[1] +TRIMMED_LOGS = True +CONCOREPATH = _resolve_concore_path() +CPPWIN = os.environ.get("CONCORE_CPPWIN", "g++") #Windows C++ 6/22/21 +CPPEXE = os.environ.get("CONCORE_CPPEXE", "g++") #Ubuntu/macOS C++ 6/22/21 +VWIN = os.environ.get("CONCORE_VWIN", "iverilog") #Windows verilog 6/25/21 +VEXE = os.environ.get("CONCORE_VEXE", "iverilog") #Ubuntu/macOS verilog 6/25/21 +PYTHONEXE = os.environ.get("CONCORE_PYTHONEXE", "python3") #Ubuntu/macOS python3 +PYTHONWIN = os.environ.get("CONCORE_PYTHONWIN", "python") #Windows python3 +MATLABEXE = os.environ.get("CONCORE_MATLABEXE", "matlab") #Ubuntu/macOS matlab +MATLABWIN = os.environ.get("CONCORE_MATLABWIN", "matlab") #Windows matlab +OCTAVEEXE = os.environ.get("CONCORE_OCTAVEEXE", "octave") #Ubuntu/macOS octave +OCTAVEWIN = os.environ.get("CONCORE_OCTAVEWIN", "octave") #Windows octave M_IS_OCTAVE = False #treat .m as octave MCRPATH = "~/MATLAB/R2021a" #path to local Ubunta Matlab Compiler Runtime DOCKEREXE = "sudo docker"#assume simple docker install @@ -147,13 +160,26 @@ def _resolve_concore_path(): with open(CONCOREPATH+"/concore.repo", "r") as f: DOCKEREPO = f.readline().strip() #docker id for repo +if os.path.exists(CONCOREPATH+"/concore.tools"): + _tools = _load_tool_config(CONCOREPATH+"/concore.tools") + CPPWIN = _tools.get("CPPWIN", CPPWIN) + CPPEXE = _tools.get("CPPEXE", CPPEXE) + VWIN = _tools.get("VWIN", VWIN) + VEXE = _tools.get("VEXE", VEXE) + PYTHONEXE = _tools.get("PYTHONEXE", PYTHONEXE) + PYTHONWIN = _tools.get("PYTHONWIN", PYTHONWIN) + MATLABEXE = _tools.get("MATLABEXE", MATLABEXE) + MATLABWIN = _tools.get("MATLABWIN", MATLABWIN) + OCTAVEEXE = _tools.get("OCTAVEEXE", OCTAVEEXE) + OCTAVEWIN = _tools.get("OCTAVEWIN", OCTAVEWIN) + prefixedgenode = "" sourcedir = sys.argv[2] outdir = sys.argv[3] -# Validate outdir argument (allow full paths) -safe_name(outdir, "Output directory argument", allow_path=True) +# Validate outdir argument (allow full paths) +safe_name(outdir, "Output directory argument", allow_path=True) if not os.path.isdir(sourcedir): logging.error(f"{sourcedir} does not exist") @@ -1227,4 +1253,4 @@ def cleanup_script_files(): os.chmod(outdir+"/clear",stat.S_IRWXU) os.chmod(outdir+"/maxtime",stat.S_IRWXU) os.chmod(outdir+"/params",stat.S_IRWXU) - os.chmod(outdir+"/unlock",stat.S_IRWXU) + os.chmod(outdir+"/unlock",stat.S_IRWXU) diff --git a/tests/test_tool_config.py b/tests/test_tool_config.py new file mode 100644 index 0000000..58adc90 --- /dev/null +++ b/tests/test_tool_config.py @@ -0,0 +1,76 @@ +import pytest +import os + +# can't import mkconcore directly (sys.argv at module level), so we duplicate the parser +def _load_tool_config(filepath): + tools = {} + with open(filepath, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + k, v = line.split("=", 1) + k, v = k.strip(), v.strip() + if v: + tools[k] = v + return tools + + +class TestLoadToolConfig: + + def test_basic_overrides(self, temp_dir): + cfg = os.path.join(temp_dir, "concore.tools") + with open(cfg, "w") as f: + f.write("CPPEXE=/usr/local/bin/g++-12\n") + f.write("PYTHONEXE=/usr/bin/python3.11\n") + + tools = _load_tool_config(cfg) + assert tools["CPPEXE"] == "/usr/local/bin/g++-12" + assert tools["PYTHONEXE"] == "/usr/bin/python3.11" + assert "VEXE" not in tools + + def test_comments_and_blanks_ignored(self, temp_dir): + cfg = os.path.join(temp_dir, "concore.tools") + with open(cfg, "w") as f: + f.write("# custom tool paths\n") + f.write("\n") + f.write("OCTAVEEXE = /snap/bin/octave\n") + f.write("# MATLABEXE = /opt/matlab/bin/matlab\n") + + tools = _load_tool_config(cfg) + assert tools["OCTAVEEXE"] == "/snap/bin/octave" + assert "MATLABEXE" not in tools + + def test_empty_value_skipped(self, temp_dir): + cfg = os.path.join(temp_dir, "concore.tools") + with open(cfg, "w") as f: + f.write("CPPWIN=\n") + f.write("VEXE = \n") + + tools = _load_tool_config(cfg) + assert "CPPWIN" not in tools + assert "VEXE" not in tools + + def test_value_with_equals_sign(self, temp_dir): + cfg = os.path.join(temp_dir, "concore.tools") + with open(cfg, "w") as f: + f.write("CPPEXE=C:\\Program Files\\g++=fast\n") + + tools = _load_tool_config(cfg) + assert tools["CPPEXE"] == "C:\\Program Files\\g++=fast" + + def test_whitespace_around_key_value(self, temp_dir): + cfg = os.path.join(temp_dir, "concore.tools") + with open(cfg, "w") as f: + f.write(" VWIN = C:\\iverilog\\bin\\iverilog.exe \n") + + tools = _load_tool_config(cfg) + assert tools["VWIN"] == "C:\\iverilog\\bin\\iverilog.exe" + + def test_empty_file(self, temp_dir): + cfg = os.path.join(temp_dir, "concore.tools") + with open(cfg, "w") as f: + pass + + tools = _load_tool_config(cfg) + assert tools == {}