Skip to content

Commit 6788516

Browse files
committed
allow tool path overrides via concore.tools config and env vars
1 parent 18a7945 commit 6788516

3 files changed

Lines changed: 169 additions & 47 deletions

File tree

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,26 @@ concore stop
5757

5858
For detailed CLI documentation, see [concore_cli/README.md](concore_cli/README.md).
5959

60+
## Configuration
61+
62+
_concore_ supports customization through configuration files in the `CONCOREPATH` directory (defaults to the _concore_ installation directory):
63+
64+
- **concore.tools** - Override tool paths (one per line, `KEY=value` format):
65+
```
66+
CPPEXE=/usr/local/bin/g++-12
67+
PYTHONEXE=/usr/bin/python3.11
68+
VEXE=/opt/iverilog/bin/iverilog
69+
OCTAVEEXE=/snap/bin/octave
70+
```
71+
Supported keys: `CPPWIN`, `CPPEXE`, `VWIN`, `VEXE`, `PYTHONEXE`, `PYTHONWIN`, `MATLABEXE`, `MATLABWIN`, `OCTAVEEXE`, `OCTAVEWIN`
72+
73+
- **concore.octave** - Treat `.m` files as Octave instead of MATLAB (presence = enabled)
74+
- **concore.mcr** - MATLAB Compiler Runtime path (single line)
75+
- **concore.sudo** - Docker command override (e.g., `docker` instead of `sudo docker`)
76+
- **concore.repo** - Docker repository override
77+
78+
Tool paths can also be set via environment variables (e.g., `CONCORE_CPPEXE=/usr/bin/g++`). Priority: config file > env var > defaults.
79+
6080
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).
6181

6282

mkconcore.py

Lines changed: 73 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -75,50 +75,63 @@
7575
import shlex # Added for POSIX shell escaping
7676

7777
# input validation helper
78-
def safe_name(value, context, allow_path=False):
79-
"""
80-
Validates that the input string does not contain characters dangerous
81-
for filesystem paths or shell command injection.
82-
"""
83-
if not value:
84-
raise ValueError(f"{context} cannot be empty")
85-
# blocks control characters and shell metacharacters
86-
# allow path separators and drive colons for full paths when needed
87-
if allow_path:
88-
pattern = r'[\x00-\x1F\x7F*?"<>|;&`$\'()]'
89-
else:
90-
# blocks path traversal (/, \, :) in addition to shell metacharacters
91-
pattern = r'[\x00-\x1F\x7F\\/:*?"<>|;&`$\'()]'
92-
if re.search(pattern, value):
93-
raise ValueError(f"Unsafe {context}: '{value}' contains illegal characters.")
94-
return value
95-
96-
MKCONCORE_VER = "22-09-18"
97-
98-
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
99-
100-
def _resolve_concore_path():
101-
script_concore = os.path.join(SCRIPT_DIR, "concore.py")
102-
if os.path.exists(script_concore):
103-
return SCRIPT_DIR
104-
cwd_concore = os.path.join(os.getcwd(), "concore.py")
105-
if os.path.exists(cwd_concore):
106-
return os.getcwd()
107-
return SCRIPT_DIR
108-
109-
GRAPHML_FILE = sys.argv[1]
110-
TRIMMED_LOGS = True
111-
CONCOREPATH = _resolve_concore_path()
112-
CPPWIN = "g++" #Windows C++ 6/22/21
113-
CPPEXE = "g++" #Ubuntu/macOS C++ 6/22/21
114-
VWIN = "iverilog" #Windows verilog 6/25/21
115-
VEXE = "iverilog" #Ubuntu/macOS verilog 6/25/21
116-
PYTHONEXE = "python3" #Ubuntu/macOS python3
117-
PYTHONWIN = "python" #Windows python3
118-
MATLABEXE = "matlab" #Ubuntu/macOS matlab
119-
MATLABWIN = "matlab" #Windows matlab
120-
OCTAVEEXE = "octave" #Ubuntu/macOS octave
121-
OCTAVEWIN = "octave" #Windows octave
78+
def safe_name(value, context, allow_path=False):
79+
"""
80+
Validates that the input string does not contain characters dangerous
81+
for filesystem paths or shell command injection.
82+
"""
83+
if not value:
84+
raise ValueError(f"{context} cannot be empty")
85+
# blocks control characters and shell metacharacters
86+
# allow path separators and drive colons for full paths when needed
87+
if allow_path:
88+
pattern = r'[\x00-\x1F\x7F*?"<>|;&`$\'()]'
89+
else:
90+
# blocks path traversal (/, \, :) in addition to shell metacharacters
91+
pattern = r'[\x00-\x1F\x7F\\/:*?"<>|;&`$\'()]'
92+
if re.search(pattern, value):
93+
raise ValueError(f"Unsafe {context}: '{value}' contains illegal characters.")
94+
return value
95+
96+
MKCONCORE_VER = "22-09-18"
97+
98+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
99+
100+
def _load_tool_config(filepath):
101+
tools = {}
102+
with open(filepath, "r") as f:
103+
for line in f:
104+
line = line.strip()
105+
if not line or line.startswith("#") or "=" not in line:
106+
continue
107+
k, v = line.split("=", 1)
108+
k, v = k.strip(), v.strip()
109+
if v:
110+
tools[k] = v
111+
return tools
112+
113+
def _resolve_concore_path():
114+
script_concore = os.path.join(SCRIPT_DIR, "concore.py")
115+
if os.path.exists(script_concore):
116+
return SCRIPT_DIR
117+
cwd_concore = os.path.join(os.getcwd(), "concore.py")
118+
if os.path.exists(cwd_concore):
119+
return os.getcwd()
120+
return SCRIPT_DIR
121+
122+
GRAPHML_FILE = sys.argv[1]
123+
TRIMMED_LOGS = True
124+
CONCOREPATH = _resolve_concore_path()
125+
CPPWIN = os.environ.get("CONCORE_CPPWIN", "g++") #Windows C++ 6/22/21
126+
CPPEXE = os.environ.get("CONCORE_CPPEXE", "g++") #Ubuntu/macOS C++ 6/22/21
127+
VWIN = os.environ.get("CONCORE_VWIN", "iverilog") #Windows verilog 6/25/21
128+
VEXE = os.environ.get("CONCORE_VEXE", "iverilog") #Ubuntu/macOS verilog 6/25/21
129+
PYTHONEXE = os.environ.get("CONCORE_PYTHONEXE", "python3") #Ubuntu/macOS python3
130+
PYTHONWIN = os.environ.get("CONCORE_PYTHONWIN", "python") #Windows python3
131+
MATLABEXE = os.environ.get("CONCORE_MATLABEXE", "matlab") #Ubuntu/macOS matlab
132+
MATLABWIN = os.environ.get("CONCORE_MATLABWIN", "matlab") #Windows matlab
133+
OCTAVEEXE = os.environ.get("CONCORE_OCTAVEEXE", "octave") #Ubuntu/macOS octave
134+
OCTAVEWIN = os.environ.get("CONCORE_OCTAVEWIN", "octave") #Windows octave
122135
M_IS_OCTAVE = False #treat .m as octave
123136
MCRPATH = "~/MATLAB/R2021a" #path to local Ubunta Matlab Compiler Runtime
124137
DOCKEREXE = "sudo docker"#assume simple docker install
@@ -147,13 +160,26 @@ def _resolve_concore_path():
147160
with open(CONCOREPATH+"/concore.repo", "r") as f:
148161
DOCKEREPO = f.readline().strip() #docker id for repo
149162

163+
if os.path.exists(CONCOREPATH+"/concore.tools"):
164+
_tools = _load_tool_config(CONCOREPATH+"/concore.tools")
165+
CPPWIN = _tools.get("CPPWIN", CPPWIN)
166+
CPPEXE = _tools.get("CPPEXE", CPPEXE)
167+
VWIN = _tools.get("VWIN", VWIN)
168+
VEXE = _tools.get("VEXE", VEXE)
169+
PYTHONEXE = _tools.get("PYTHONEXE", PYTHONEXE)
170+
PYTHONWIN = _tools.get("PYTHONWIN", PYTHONWIN)
171+
MATLABEXE = _tools.get("MATLABEXE", MATLABEXE)
172+
MATLABWIN = _tools.get("MATLABWIN", MATLABWIN)
173+
OCTAVEEXE = _tools.get("OCTAVEEXE", OCTAVEEXE)
174+
OCTAVEWIN = _tools.get("OCTAVEWIN", OCTAVEWIN)
175+
150176

151177
prefixedgenode = ""
152178
sourcedir = sys.argv[2]
153179
outdir = sys.argv[3]
154180

155-
# Validate outdir argument (allow full paths)
156-
safe_name(outdir, "Output directory argument", allow_path=True)
181+
# Validate outdir argument (allow full paths)
182+
safe_name(outdir, "Output directory argument", allow_path=True)
157183

158184
if not os.path.isdir(sourcedir):
159185
logging.error(f"{sourcedir} does not exist")
@@ -1227,4 +1253,4 @@ def cleanup_script_files():
12271253
os.chmod(outdir+"/clear",stat.S_IRWXU)
12281254
os.chmod(outdir+"/maxtime",stat.S_IRWXU)
12291255
os.chmod(outdir+"/params",stat.S_IRWXU)
1230-
os.chmod(outdir+"/unlock",stat.S_IRWXU)
1256+
os.chmod(outdir+"/unlock",stat.S_IRWXU)

tests/test_tool_config.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import pytest
2+
import os
3+
4+
# can't import mkconcore directly (sys.argv at module level), so we duplicate the parser
5+
def _load_tool_config(filepath):
6+
tools = {}
7+
with open(filepath, "r") as f:
8+
for line in f:
9+
line = line.strip()
10+
if not line or line.startswith("#") or "=" not in line:
11+
continue
12+
k, v = line.split("=", 1)
13+
k, v = k.strip(), v.strip()
14+
if v:
15+
tools[k] = v
16+
return tools
17+
18+
19+
class TestLoadToolConfig:
20+
21+
def test_basic_overrides(self, temp_dir):
22+
cfg = os.path.join(temp_dir, "concore.tools")
23+
with open(cfg, "w") as f:
24+
f.write("CPPEXE=/usr/local/bin/g++-12\n")
25+
f.write("PYTHONEXE=/usr/bin/python3.11\n")
26+
27+
tools = _load_tool_config(cfg)
28+
assert tools["CPPEXE"] == "/usr/local/bin/g++-12"
29+
assert tools["PYTHONEXE"] == "/usr/bin/python3.11"
30+
assert "VEXE" not in tools
31+
32+
def test_comments_and_blanks_ignored(self, temp_dir):
33+
cfg = os.path.join(temp_dir, "concore.tools")
34+
with open(cfg, "w") as f:
35+
f.write("# custom tool paths\n")
36+
f.write("\n")
37+
f.write("OCTAVEEXE = /snap/bin/octave\n")
38+
f.write("# MATLABEXE = /opt/matlab/bin/matlab\n")
39+
40+
tools = _load_tool_config(cfg)
41+
assert tools["OCTAVEEXE"] == "/snap/bin/octave"
42+
assert "MATLABEXE" not in tools
43+
44+
def test_empty_value_skipped(self, temp_dir):
45+
cfg = os.path.join(temp_dir, "concore.tools")
46+
with open(cfg, "w") as f:
47+
f.write("CPPWIN=\n")
48+
f.write("VEXE = \n")
49+
50+
tools = _load_tool_config(cfg)
51+
assert "CPPWIN" not in tools
52+
assert "VEXE" not in tools
53+
54+
def test_value_with_equals_sign(self, temp_dir):
55+
cfg = os.path.join(temp_dir, "concore.tools")
56+
with open(cfg, "w") as f:
57+
f.write("CPPEXE=C:\\Program Files\\g++=fast\n")
58+
59+
tools = _load_tool_config(cfg)
60+
assert tools["CPPEXE"] == "C:\\Program Files\\g++=fast"
61+
62+
def test_whitespace_around_key_value(self, temp_dir):
63+
cfg = os.path.join(temp_dir, "concore.tools")
64+
with open(cfg, "w") as f:
65+
f.write(" VWIN = C:\\iverilog\\bin\\iverilog.exe \n")
66+
67+
tools = _load_tool_config(cfg)
68+
assert tools["VWIN"] == "C:\\iverilog\\bin\\iverilog.exe"
69+
70+
def test_empty_file(self, temp_dir):
71+
cfg = os.path.join(temp_dir, "concore.tools")
72+
with open(cfg, "w") as f:
73+
pass
74+
75+
tools = _load_tool_config(cfg)
76+
assert tools == {}

0 commit comments

Comments
 (0)