From 593201db4cfbb59f3cb0654cb032cbf7502aa967 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 12 Jun 2026 13:32:47 -0500 Subject: [PATCH 1/5] Generate API sources in the build directory --- src/api/CMakeLists.txt | 51 +++++++++++++++++++++++++++-- tools/generate.py | 73 +++++++++++++++++++++++++++++++++--------- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 9e83fc6b240..30ff8089687 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -22,9 +22,56 @@ # THE SOFTWARE. ##################################################################################### +# The C API (migraphx.h and api.cpp) is generated from the templates in +# tools/api. `make generate` still writes them into the source tree so the +# output can be reviewed, but migraphx_c is built from a freshly generated copy +# placed in the build directory instead of the checked-in src/api copies. +find_package(Python 3 COMPONENTS Interpreter REQUIRED) +find_program(CLANG_FORMAT clang-format PATHS /opt/rocm/llvm ENV HIP_PATH PATH_SUFFIXES bin) + +set(MIGRAPHX_API_TOOLS_DIR ${PROJECT_SOURCE_DIR}/tools) +set(MIGRAPHX_API_GEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) +set(MIGRAPHX_API_GEN_HEADER ${MIGRAPHX_API_GEN_INCLUDE_DIR}/migraphx/migraphx.h) +set(MIGRAPHX_API_GEN_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/api.cpp) + +# clang-format only affects readability of the generated files, so it is +# optional for the build; pass it along only when it is available. +set(MIGRAPHX_API_FORMAT_ARG) +if(CLANG_FORMAT) + set(MIGRAPHX_API_FORMAT_ARG -f ${CLANG_FORMAT}) +endif() + +add_custom_command( + OUTPUT ${MIGRAPHX_API_GEN_HEADER} ${MIGRAPHX_API_GEN_SOURCE} + COMMAND ${Python_EXECUTABLE} ${MIGRAPHX_API_TOOLS_DIR}/generate.py + --api-only + --api-header ${MIGRAPHX_API_GEN_HEADER} + --api-source ${MIGRAPHX_API_GEN_SOURCE} + ${MIGRAPHX_API_FORMAT_ARG} + WORKING_DIRECTORY ${MIGRAPHX_API_TOOLS_DIR} + DEPENDS + ${MIGRAPHX_API_TOOLS_DIR}/generate.py + ${MIGRAPHX_API_TOOLS_DIR}/api.py + ${MIGRAPHX_API_TOOLS_DIR}/api/migraphx.h + ${MIGRAPHX_API_TOOLS_DIR}/api/api.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/migraphx.py + COMMENT "Generating C API (migraphx.h and api.cpp) in build directory" + VERBATIM +) + +add_custom_target(generate_api DEPENDS ${MIGRAPHX_API_GEN_HEADER} ${MIGRAPHX_API_GEN_SOURCE}) + +# The non-generated public headers (e.g. migraphx.hpp) live alongside the +# generated header so consumers get a complete include directory. +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/migraphx/migraphx.hpp + ${MIGRAPHX_API_GEN_INCLUDE_DIR}/migraphx/migraphx.hpp + COPYONLY) + add_library(migraphx_c - api.cpp + ${MIGRAPHX_API_GEN_SOURCE} ) +add_dependencies(migraphx_c generate_api) set_target_properties(migraphx_c PROPERTIES EXPORT_NAME c) migraphx_generate_export_header(migraphx_c DIRECTORY migraphx/api) @@ -43,5 +90,5 @@ target_link_libraries(migraphx_c PUBLIC migraphx_version) rocm_install_targets( TARGETS migraphx_c INCLUDE - ${CMAKE_CURRENT_SOURCE_DIR}/include + ${MIGRAPHX_API_GEN_INCLUDE_DIR} ) diff --git a/tools/generate.py b/tools/generate.py index c776cbc0399..72b46c07e33 100644 --- a/tools/generate.py +++ b/tools/generate.py @@ -41,40 +41,81 @@ def clang_format(buffer, **kwargs): **kwargs).stdout.decode('utf-8') -def api_generate(input_path: Path, output_path: Path): +def maybe_format(buffer, do_format=True): + return clang_format(buffer) if do_format else buffer + + +def api_generate(input_path: Path, output_path: Path, do_format=True): + output_path.parent.mkdir(parents=True, exist_ok=True) with open(output_path, 'w') as f: - f.write(clang_format(api.run(input_path))) + f.write(maybe_format(api.run(input_path), do_format)) -def te_generate(input_path: Path, output_path: Path): +def te_generate(input_path: Path, output_path: Path, do_format=True): with open(output_path, 'w') as f: - f.write(clang_format(te.run(input_path))) + f.write(maybe_format(te.run(input_path), do_format)) + + +def generate_all(do_format=True): + files = Path('include').absolute().iterdir() + for f in [f for f in files if f.is_file()]: + te_generate(f, src_dir / f'include/migraphx/{f.name}', do_format) + runpy.run_path(str(migraphx_py_path)) + api_generate(work_dir / 'api/migraphx.h', + src_dir / 'api/include/migraphx/migraphx.h', do_format) + print('Finished generating header migraphx.h') + api_generate(work_dir / 'api/api.cpp', src_dir / 'api/api.cpp', do_format) + print('Finished generating source api.cpp') + + +def generate_api(header_path: Path, source_path: Path, do_format=True): + runpy.run_path(str(migraphx_py_path)) + api_generate(work_dir / 'api/migraphx.h', header_path, do_format) + print(f'Finished generating header {header_path}') + api_generate(work_dir / 'api/api.cpp', source_path, do_format) + print(f'Finished generating source {source_path}') def main(): parser = argparse.ArgumentParser() parser.add_argument('-f', '--clang-format', type=Path) + parser.add_argument('--api-only', + action='store_true', + help='Only generate the C API files (migraphx.h and ' + 'api.cpp) to the paths given by --api-header and ' + '--api-source instead of writing into the source tree') + parser.add_argument('--api-header', + type=Path, + help='Output path for the generated migraphx.h header') + parser.add_argument('--api-source', + type=Path, + help='Output path for the generated api.cpp source') args = parser.parse_args() global clang_format_path if args.clang_format: clang_format_path = args.clang_format - if not clang_format_path.is_file(): - print(f"{clang_format_path}: invalid path or not installed", + # clang-format only affects the readability of the generated files, so it + # is optional in api-only mode where the output is just a build input. + do_format = clang_format_path.is_file() + if not do_format: + if not args.api_only: + print(f"{clang_format_path}: invalid path or not installed", + file=sys.stderr) + return + print(f"{clang_format_path}: clang-format not found, generating " + "without formatting", file=sys.stderr) - return + + if args.api_only and not (args.api_header and args.api_source): + parser.error('--api-only requires --api-header and --api-source') try: - files = Path('include').absolute().iterdir() - for f in [f for f in files if f.is_file()]: - te_generate(f, src_dir / f'include/migraphx/{f.name}') - runpy.run_path(str(migraphx_py_path)) - api_generate(work_dir / 'api/migraphx.h', - src_dir / 'api/include/migraphx/migraphx.h') - print('Finished generating header migraphx.h') - api_generate(work_dir / 'api/api.cpp', src_dir / 'api/api.cpp') - print('Finished generating source api.cpp') + if args.api_only: + generate_api(args.api_header, args.api_source, do_format) + else: + generate_all(do_format) except subprocess.CalledProcessError as ex: if ex.stdout: print(ex.stdout.decode('utf-8')) From 3975943dddaf7fff226d61c38fb5b8de17c89e5f Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 12 Jun 2026 13:54:38 -0500 Subject: [PATCH 2/5] Simplify --- src/api/CMakeLists.txt | 15 ++++--------- tools/generate.py | 48 +++++++++++++++++------------------------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 30ff8089687..c73e2e8a598 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -27,27 +27,20 @@ # output can be reviewed, but migraphx_c is built from a freshly generated copy # placed in the build directory instead of the checked-in src/api copies. find_package(Python 3 COMPONENTS Interpreter REQUIRED) -find_program(CLANG_FORMAT clang-format PATHS /opt/rocm/llvm ENV HIP_PATH PATH_SUFFIXES bin) set(MIGRAPHX_API_TOOLS_DIR ${PROJECT_SOURCE_DIR}/tools) set(MIGRAPHX_API_GEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) set(MIGRAPHX_API_GEN_HEADER ${MIGRAPHX_API_GEN_INCLUDE_DIR}/migraphx/migraphx.h) set(MIGRAPHX_API_GEN_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/api.cpp) -# clang-format only affects readability of the generated files, so it is -# optional for the build; pass it along only when it is available. -set(MIGRAPHX_API_FORMAT_ARG) -if(CLANG_FORMAT) - set(MIGRAPHX_API_FORMAT_ARG -f ${CLANG_FORMAT}) -endif() - +# The build copy is only a compiler input, so it is generated without +# clang-format; `make generate` still formats the reviewable source-tree copy. add_custom_command( OUTPUT ${MIGRAPHX_API_GEN_HEADER} ${MIGRAPHX_API_GEN_SOURCE} COMMAND ${Python_EXECUTABLE} ${MIGRAPHX_API_TOOLS_DIR}/generate.py --api-only --api-header ${MIGRAPHX_API_GEN_HEADER} --api-source ${MIGRAPHX_API_GEN_SOURCE} - ${MIGRAPHX_API_FORMAT_ARG} WORKING_DIRECTORY ${MIGRAPHX_API_TOOLS_DIR} DEPENDS ${MIGRAPHX_API_TOOLS_DIR}/generate.py @@ -61,8 +54,8 @@ add_custom_command( add_custom_target(generate_api DEPENDS ${MIGRAPHX_API_GEN_HEADER} ${MIGRAPHX_API_GEN_SOURCE}) -# The non-generated public headers (e.g. migraphx.hpp) live alongside the -# generated header so consumers get a complete include directory. +# migraphx.hpp is hand-written (not generated); copy it next to the generated +# migraphx.h so the build include directory is a complete public API. configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/migraphx/migraphx.hpp ${MIGRAPHX_API_GEN_INCLUDE_DIR}/migraphx/migraphx.hpp diff --git a/tools/generate.py b/tools/generate.py index 72b46c07e33..2d593c1af44 100644 --- a/tools/generate.py +++ b/tools/generate.py @@ -56,18 +56,6 @@ def te_generate(input_path: Path, output_path: Path, do_format=True): f.write(maybe_format(te.run(input_path), do_format)) -def generate_all(do_format=True): - files = Path('include').absolute().iterdir() - for f in [f for f in files if f.is_file()]: - te_generate(f, src_dir / f'include/migraphx/{f.name}', do_format) - runpy.run_path(str(migraphx_py_path)) - api_generate(work_dir / 'api/migraphx.h', - src_dir / 'api/include/migraphx/migraphx.h', do_format) - print('Finished generating header migraphx.h') - api_generate(work_dir / 'api/api.cpp', src_dir / 'api/api.cpp', do_format) - print('Finished generating source api.cpp') - - def generate_api(header_path: Path, source_path: Path, do_format=True): runpy.run_path(str(migraphx_py_path)) api_generate(work_dir / 'api/migraphx.h', header_path, do_format) @@ -76,6 +64,14 @@ def generate_api(header_path: Path, source_path: Path, do_format=True): print(f'Finished generating source {source_path}') +def generate_all(do_format=True): + files = Path('include').absolute().iterdir() + for f in [f for f in files if f.is_file()]: + te_generate(f, src_dir / f'include/migraphx/{f.name}', do_format) + generate_api(src_dir / 'api/include/migraphx/migraphx.h', + src_dir / 'api/api.cpp', do_format) + + def main(): parser = argparse.ArgumentParser() parser.add_argument('-f', '--clang-format', type=Path) @@ -92,30 +88,24 @@ def main(): help='Output path for the generated api.cpp source') args = parser.parse_args() + if args.api_only and not (args.api_header and args.api_source): + parser.error('--api-only requires --api-header and --api-source') + global clang_format_path if args.clang_format: clang_format_path = args.clang_format - # clang-format only affects the readability of the generated files, so it - # is optional in api-only mode where the output is just a build input. - do_format = clang_format_path.is_file() - if not do_format: - if not args.api_only: - print(f"{clang_format_path}: invalid path or not installed", - file=sys.stderr) - return - print(f"{clang_format_path}: clang-format not found, generating " - "without formatting", - file=sys.stderr) - - if args.api_only and not (args.api_header and args.api_source): - parser.error('--api-only requires --api-header and --api-source') - try: if args.api_only: - generate_api(args.api_header, args.api_source, do_format) + # These files are only consumed by the compiler, so skip + # clang-format; only `make generate` formats them for review. + generate_api(args.api_header, args.api_source, do_format=False) else: - generate_all(do_format) + if not clang_format_path.is_file(): + print(f"{clang_format_path}: invalid path or not installed", + file=sys.stderr) + return + generate_all() except subprocess.CalledProcessError as ex: if ex.stdout: print(ex.stdout.decode('utf-8')) From b9194a88c4de7e0a11a4615b9855e3dc995d53f5 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 12 Jun 2026 16:48:52 -0500 Subject: [PATCH 3/5] Output dir --- src/api/CMakeLists.txt | 9 +++++---- tools/generate.py | 27 ++++++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index c73e2e8a598..0f6b42cc88a 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -33,14 +33,15 @@ set(MIGRAPHX_API_GEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) set(MIGRAPHX_API_GEN_HEADER ${MIGRAPHX_API_GEN_INCLUDE_DIR}/migraphx/migraphx.h) set(MIGRAPHX_API_GEN_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/api.cpp) -# The build copy is only a compiler input, so it is generated without -# clang-format; `make generate` still formats the reviewable source-tree copy. +# generate.py writes migraphx.h under include/migraphx/ and api.cpp at the top +# level of the output dir, mirroring the source-tree layout. The build copy is +# only a compiler input, so it is generated without clang-format; `make +# generate` still formats the reviewable source-tree copy. add_custom_command( OUTPUT ${MIGRAPHX_API_GEN_HEADER} ${MIGRAPHX_API_GEN_SOURCE} COMMAND ${Python_EXECUTABLE} ${MIGRAPHX_API_TOOLS_DIR}/generate.py --api-only - --api-header ${MIGRAPHX_API_GEN_HEADER} - --api-source ${MIGRAPHX_API_GEN_SOURCE} + --api-output-dir ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${MIGRAPHX_API_TOOLS_DIR} DEPENDS ${MIGRAPHX_API_TOOLS_DIR}/generate.py diff --git a/tools/generate.py b/tools/generate.py index 2d593c1af44..5d87fd16456 100644 --- a/tools/generate.py +++ b/tools/generate.py @@ -56,8 +56,10 @@ def te_generate(input_path: Path, output_path: Path, do_format=True): f.write(maybe_format(te.run(input_path), do_format)) -def generate_api(header_path: Path, source_path: Path, do_format=True): +def generate_api(output_dir: Path, do_format=True): runpy.run_path(str(migraphx_py_path)) + header_path = output_dir / 'include/migraphx/migraphx.h' + source_path = output_dir / 'api.cpp' api_generate(work_dir / 'api/migraphx.h', header_path, do_format) print(f'Finished generating header {header_path}') api_generate(work_dir / 'api/api.cpp', source_path, do_format) @@ -68,8 +70,7 @@ def generate_all(do_format=True): files = Path('include').absolute().iterdir() for f in [f for f in files if f.is_file()]: te_generate(f, src_dir / f'include/migraphx/{f.name}', do_format) - generate_api(src_dir / 'api/include/migraphx/migraphx.h', - src_dir / 'api/api.cpp', do_format) + generate_api(src_dir / 'api', do_format) def main(): @@ -78,18 +79,18 @@ def main(): parser.add_argument('--api-only', action='store_true', help='Only generate the C API files (migraphx.h and ' - 'api.cpp) to the paths given by --api-header and ' - '--api-source instead of writing into the source tree') - parser.add_argument('--api-header', + 'api.cpp) under the directory given by ' + '--api-output-dir instead of writing into the source ' + 'tree') + parser.add_argument('--api-output-dir', type=Path, - help='Output path for the generated migraphx.h header') - parser.add_argument('--api-source', - type=Path, - help='Output path for the generated api.cpp source') + help='Base output directory for the generated C API ' + 'files: migraphx.h is written under include/migraphx/ ' + 'and api.cpp at the top level') args = parser.parse_args() - if args.api_only and not (args.api_header and args.api_source): - parser.error('--api-only requires --api-header and --api-source') + if args.api_only and not args.api_output_dir: + parser.error('--api-only requires --api-output-dir') global clang_format_path if args.clang_format: @@ -99,7 +100,7 @@ def main(): if args.api_only: # These files are only consumed by the compiler, so skip # clang-format; only `make generate` formats them for review. - generate_api(args.api_header, args.api_source, do_format=False) + generate_api(args.api_output_dir, do_format=False) else: if not clang_format_path.is_file(): print(f"{clang_format_path}: invalid path or not installed", From ad315dbe2683a1317dc1932460df333e13042cdc Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 12 Jun 2026 16:55:34 -0500 Subject: [PATCH 4/5] Update year --- src/api/CMakeLists.txt | 2 +- tools/generate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 0f6b42cc88a..9ebd8e29a73 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -1,7 +1,7 @@ ##################################################################################### # The MIT License (MIT) # -# Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2015-2026 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/tools/generate.py b/tools/generate.py index 5d87fd16456..7d9d11105b1 100644 --- a/tools/generate.py +++ b/tools/generate.py @@ -1,7 +1,7 @@ ##################################################################################### # The MIT License (MIT) # -# Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved. +# Copyright (c) 2015-2026 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From 63d47187a84222755c7eff64f5146fb11c0b448d Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 16 Jun 2026 09:17:20 -0500 Subject: [PATCH 5/5] Fix tidy --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9804fb298d0..0654b9f320b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -144,7 +144,7 @@ jobs: -DCLANG_TIDY_CACHE=/data/tidy-cache \ -DGPU_TARGETS=gfx908 \ .. - make -j$(nproc) -k onnx-proto tf-proto tidy + make -j$(nproc) -k onnx-proto tf-proto generate_api tidy # GH actions can not update existing cache, as a workaround clear cache and then save it - name: Clear tidy cache before saving