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 diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index 9e83fc6b240..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 @@ -22,9 +22,50 @@ # 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) + +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) + +# 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-output-dir ${CMAKE_CURRENT_BINARY_DIR} + 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}) + +# 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 + 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 +84,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..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 @@ -41,40 +41,72 @@ 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_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) + 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', do_format) 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) under the directory given by ' + '--api-output-dir instead of writing into the source ' + 'tree') + parser.add_argument('--api-output-dir', + type=Path, + 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_output_dir: + parser.error('--api-only requires --api-output-dir') + 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", - file=sys.stderr) - return - 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: + # These files are only consumed by the compiler, so skip + # clang-format; only `make generate` formats them for review. + 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", + file=sys.stderr) + return + generate_all() except subprocess.CalledProcessError as ex: if ex.stdout: print(ex.stdout.decode('utf-8'))