diff --git a/README.md b/README.md index 103ecb2e..37305503 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 3b20f8e4..14abf34d 100644 --- a/mkconcore.py +++ b/mkconcore.py @@ -97,6 +97,19 @@ def safe_name(value, context, allow_path=False): 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): @@ -109,16 +122,16 @@ def _resolve_concore_path(): 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 +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,6 +160,18 @@ 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] diff --git a/tests/test_tool_config.py b/tests/test_tool_config.py new file mode 100644 index 00000000..58adc903 --- /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 == {}