diff --git a/CMakeLists.txt b/CMakeLists.txt index 73bdc0b4a1..e316cd6c74 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ include(GNUInstallDirs) option(BUILD_TESTS "Build test suite" OFF) # TODO: Enable once virtualenv is installed on CI machines option(BUILD_PACKAGE "Build python package" OFF) +option(ENABLE_CLI "Enable AMDSMI-CLI install" OFF) option(ENABLE_LDCONFIG "Set library links and caches using ldconfig." ON) # Set share path here because project name != amd_smi @@ -134,11 +135,15 @@ if(BUILD_PACKAGE) add_subdirectory("py-interface") endif() +if(ENABLE_CLI) + add_subdirectory("amdsmi_cli") +endif() + include(CMakePackageConfigHelpers) configure_package_config_file( amd_smi-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/amd_smi-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/amd_smi + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${AMD_SMI} PATH_VARS CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_BINDIR) write_basic_package_version_file( diff --git a/README.md b/README.md index 25f8225bec..21975b0c3d 100755 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ - - # AMD System Management Interface (AMD SMI) Library The AMD System Management Interface Library, or AMD SMI library, is a C library for Linux that provides a user space interface for applications to monitor and control AMD devices. ## Supported platforms + At initial release, the AMD SMI library will support Linux bear metal and Linux virtual machine guest for AMD GPUs. In the future release, the library will be extended to support AMD EPYC™ CPUs. AMD SMI library can run on AMD ROCm supported platforms, please refer to [List of Supported Operating Systems and GPUs](https://docs.amd.com/bundle/ROCm-Getting-Started-Guide-v5.3/page/Introduction_to_ROCm_Getting_Started_Guide_for_Linux.html) @@ -12,9 +11,9 @@ AMD SMI library can run on AMD ROCm supported platforms, please refer to [List o To run the AMD SMI library, the amdgpu driver needs to be installed. Optionally, the libdrm can be installed to query firmware information and hardware IPs. -# Building AMD SMI +## Building AMD SMI -## Additional Required software for building +### Additional Required software for building In order to build the AMD SMI library, the following components are required. Note that the software versions listed are what was used in development. Earlier versions are not guaranteed to work: * CMake (v3.11.0) - `pip3 install cmake` @@ -26,31 +25,35 @@ In order to build the AMD SMI python package, the following components are requi * virtualenv - `pip3 install virtualenv` In order to build the latest documentation, the following are required: + * DOxygen (1.8.11) * latex (pdfTeX 3.14159265-2.6-1.40.16) The source code for AMD SMI is available on Github. -After the AMD SMI library git repository has been cloned to a local Linux machine, building the library is achieved by following the typical CMake build sequence. Specifically, +After the AMD SMI library git repository has been cloned to a local Linux machine, the Default location for the library and headers is /opt/rocm. Building the library is achieved by following the typical CMake build sequence, specifically: + ```bash mkdir -p build cd build cmake .. make -j $(nproc) -# Install library file and header; default location is /opt/rocm -make install``` +make install +``` The built library will appear in the `build` folder. To build the rpm and deb packages follow the above steps with: -```bash -make package``` -## Documentation +```bash +make package +``` + +### Documentation The reference manual, `refman.pdf` will be in the `latex` directory upon a successful build. -## Building the Tests +### Building the Tests In order to verify the build and capability of AMD SMI on your system and to see an example of how AMD SMI can be used, you may build and run the tests that are available in the repo. To build the tests, follow these steps: @@ -61,21 +64,23 @@ cmake -DBUILD_TESTS=ON make -j $(nproc) ``` -## Run the Tests +### Run the Tests To run the test, execute the program `amdsmitst` that is built from the steps above. -# Usage Basics +## Usage Basics + +### Device/Socket handles -## Device/Socket handles Many of the functions in the library take a "socket handle" or "device handle". The socket is an abstraction of hardware physical socket. This will enable amd-smi to provide a better representation of the hardware to user. Although there is always one distinct GPU for a socket, the APU may have both GPU device and CPU device on the same socket. Moreover, for MI200, it may have multiple GCDs. To discover the sockets in the system, `amdsmi_get_socket_handles()` is called to get list of sockets handles, which in turn can be used to query the devices in that socket using `amdsmi_get_device_handles()`. The device handler is used to distinguish the detected devices from one another. It is important to note that a device may end up with a different device handles after restart application, so a device handle should not be relied upon to be constant over process. -# Hello AMD SMI -The only required AMD-SMI call for any program that wants to use AMD-SMI is the `amdsmi_init()` call. This call initializes some internal data structures that will be used by subsequent AMD-SMI calls. In the call, a flag can be passed if the application is only interested in a specific device type. +## Hello AMD SMI + +The only required AMD-SMI call for any program that wants to use AMD-SMI is the `amdsmi_init()` call. This call initializes some internal data structures that will be used by subsequent AMD-SMI calls. In the call, a flag can be passed if the application is only interested in a specific device type. When AMD-SMI is no longer being used, `amdsmi_shut_down()` should be called. This provides a way to do any releasing of resources that AMD-SMI may have held. diff --git a/amdsmi_cli/CMakeLists.txt b/amdsmi_cli/CMakeLists.txt new file mode 100644 index 0000000000..712c955f91 --- /dev/null +++ b/amdsmi_cli/CMakeLists.txt @@ -0,0 +1,89 @@ +message("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") +message(" CMake AMDSMI CLI Install ") +message("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") + + +# Set CLI Build Directory +set(CLI_BUILD_DIR "amdsmi_cli" CACHE STRING "") +set(SHARE_WHL_LOCATION "${SHARE_INSTALL_PREFIX}/${AMD_SMI}/amdsmi-0.1-py3-none-any.whl") + +# The CLI requires the python amdsmi wrapper whl file to exist +add_custom_target( + amdsmi_cli + DEPENDS SHARE_WHL_LOCATION) + +# if Python3 is found but the version is below 3.7 - Python3_FOUND is set to FALSE +find_package(Python3 3.7 COMPONENTS Interpreter) + +# WARN: This is a HACK to pass compile on AMD rhel8 CI systems! +# Those still use python3.6 which is too old for this project! +if(NOT Python3_FOUND) + message(AUTHOR_WARNING "Python3 DOESN'T EXIST OR VERSION IS TOO OLD!: ${Python3_VERSION}") + # WARN: EXIT CURRENT CMAKE FILE + return() +endif() + +add_custom_target( + amdsmi_whl_install + COMMAND ${Python3_EXECUTABLE} -m pip install --upgrade --force-reinstall SHARE_WHL_LOCATION) + +# symlinking instead of copying avoids unnecessarry regeneration of packaged files +add_custom_command( + OUTPUT ${CLI_BUILD_DIR}/pyproject.toml + ${CLI_BUILD_DIR}/__init__.py + ${CLI_BUILD_DIR}/_version.py + ${CLI_BUILD_DIR}/amdsmi_cli.py + ${CLI_BUILD_DIR}/amdsmi_commands.py + ${CLI_BUILD_DIR}/amdsmi_helpers.py + ${CLI_BUILD_DIR}/amdsmi_logger.py + ${CLI_BUILD_DIR}/amdsmi_parser.py + ${CLI_BUILD_DIR}/BDF.py + DEPENDS amdsmi_cli + amdsmi_whl_install + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/_version.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_cli.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_commands.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_helpers.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_logger.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_parser.py ${CLI_BUILD_DIR}/ + COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/BDF.py ${CLI_BUILD_DIR}/) + +install( + FILES _version.py + amdsmi_commands.py + amdsmi_helpers.py + amdsmi_init.py + amdsmi_logger.py + amdsmi_parser.py + BDF.py + DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/${AMD_SMI}/amdsmi_cli) + +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/amdsmi_cli.py + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ + GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/${AMD_SMI}/amdsmi_cli) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/bin/amd-smi + DESTINATION ${CMAKE_INSTALL_BINDIR}) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/bin/gpuv-smi + DESTINATION ${CMAKE_INSTALL_BINDIR}) + +# symlink amdsmi_cli.py to amd-smi +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +add_custom_target( + link_amdsmi_cli ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E create_symlink + ../${CMAKE_INSTALL_LIBEXECDIR}/${AMD_SMI}/amdsmi_cli/amdsmi_cli.py + ${CMAKE_CURRENT_BINARY_DIR}/bin/amd-smi) + +add_custom_target( + link_gpuvsmi_cli ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E create_symlink + ../${CMAKE_INSTALL_LIBEXECDIR}/${AMD_SMI}/amdsmi_cli/amdsmi_cli.py + ${CMAKE_CURRENT_BINARY_DIR}/bin/gpuv-smi) diff --git a/amdsmi_cli/__init__.py b/amdsmi_cli/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/amdsmi_cli/amdsmi_cli.py b/amdsmi_cli/amdsmi_cli.py index 38e346fb9e..588e7f8d2b 100755 --- a/amdsmi_cli/amdsmi_cli.py +++ b/amdsmi_cli/amdsmi_cli.py @@ -25,10 +25,18 @@ import sys from amdsmi_commands import AMDSMICommands from amdsmi_parser import AMDSMIParser - +from amdsmi_logger import AMDSMILogger if __name__ == "__main__": - amd_smi_commands = AMDSMICommands() + # Set compatability mode based on which cli mapping user selects + if 'gpuv-smi' in sys.argv[0]: + compatibility = AMDSMILogger.LoggerCompatibility.gpuvsmi.value + elif 'rocm-smi' in sys.argv[0]: + compatibility = AMDSMILogger.LoggerCompatibility.rocmsmi.value + else: + compatibility = AMDSMILogger.LoggerCompatibility.amdsmi.value + + amd_smi_commands = AMDSMICommands(compatibility=compatibility) amd_smi_parser = AMDSMIParser(amd_smi_commands.version, amd_smi_commands.discovery, amd_smi_commands.static, @@ -59,8 +67,6 @@ if __name__ == "__main__": 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL} logging.basicConfig(format='%(levelname)s: %(message)s', level=logging_dict[args.loglevel]) - if args.compatibility: - amd_smi_commands.logger.compatibility = args.compatibility # Execute subcommands args.func(args) diff --git a/amdsmi_cli/amdsmi_commands.py b/amdsmi_cli/amdsmi_commands.py index 5a8129e1bb..a661c07d31 100644 --- a/amdsmi_cli/amdsmi_commands.py +++ b/amdsmi_cli/amdsmi_commands.py @@ -31,8 +31,9 @@ destination. from _version import __version__ -from amdsmi_helpers import * +from amdsmi_helpers import AMDSMIHelpers from amdsmi_logger import AMDSMILogger +from amdsmi import amdsmi_interface class AMDSMICommands(): @@ -744,9 +745,14 @@ class AMDSMICommands(): 'rpm' : fan_rpm, 'usage' : fan_percent} if args.pcie_usage: - pcie_link_status = amdsmi_interface.amdsmi_get_pcie_link_status(args.gpu) + try: + pcie_link_status = amdsmi_interface.amdsmi_get_pcie_link_status(args.gpu) + pcie_link_status_call = True + except amdsmi_interface.AmdSmiLibraryException as err: + pcie_link_status = err.get_error_info() + pcie_link_status_call = False - if self.logger.is_human_readable_format(): + if self.logger.is_human_readable_format() and pcie_link_status_call: unit ='MT/s' pcie_link_status['pcie_speed'] = f"{pcie_link_status['pcie_speed']} {unit}" diff --git a/amdsmi_cli/amdsmi_init.py b/amdsmi_cli/amdsmi_init.py index 0c1c941e51..415ce4d3c2 100644 --- a/amdsmi_cli/amdsmi_init.py +++ b/amdsmi_cli/amdsmi_init.py @@ -29,8 +29,7 @@ import sys from pathlib import Path -sys.path.insert( 0, "../build/python_package/amdsmi") -import amdsmi_interface +import amdsmi as amdsmi_interface # Using basic python logging for user errors and development logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR) # User level logging diff --git a/amdsmi_cli/amdsmi_logger.py b/amdsmi_cli/amdsmi_logger.py index ec95b8feaa..fd37f2bb19 100644 --- a/amdsmi_cli/amdsmi_logger.py +++ b/amdsmi_cli/amdsmi_logger.py @@ -34,7 +34,7 @@ class AMDSMILogger(): self.output = {} self.multiple_device_output = [] self.watch_output = [] - self.compatibility = compatibility # amdsmi, gpuvsmi, or rocmsmi + self.compatibility = compatibility # amd-smi, gpuv-smi, or rocm-smi self.format = format # csv, json, or human_readable self.destination = destination # stdout, path to a file (append) self.amd_smi_helpers = AMDSMIHelpers() @@ -46,7 +46,7 @@ class AMDSMILogger(): csv = 'csv' human_readable = 'human_readable' - class Loggercompatibility(Enum): + class LoggerCompatibility(Enum): """Enum for logger compatibility""" amdsmi = 'amdsmi' rocmsmi = 'rocmsmi' @@ -66,15 +66,15 @@ class AMDSMILogger(): def is_amdsmi_compatibility(self): - return self.compatibility == self.Loggercompatibility.amdsmi.value + return self.compatibility == self.LoggerCompatibility.amdsmi.value def is_rocmsmi_compatibility(self): - return self.compatibility == self.Loggercompatibility.rocmsmi.value + return self.compatibility == self.LoggerCompatibility.rocmsmi.value def is_gpuvsmi_compatibility(self): - return self.compatibility == self.Loggercompatibility.gpuvsmi.value + return self.compatibility == self.LoggerCompatibility.gpuvsmi.value def store_output(self, device_handle, argument, data): diff --git a/amdsmi_cli/amdsmi_parser.py b/amdsmi_cli/amdsmi_parser.py index 03885d903e..0fd0b1122a 100644 --- a/amdsmi_cli/amdsmi_parser.py +++ b/amdsmi_cli/amdsmi_parser.py @@ -183,7 +183,6 @@ class AMDSMIParser(argparse.ArgumentParser): csv_help = "Displays output in CSV format (human readable by default)." file_help = "Saves output into a file on the provided path (stdout by default)." loglevel_help = "Set the logging level for the parser commands" - compatibility_help = "Display output in gpuvsmi or rocmsmi legacy format if possible" command_modifier_group = subcommand_parser.add_argument_group('Command Modifiers') @@ -197,10 +196,6 @@ class AMDSMIParser(argparse.ArgumentParser): command_modifier_group.add_argument('--loglevel', action='store', required=False, help=loglevel_help, default='ERROR', choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]) - # Output Compatibility options - command_modifier_group.add_argument('--compatibility', action='store', required=False, help=compatibility_help, - choices=["amdsmi", "gpuvsmi", "rocmsmi"], default='amdsmi') - def _add_device_arguments(self, subcommand_parser, required=False): # Device arguments help text diff --git a/py-interface/CMakeLists.txt b/py-interface/CMakeLists.txt index 6d44b32587..5407e88d78 100644 --- a/py-interface/CMakeLists.txt +++ b/py-interface/CMakeLists.txt @@ -64,11 +64,11 @@ else() # extract clang version manually because find_package(clang) doesn't work execute_process(COMMAND ${clang} --version OUTPUT_VARIABLE clang_full_version_string) string (REGEX REPLACE ".*clang version ([0-9]+\\.[0-9]+).*" "\\1" CLANG_VERSION_STRING ${clang_full_version_string}) - if(CLANG_VERSION_STRING VERSION_EQUAL 14.0) + if(CLANG_VERSION_STRING VERSION_EQUAL clang_ver) message("GOOD CLANG VERSION: ${CLANG_VERSION_STRING}") set(GOOD_CLANG_FOUND TRUE) else() - message(AUTHOR_WARNING "CLANG VERSION TOO OLD!: ${CLANG_VERSION_STRING}") + message(AUTHOR_WARNING "${clang} VERSION TOO OLD!: ${CLANG_VERSION_STRING}") endif() endif() if(NOT GOOD_CLANG_FOUND) diff --git a/py-interface/amdsmi_interface.py b/py-interface/amdsmi_interface.py index 55ade08abb..0f45c8dc08 100644 --- a/py-interface/amdsmi_interface.py +++ b/py-interface/amdsmi_interface.py @@ -26,8 +26,7 @@ from enum import IntEnum from collections.abc import Iterable from . import amdsmi_wrapper -from amdsmi_exception import * - +from .amdsmi_exception import * class AmdSmiInitFlags(IntEnum): ALL_DEVICES = amdsmi_wrapper.AMDSMI_INIT_ALL_DEVICES diff --git a/py-interface/amdsmi_wrapper.py b/py-interface/amdsmi_wrapper.py index 19fde95fcf..488e84fc4e 100644 --- a/py-interface/amdsmi_wrapper.py +++ b/py-interface/amdsmi_wrapper.py @@ -20,7 +20,6 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -import os # -*- coding: utf-8 -*- # # TARGET arch is: ['-I/usr/lib/llvm-14/lib/clang/14.0.0/include'] @@ -28,8 +27,10 @@ import os # POINTER_SIZE is: 8 # LONGDOUBLE_SIZE is: 16 # -import ctypes +import os +import ctypes +from pathlib import Path c_int128 = ctypes.c_ubyte*16 c_uint128 = c_int128 @@ -168,16 +169,23 @@ def char_pointer_cast(string, encoding='utf-8'): _libraries = {} -if os.path.isfile('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libamd_smi.so'): - # try to find library in install directory provided by CMake - _libraries['libamd_smi.so'] = ctypes.CDLL(os.path.join('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@', 'libamd_smi.so')) -elif os.path.isfile('/opt/rocm/lib/libamd_smi.so'): - # try /opt/rocm/lib as a fallback - _libraries['libamd_smi.so'] = ctypes.CDLL(os.path.join('/opt/rocm/lib', 'libamd_smi.so')) -else: - # lastly - search in current directory - _libraries['libamd_smi.so'] = ctypes.CDLL(os.path.join(os.path.dirname(__file__), 'libamd_smi.so')) +libamd_smi_cpack = Path("@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libamd_smi.so") +libamd_smi_optrocm = Path("/opt/rocm/lib/libamd_smi.so") +libamd_smi_parent_dir = Path(__file__).resolve().parent / "libamd_smi.so" +libamd_smi_cwd = Path.cwd() +if libamd_smi_cpack.is_file(): + # try to find library in install directory provided by CMake + _libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_cpack) +elif libamd_smi_optrocm.is_file(): + # try /opt/rocm/lib as a fallback + _libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_optrocm) +elif libamd_smi_parent_dir.is_file(): + # try to fall back to parent directory + _libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_parent_dir) +else: + # lastly - search in current working directory + _libraries['libamd_smi.so'] = ctypes.CDLL(libamd_smi_cwd) diff --git a/tools/generator.py b/tools/generator.py index 6eb0f335ed..fb380b6c20 100644 --- a/tools/generator.py +++ b/tools/generator.py @@ -1,131 +1,137 @@ -# -# Copyright (C) 2023 Advanced Micro Devices. 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 in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - - -import os -import sys -import argparse -import tempfile -import shutil -import platform -from subprocess import run, PIPE -from ctypeslib.clang2py import main as clangToPy - -HEADER = \ -""" -# -# Copyright (C) 2023 Advanced Micro Devices. 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 in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -import os -""" - - -def parseArgument(): - parser = argparse.ArgumentParser(description="parse input arguments") - parser.add_argument('-o','--output', type=str, required=True, - help='The output file name') - parser.add_argument('-i','--input', type=str, required=True, - help='The input file name') - parser.add_argument('-l', '--library', type=str, required=True, - help='Loading dynamic link libraries') - args = vars(parser.parse_args()) - - return args['output'], args['input'], args['library'] - - -def replace_line(full_path_file_name, string_to_repalce, new_string): - fh, abs_path = tempfile.mkstemp() - with os.fdopen(fh, 'w') as new_file: - new_file.write(HEADER) - with open(full_path_file_name, 'r+') as old_file: - for line in old_file: - new_file.write(line.replace(string_to_repalce, new_string)) - - shutil.copymode(full_path_file_name, abs_path) - os.remove(full_path_file_name) - shutil.move(abs_path, full_path_file_name) - - -def main(): - output_file, input_file, library = parseArgument() - - library_name = os.path.basename(library) - - clang_include_dir = \ - run(["clang", "--print-resource-dir"], stdout=PIPE, stderr=PIPE, encoding="utf-8").stdout.strip() - - os_platform = platform.system() - if os_platform == "Windows": - clang_include_dir += "\include" - if "Program Files(x86)" in clang_include_dir: - clang_include_dir = clang_include_dir.replace("Program Files(x86)", "Progra~2") - elif "Program Files" in clang_include_dir: - clang_include_dir = clang_include_dir.replace("Program Files", "Progra~1") - - arguments = [input_file, "-o", output_file] - line_to_replace = "_libraries['FIXME_STUB'] = FunctionFactoryStub() # ctypes.CDLL('FIXME_STUB')" - new_line = "_libraries['FIXME_STUB'] = ctypes.CDLL('{}')".format(library_name) - elif os_platform == "Linux": - clang_include_dir += "/include" - arguments = [input_file, "-o", output_file, "-l", library] - library_path = os.path.join(os.path.dirname(__file__), library) - line_to_replace = "_libraries['{}'] = ctypes.CDLL('{}')".format(library_name, library_path) - new_line = """ -if os.path.isfile('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/{0}'): - # try to find library in install directory provided by CMake - _libraries['{0}'] = ctypes.CDLL(os.path.join('@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@', '{0}')) -elif os.path.isfile('/opt/rocm/lib/{0}'): - # try /opt/rocm/lib as a fallback - _libraries['{0}'] = ctypes.CDLL(os.path.join('/opt/rocm/lib', '{0}')) -else: - # lastly - search in current directory - _libraries['{0}'] = ctypes.CDLL(os.path.join(os.path.dirname(__file__), '{0}')) -""".format(library_name) - else: - print("Unknown operating system. It is only supporing Linux and Windows.") - return - - arguments.append(f"--clang-args=-I" + clang_include_dir) - clangToPy(arguments) - - replace_line(output_file, line_to_replace, new_line) - - -if __name__ == "__main__": - main() +# +# Copyright (C) 2023 Advanced Micro Devices. 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 in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + + +import os +import argparse +import tempfile +import shutil +import platform +from subprocess import run, PIPE +from ctypeslib.clang2py import main as clangToPy + +HEADER = \ +""" +# +# Copyright (C) 2023 Advanced Micro Devices. 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 in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import os +""" + + +def parseArgument(): + parser = argparse.ArgumentParser(description="parse input arguments") + parser.add_argument('-o','--output', type=str, required=True, + help='The output file name') + parser.add_argument('-i','--input', type=str, required=True, + help='The input file name') + parser.add_argument('-l', '--library', type=str, required=True, + help='Loading dynamic link libraries') + args = vars(parser.parse_args()) + + return args['output'], args['input'], args['library'] + + +def replace_line(full_path_file_name, string_to_repalce, new_string): + fh, abs_path = tempfile.mkstemp() + with os.fdopen(fh, 'w') as new_file: + new_file.write(HEADER) + with open(full_path_file_name, 'r+', encoding='UTF-8') as old_file: + for line in old_file: + new_file.write(line.replace(string_to_repalce, new_string)) + + shutil.copymode(full_path_file_name, abs_path) + os.remove(full_path_file_name) + shutil.move(abs_path, full_path_file_name) + + +def main(): + output_file, input_file, library = parseArgument() + + library_name = os.path.basename(library) + + clang_include_dir = \ + run(["clang", "--print-resource-dir"], stdout=PIPE, stderr=PIPE, encoding="utf-8").stdout.strip() + + os_platform = platform.system() + if os_platform == "Windows": + clang_include_dir += "\include" + if "Program Files(x86)" in clang_include_dir: + clang_include_dir = clang_include_dir.replace("Program Files(x86)", "Progra~2") + elif "Program Files" in clang_include_dir: + clang_include_dir = clang_include_dir.replace("Program Files", "Progra~1") + + arguments = [input_file, "-o", output_file] + line_to_replace = "_libraries['FIXME_STUB'] = FunctionFactoryStub() # ctypes.CDLL('FIXME_STUB')" + new_line = "_libraries['FIXME_STUB'] = ctypes.CDLL('{}')".format(library_name) + elif os_platform == "Linux": + clang_include_dir += "/include" + arguments = [input_file, "-o", output_file, "-l", library] + library_path = os.path.join(os.path.dirname(__file__), library) + line_to_replace = "_libraries['{}'] = ctypes.CDLL('{}')".format(library_name, library_path) + new_line = f"""from pathlib import Path +libamd_smi_cpack = Path("@CPACK_PACKAGING_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/{library_name}") +libamd_smi_optrocm = Path("/opt/rocm/lib/{library_name}") +libamd_smi_parent_dir = Path(__file__).resolve().parent / "{library_name}" +libamd_smi_cwd = Path.cwd() + +if libamd_smi_cpack.is_file(): + # try to find library in install directory provided by CMake + _libraries['{library_name}'] = ctypes.CDLL(libamd_smi_cpack) +elif libamd_smi_optrocm.is_file(): + # try /opt/rocm/lib as a fallback + _libraries['{library_name}'] = ctypes.CDLL(libamd_smi_optrocm) +elif libamd_smi_parent_dir.is_file(): + # try to fall back to parent directory + _libraries['{library_name}'] = ctypes.CDLL(libamd_smi_parent_dir) +else: + # lastly - search in current working directory + _libraries['{library_name}'] = ctypes.CDLL(libamd_smi_cwd)""" + else: + print("Unknown operating system. It is only supporing Linux and Windows.") + return + + arguments.append("--clang-args=-I" + clang_include_dir) + clangToPy(arguments) + + replace_line(output_file, line_to_replace, new_line) + + +if __name__ == "__main__": + main()