Skip to content

Commit b3e0e54

Browse files
authored
Merge pull request #947 from mlcommons/dev
Merge Dev
2 parents e6b0205 + cc725e1 commit b3e0e54

416 files changed

Lines changed: 27361 additions & 1550 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-mlc-script-features.yml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,135 @@ jobs:
103103
run: |
104104
mlcr run,docker,container --adr.compiler.tags=gcc --docker_mlc_repo=mlcommons@mlperf-automations --docker_mlc_repo_branch=dev --image_name=mlc-script-app-image-classification-onnx-py --env.MLC_DOCKER_RUN_SCRIPT_TAGS=app,image-classification,onnx,python --env.MLC_DOCKER_IMAGE_BASE=ubuntu:22.04 --env.MLC_DOCKER_IMAGE_REPO=local --quiet
105105
106+
- name: Test docker process_mounts input_mapping fallback (src_path fix)
107+
shell: bash
108+
run: |
109+
python3 << 'PYTEST'
110+
import subprocess, re, sys, os
111+
112+
# Find the automation/script directory via mlc
113+
out = subprocess.run(['mlc', 'find', 'script', '--tags=detect,os', '--quiet'],
114+
capture_output=True, text=True)
115+
match = re.search(r'Item path:\s*(.+)', out.stdout + out.stderr)
116+
if not match:
117+
print("ERROR: Could not locate MLC repo via 'mlc find script'")
118+
sys.exit(1)
119+
repo_root = os.path.dirname(os.path.dirname(match.group(1).strip()))
120+
sys.path.insert(0, os.path.join(repo_root, 'automation'))
121+
122+
from script.docker_utils import process_mounts
123+
124+
# Test 1: input_mapping in run_state is used as fallback for docker path remapping
125+
docker_settings = {'user': 'mlcuser'}
126+
mounts = ['${{ MLC_SRC_REPO_PATH }}:${{ MLC_SRC_REPO_PATH }}']
127+
env = {'MLC_SRC_REPO_PATH': '/home/user/repos/my-project'}
128+
run_state = {
129+
'input_mapping': {'src_path': 'MLC_SRC_REPO_PATH'},
130+
'file_path_env_keys': [],
131+
'folder_path_env_keys': []
132+
}
133+
f_run_cmd = {'src_path': '/home/user/repos/my-project'}
134+
135+
result = process_mounts(mounts, env, docker_settings, f_run_cmd, run_state)
136+
assert result['return'] == 0, f"process_mounts failed: {result}"
137+
assert f_run_cmd['src_path'] != '/home/user/repos/my-project', \
138+
f"FAIL: src_path was not remapped to container path! Got: {f_run_cmd['src_path']}"
139+
assert '/mlc-mount/' in f_run_cmd['src_path'], \
140+
f"FAIL: Expected /mlc-mount/ in remapped path, got: {f_run_cmd['src_path']}"
141+
print(f"PASS: src_path correctly remapped to {f_run_cmd['src_path']}")
142+
143+
# Test 2: without input_mapping, f_run_cmd should not be modified
144+
f_run_cmd2 = {'src_path': '/home/user/repos/my-project'}
145+
run_state2 = {
146+
'input_mapping': {},
147+
'file_path_env_keys': [],
148+
'folder_path_env_keys': []
149+
}
150+
mounts2 = ['${{ MLC_SRC_REPO_PATH }}:${{ MLC_SRC_REPO_PATH }}']
151+
result2 = process_mounts(mounts2, env, docker_settings, f_run_cmd2, run_state2)
152+
assert result2['return'] == 0
153+
assert f_run_cmd2['src_path'] == '/home/user/repos/my-project', \
154+
f"FAIL: Without input_mapping, src_path should be unchanged, got: {f_run_cmd2['src_path']}"
155+
print("PASS: Without input_mapping, src_path stays as host path")
156+
157+
print("\nAll process_mounts tests passed!")
158+
PYTEST
159+
- name: Test nested Docker opt-in mount handling
160+
shell: bash
161+
run: |
162+
python - <<'PY'
163+
import importlib.util
164+
import sys
165+
from types import SimpleNamespace
166+
from unittest.mock import patch
167+
168+
sys.path.insert(0, 'automation')
169+
170+
spec = importlib.util.spec_from_file_location(
171+
'run_docker_customize',
172+
'script/run-docker-container/customize.py'
173+
)
174+
mod = importlib.util.module_from_spec(spec)
175+
spec.loader.exec_module(mod)
176+
177+
logger_messages = []
178+
logger = SimpleNamespace(
179+
info=lambda *args, **kwargs: logger_messages.append(('info', args)),
180+
error=lambda *args, **kwargs: logger_messages.append(('error', args))
181+
)
182+
DOCKER_ENV_MARKER = '/.dockerenv'
183+
DOCKER_SOCKET = '/var/run/docker.sock'
184+
DOCKER_BIN = '/usr/bin/docker'
185+
186+
def run_case(enable_nested, docker_exit_code=0):
187+
captured = {'cmd': ''}
188+
189+
def fake_system(cmd):
190+
captured['cmd'] = cmd
191+
return docker_exit_code
192+
193+
def fake_exists(path):
194+
if path in [DOCKER_ENV_MARKER, DOCKER_SOCKET, DOCKER_BIN]:
195+
return True
196+
return False
197+
198+
env = {
199+
'MLC_CONTAINER_TOOL': 'docker',
200+
'MLC_DOCKER_RUN_SCRIPT_TAGS': 'run,docker,container',
201+
'MLC_DOCKER_RUN_CMD': 'echo nested-docker-ci-check',
202+
'MLC_DOCKER_ENABLE_NESTED': 'yes' if enable_nested else 'no'
203+
}
204+
i = {
205+
'os_info': {'platform': 'linux'},
206+
'env': env,
207+
'automation': SimpleNamespace(logger=logger)
208+
}
209+
210+
with patch.object(mod.os.path, 'exists', side_effect=fake_exists), \
211+
patch.object(mod.os, 'system', side_effect=fake_system), \
212+
patch.object(mod.shutil, 'which', return_value=DOCKER_BIN):
213+
result = mod.postprocess(i)
214+
215+
return result, captured['cmd']
216+
217+
enabled_result, enabled_cmd = run_case(True, docker_exit_code=0)
218+
disabled_result, disabled_cmd = run_case(False, docker_exit_code=0)
219+
failed_result, _ = run_case(True, docker_exit_code=1)
220+
221+
assert enabled_result['return'] == 0, enabled_result
222+
assert disabled_result['return'] == 0, disabled_result
223+
error_msg = failed_result.get('error', '').lower()
224+
assert failed_result['return'] == 1, failed_result
225+
assert 'docker' in error_msg, failed_result
226+
assert 'failed' in error_msg, failed_result
227+
228+
assert f'{DOCKER_SOCKET}:{DOCKER_SOCKET}' in enabled_cmd, enabled_cmd
229+
assert f'{DOCKER_BIN}:{DOCKER_BIN}' in enabled_cmd, enabled_cmd
230+
assert f'{DOCKER_SOCKET}:{DOCKER_SOCKET}' not in disabled_cmd, disabled_cmd
231+
assert f'{DOCKER_BIN}:{DOCKER_BIN}' not in disabled_cmd, disabled_cmd
232+
print('Nested Docker opt-in mount handling test passed')
233+
PY
234+
106235
test_experiment:
107236
runs-on: ${{ matrix.os }}
108237
strategy:

.github/workflows/test-mlperf-inference-abtf-poc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
os: [ubuntu-24.04, ubuntu-latest, macos-latest, macos-15, windows-latest]
19-
python-version: [ "3.9", "3.14" ]
19+
python-version: [ "3.14" ]
2020
backend: [ "pytorch" ]
2121
implementation: [ "python" ]
2222
docker: [ "", "docker" ]

.github/workflows/test-mlperf-inference-mlcommons-cpp-resnet50.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ jobs:
2222
exclude:
2323
- os: windows-latest
2424
- os: macos-latest
25+
compiler-string: --adr.compiler.tags=gcc
26+
- os: macos-latest
27+
compiler-string: --adr.compiler.tags=aocc --env.MLC_AOCC_ACCEPT_EULA=yes
2528

2629
steps:
2730
- uses: actions/checkout@v3

.github/workflows/test-mlperf-inference-resnet50.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
name: MLPerf inference ResNet50
22

3+
permissions:
4+
contents: read
5+
36
on:
47
pull_request_target:
58
branches: [ "main", "dev" ]
@@ -107,4 +110,45 @@ jobs:
107110
git config --global credential.https://gist.github.com.helper ""
108111
git config --global credential.https://gist.github.com.helper "!gh auth git-credential"
109112
mlcr push,github,mlperf,inference,submission --repo_url=https://github.com/mlcommons/mlperf_inference_test_submissions_v5.0 --repo_branch=auto-update --commit_message="Results from R50 GH action on ${{ matrix.os }}" --quiet
113+
114+
mlc-run-rhel-docker:
115+
runs-on: ubuntu-latest
116+
env:
117+
MLC_INDEX: "on"
118+
steps:
119+
- uses: actions/checkout@v4
120+
with:
121+
fetch-depth: 0
122+
- name: Set up Python 3.12
123+
uses: actions/setup-python@v3
124+
with:
125+
python-version: "3.12"
126+
- name: Install mlcflow
127+
run: |
128+
pip install mlcflow
129+
pip install tabulate
130+
- name: Pull MLOps repo
131+
shell: bash
132+
env:
133+
REPO: ${{ github.event.pull_request.head.repo.html_url }}
134+
BRANCH: ${{ github.event.pull_request.head.ref }}
135+
run: |
136+
mlc pull repo "$REPO" --branch="$BRANCH"
137+
- name: Test MLPerf Inference ResNet50 (RHEL Docker)
138+
run: |
139+
mlcd run-mlperf,inference,_submission,_short,_r6.0-dev \
140+
--submitter="MLCommons" \
141+
--pull_changes=yes \
142+
--pull_inference_changes=yes \
143+
--hw_name="gh_rhel9_docker x86" \
144+
--model=resnet50 \
145+
--implementation=python \
146+
--backend=onnxruntime \
147+
--device=cpu \
148+
--scenario=Offline \
149+
--test_query_count=500 \
150+
--target_qps=1 \
151+
--docker_os=rhel \
152+
--docker_os_version=9 \
153+
-v --quiet
110154

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ MANIFEST
55
__pycache__
66
develop-eggs/
77
dist/
8+
*.egg-info/
89
eggs/
910
.eggs/
11+
*.egg-info/
1012
lib/
1113
lib64/
1214
sdist/
@@ -23,3 +25,4 @@ repos.json
2325
index_script.json
2426
index_cache.json
2527
index_experiment.json
28+
*.egg-info/

automation/script/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ mlcr "app,mlperf,inference" --docker_run --docker_image_repo=myrepo --docker_reb
788788
3. **Mount Handling**: Host directories are automatically mounted into the container based on environment variable paths.
789789
4. **Environment Passing**: Host env variables (proxies, tokens, etc.) are passed into the container.
790790
5. **Execution**: The MLC run command is re-executed inside the container with `--docker_run_deps` to handle Docker-specific dependency installation.
791+
6. **Nested Docker Support**: Set `MLC_DOCKER_ENABLE_NESTED=yes` to mount Docker socket (`/var/run/docker.sock`) and Docker CLI binary into launched containers (when available) when MLC is running inside Docker.
791792

792793
### Docker Meta Configuration
793794

automation/script/docker.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ def dockerfile(self_module, input_params):
2525
# Step 2: Process Dockerfile-related configurations
2626
env = input_params.get('env', {})
2727

28-
host_only_env_keys = [ "HOME", "USER" ]
28+
host_only_env_keys = ["HOME", "USER"]
2929
for key in host_only_env_keys:
3030
if key in env:
31-
del(env[key])
31+
del (env[key])
3232

3333
state_data = input_params.get('state', {})
3434
constant_vars = input_params.get('const', {})
@@ -141,8 +141,9 @@ def dockerfile(self_module, input_params):
141141
comments = []
142142

143143
# Push Docker image if specified
144-
if str(input_params.get('docker_push_image')
145-
).lower() in ['true', 'yes', '1']:
144+
docker_push_image = input_params.get(
145+
'docker_push_image', input_params.get('docker_upload', ''))
146+
if str(docker_push_image).lower() in ['true', 'yes', '1']:
146147
env['MLC_DOCKER_PUSH_IMAGE'] = 'yes'
147148

148149
dockerfile_env = docker_inputs.get('env')
@@ -156,6 +157,7 @@ def dockerfile(self_module, input_params):
156157
state = {}
157158
state['dockerfile_env'] = dockerfile_env
158159
state['dockerfile_build_env'] = dockerfile_build_env
160+
159161
# Generate Dockerfile
160162
mlc_docker_input = {
161163
'action': 'run', 'automation': 'script', 'tags': 'build,dockerfile',
@@ -170,6 +172,9 @@ def dockerfile(self_module, input_params):
170172
if docker_inputs.get('mlc_repo_path', '') != '':
171173
mlc_docker_input['mlc_repo_path'] = docker_inputs['mlc_repo_path']
172174

175+
if is_true(input_params.get('docker_host_mlc_repos', '')):
176+
mlc_docker_input['host_mlc_repos'] = 'yes'
177+
173178
docker_v = False
174179
docker_s = False
175180
if is_true(input_params.get(
@@ -225,10 +230,10 @@ def docker_run(self_module, i):
225230
show_time = i.get('show_time', False)
226231
logger = self_module.logger
227232
env = i.get('env', {})
228-
host_only_env_keys = [ "HOME", "USER" ]
233+
host_only_env_keys = ["HOME", "USER"]
229234
for key in host_only_env_keys:
230235
if key in env:
231-
del(env[key])
236+
del (env[key])
232237

233238
self_module.env = env
234239

automation/script/docker_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def process_mounts(mounts, env, docker_settings, f_run_cmd, run_state):
4545
if "${{ " + key + " }}:${{ " + key + " }}" not in mounts:
4646
mounts.append("${{ " + key + " }}:${{ " + key + " }}")
4747

48-
docker_input_mapping = docker_settings.get("input_mapping", {})
48+
docker_input_mapping = docker_settings.get(
49+
"input_mapping", run_state.get("input_mapping", {}))
4950
container_env_string = ""
5051

5152
for index in range(len(mounts)):
@@ -142,6 +143,7 @@ def prepare_docker_inputs(input_params, docker_settings,
142143
"mlc_repos", "skip_mlc_sys_upgrade", "extra_sys_deps", "image_name",
143144
"gh_token", "fake_run_deps", "run_final_cmds", "real_run", "copy_files", "path", "user", "env", "build_env"
144145
]
146+
keys += ["cache", "split_mlc_run_cmd"]
145147

146148
if run_stage:
147149
keys += [

automation/script/module.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2658,7 +2658,7 @@ def search(self, i):
26582658

26592659
script_tags = i.get('script_tags', [])
26602660
variation_tags = i.get('variation_tags', [])
2661-
2661+
26622662
if not script_tags and tags_string:
26632663
r = get_variation_and_script_tags(tags_string.strip())
26642664
script_tags = r['script_tags']
@@ -4483,8 +4483,8 @@ def docker(self, i):
44834483
from script.docker import docker_run
44844484
return docker_run(self, i)
44854485

4486-
44874486
############################################################
4487+
44884488
def apptainerfile(self, i):
44894489
from script.apptainer import apptainerfile
44904490
return apptainerfile(self, i)
@@ -4494,6 +4494,7 @@ def apptainer(self, i):
44944494
from script.apptainer import apptainer_run
44954495
return apptainer_run(self, i)
44964496
############################################################
4497+
44974498
def experiment(self, i):
44984499
from script.experiment import experiment_run
44994500
return experiment_run(self, i)
@@ -5872,6 +5873,7 @@ def update_state_from_meta(meta, env, state, const, const_state, run_state, i):
58725873

58735874
input_mapping = meta.get('input_mapping', {})
58745875
if input_mapping:
5876+
run_state['input_mapping'] = input_mapping
58755877
update_env_from_input_mapping(
58765878
env,
58775879
input_update_env,
@@ -5928,7 +5930,6 @@ def update_state_from_meta(meta, env, state, const, const_state, run_state, i):
59285930
if folder_path_env_keys:
59295931
run_state['folder_path_env_keys'] += folder_path_env_keys
59305932

5931-
59325933
return {'return': 0}
59335934

59345935
##############################################################################

0 commit comments

Comments
 (0)