Skip to content

Commit 2182254

Browse files
authored
Merge pull request #541 from Titas-Ghosh/fix-mkconcore-tool-path-quoting
fix(mkconcore): handle tool executable paths with spaces in generated scripts
2 parents 12971f7 + 7c4e0ea commit 2182254

2 files changed

Lines changed: 101 additions & 9 deletions

File tree

mkconcore.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,21 @@ def safe_relpath(value, context):
111111
if any(part in ("", "..") for part in normalized.split("/")):
112112
raise ValueError(f"Unsafe {context}: invalid path segment.")
113113
return normalized
114-
114+
115+
def _quote_executable_windows(value):
116+
value = value.strip()
117+
if value.startswith('"') and value.endswith('"'):
118+
return value
119+
return f'"{value}"'
120+
121+
def _quote_executable_posix(value):
122+
value = value.strip()
123+
if (value.startswith("'") and value.endswith("'")) or (
124+
value.startswith('"') and value.endswith('"')
125+
):
126+
return value
127+
return shlex.quote(value)
128+
115129
MKCONCORE_VER = "22-09-18"
116130

117131
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -1136,14 +1150,32 @@ def cleanup_script_files():
11361150
fbuild.write('cd ..'+"\n")
11371151
i=i+1
11381152

1139-
#start running source in associated dirs (run and debug scripts)
1140-
if concoretype=="posix":
1141-
fdebug.write('#!/bin/bash' + "\n")
1142-
frun.write('#!/bin/bash' + "\n")
1143-
1144-
1145-
i=0
1146-
for node in nodes_dict:
1153+
#start running source in associated dirs (run and debug scripts)
1154+
if concoretype=="posix":
1155+
fdebug.write('#!/bin/bash' + "\n")
1156+
frun.write('#!/bin/bash' + "\n")
1157+
1158+
# Normalize tool executable quoting once, then reuse existing script templates.
1159+
if concoretype == "windows":
1160+
PYTHONWIN = _quote_executable_windows(PYTHONWIN)
1161+
CPPWIN = _quote_executable_windows(CPPWIN)
1162+
VWIN = _quote_executable_windows(VWIN)
1163+
JAVACWIN = _quote_executable_windows(JAVACWIN)
1164+
JAVAWIN = _quote_executable_windows(JAVAWIN)
1165+
OCTAVEWIN = _quote_executable_windows(OCTAVEWIN)
1166+
MATLABWIN = _quote_executable_windows(MATLABWIN)
1167+
else:
1168+
PYTHONEXE = _quote_executable_posix(PYTHONEXE)
1169+
CPPEXE = _quote_executable_posix(CPPEXE)
1170+
VEXE = _quote_executable_posix(VEXE)
1171+
JAVACEXE = _quote_executable_posix(JAVACEXE)
1172+
JAVAEXE = _quote_executable_posix(JAVAEXE)
1173+
OCTAVEEXE = _quote_executable_posix(OCTAVEEXE)
1174+
MATLABEXE = _quote_executable_posix(MATLABEXE)
1175+
1176+
1177+
i=0
1178+
for node in nodes_dict:
11471179
containername,sourcecode = nodes_dict[node].split(':')
11481180
if len(sourcecode)!=0:
11491181
dockername,langext = sourcecode.rsplit(".", 1)

tests/test_cli.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import json
66
from pathlib import Path
7+
from unittest.mock import patch
78
from click.testing import CliRunner
89
from concore_cli.cli import cli
910

@@ -185,6 +186,65 @@ def test_build_command_default_type(self):
185186
else:
186187
self.assertTrue(Path("out/build").exists())
187188

189+
def test_build_command_quotes_posix_tool_path_with_spaces(self):
190+
with self.runner.isolated_filesystem(temp_dir=self.temp_dir):
191+
result = self.runner.invoke(cli, ["init", "test-project"])
192+
self.assertEqual(result.exit_code, 0)
193+
194+
with patch.dict(
195+
os.environ,
196+
{"CONCORE_PYTHONEXE": "/opt/custom tools/python3"},
197+
clear=False,
198+
):
199+
result = self.runner.invoke(
200+
cli,
201+
[
202+
"build",
203+
"test-project/workflow.graphml",
204+
"--source",
205+
"test-project/src",
206+
"--output",
207+
"out",
208+
"--type",
209+
"posix",
210+
],
211+
)
212+
213+
self.assertEqual(result.exit_code, 0)
214+
run_script = Path("out/run").read_text()
215+
self.assertIn("'/opt/custom tools/python3' script.py", run_script)
216+
217+
def test_build_command_quotes_windows_tool_path_with_spaces(self):
218+
with self.runner.isolated_filesystem(temp_dir=self.temp_dir):
219+
result = self.runner.invoke(cli, ["init", "test-project"])
220+
self.assertEqual(result.exit_code, 0)
221+
222+
with patch.dict(
223+
os.environ,
224+
{"CONCORE_PYTHONWIN": r"C:\Program Files\Python313\python.exe"},
225+
clear=False,
226+
):
227+
result = self.runner.invoke(
228+
cli,
229+
[
230+
"build",
231+
"test-project/workflow.graphml",
232+
"--source",
233+
"test-project/src",
234+
"--output",
235+
"out",
236+
"--type",
237+
"windows",
238+
],
239+
)
240+
241+
self.assertEqual(result.exit_code, 0)
242+
run_script = Path("out/run.bat").read_text()
243+
self.assertIn(
244+
'"C:\\Program Files\\Python313\\python.exe" "script.py"',
245+
run_script,
246+
)
247+
188248
def test_build_command_nested_output_path(self):
189249
with self.runner.isolated_filesystem(temp_dir=self.temp_dir):
190250
result = self.runner.invoke(cli, ["init", "test-project"])

0 commit comments

Comments
 (0)