| #!/usr/bin/python |
| # -*- coding: utf-8 -*- |
| |
| # Copyright (c) 2023 Arm Limited. |
| # |
| # SPDX-License-Identifier: MIT |
| # |
| # 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. |
| |
| """Generates build files for either bazel or cmake experimental builds using filelist.json |
| Usage |
| python scripts/generate_build_files.py --bazel |
| python scripts/generate_build_files.py --cmake |
| |
| Writes generated file to the bazel BUILD file located under src/ if using --bazel flag. |
| Writes generated file to the CMake CMakeLists.txt file located under src/ if using --cmake flag. |
| """ |
| |
| import argparse |
| import json |
| import glob |
| |
| |
| def get_operator_backend_files(filelist, operators, backend='', techs=[], attrs=[]): |
| files = {"common": []} |
| |
| # Early return if filelist is empty |
| if backend not in filelist: |
| return files |
| |
| # Iterate over operators and create the file lists to compiler |
| for operator in operators: |
| if operator in filelist[backend]['operators']: |
| files['common'] += filelist[backend]['operators'][operator]["files"]["common"] |
| for tech in techs: |
| if tech in filelist[backend]['operators'][operator]["files"]: |
| # Add tech as a key to dictionary if not there |
| if tech not in files: |
| files[tech] = [] |
| |
| # Add tech files to the tech file list |
| tech_files = filelist[backend]['operators'][operator]["files"][tech] |
| files[tech] += tech_files.get('common', []) |
| for attr in attrs: |
| files[tech] += tech_files.get(attr, []) |
| |
| # Remove duplicates if they exist |
| return {k: list(set(v)) for k, v in files.items()} |
| |
| |
| def collect_operators(filelist, operators, backend=''): |
| ops = set() |
| for operator in operators: |
| if operator in filelist[backend]['operators']: |
| ops.add(operator) |
| if 'deps' in filelist[backend]['operators'][operator]: |
| ops.update(filelist[backend]['operators'][operator]['deps']) |
| else: |
| print("Operator {0} is unsupported on {1} backend!".format( |
| operator, backend)) |
| |
| return ops |
| |
| |
| def resolve_operator_dependencies(filelist, operators, backend=''): |
| resolved_operators = collect_operators(filelist, operators, backend) |
| |
| are_ops_resolved = False |
| while not are_ops_resolved: |
| resolution_pass = collect_operators( |
| filelist, resolved_operators, backend) |
| if len(resolution_pass) != len(resolved_operators): |
| resolved_operators.update(resolution_pass) |
| else: |
| are_ops_resolved = True |
| |
| return resolved_operators |
| |
| def get_template_header(): |
| return """# Copyright (c) 2023 Arm Limited. |
| # |
| # SPDX-License-Identifier: MIT |
| # |
| # 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.""" |
| |
| def build_from_template_bazel(srcs_graph, srcs_sve, srcs_sve2, srcs_core): |
| |
| line_separator = '",\n\t"' |
| |
| template = f"""{get_template_header()} |
| |
| filegroup( |
| name = "arm_compute_graph_srcs", |
| srcs = ["{line_separator.join(srcs_graph)}"] + |
| glob(["**/*.h", |
| "**/*.hpp", |
| "**/*.inl"]), |
| visibility = ["//visibility:public"] |
| ) |
| |
| filegroup( |
| name = "arm_compute_sve2_srcs", |
| srcs = ["{line_separator.join(srcs_sve2)}"] + |
| glob(["**/*.h", |
| "**/*.hpp", |
| "**/*.inl"]), |
| visibility = ["//visibility:public"] |
| ) |
| |
| filegroup( |
| name = "arm_compute_sve_srcs", |
| srcs = ["{line_separator.join(srcs_sve)}"] + |
| glob(["**/*.h", |
| "**/*.hpp", |
| "**/*.inl"]), |
| visibility = ["//visibility:public"] |
| ) |
| |
| filegroup( |
| name = "arm_compute_srcs", |
| srcs = ["{line_separator.join(srcs_core)}"] + |
| glob(["**/*.h", |
| "**/*.hpp", |
| "**/*.inl"]), |
| visibility = ["//visibility:public"] |
| ) |
| """ |
| |
| return template |
| |
| |
| def build_from_template_cmake(srcs_graph, srcs_sve, srcs_sve2, srcs_core): |
| |
| line_separator = '\n\t' |
| |
| template = f"""{get_template_header()} |
| |
| target_sources( |
| arm_compute_graph |
| PRIVATE |
| {line_separator.join(srcs_graph)} |
| ) |
| |
| target_sources( |
| arm_compute_sve |
| PRIVATE |
| {line_separator.join(srcs_sve)} |
| ) |
| |
| target_sources( |
| arm_compute_sve2 |
| PRIVATE |
| {line_separator.join(srcs_sve2)} |
| ) |
| |
| target_sources( |
| arm_compute |
| PRIVATE |
| {line_separator.join(srcs_core)} |
| )""" |
| return template |
| |
| |
| def gather_sources(): |
| |
| # Source file list |
| with open("filelist.json") as fp: |
| filelist = json.load(fp) |
| |
| # Common backend files |
| lib_files = filelist['common'] |
| |
| # Logging files |
| lib_files += filelist['logging'] |
| |
| # C API files |
| lib_files += filelist['c_api']['common'] |
| lib_files += filelist['c_api']['operators'] |
| |
| # Scheduler infrastructure |
| lib_files += filelist['scheduler']['single'] |
| # Add both cppthreads and omp sources for now |
| lib_files += filelist['scheduler']['threads'] |
| lib_files += filelist['scheduler']['omp'] |
| |
| # Graph files |
| graph_files = glob.glob('src/graph/*.cpp') |
| graph_files += glob.glob('src/graph/*/*.cpp') |
| |
| lib_files_sve = [] |
| lib_files_sve2 = [] |
| |
| # ------------------------------------- |
| # NEON files |
| lib_files += filelist['cpu']['common'] |
| simd = ['neon', 'sve', 'sve2'] |
| |
| # Get attributes |
| data_types = ["qasymm8", "qasymm8_signed", "qsymm16", |
| "fp16", "fp32", "integer"] |
| data_layouts = ["nhwc", "nchw"] |
| fixed_format_kernels = ["fixed_format_kernels"] |
| attrs = data_types + data_layouts + \ |
| fixed_format_kernels + ["estate64"] |
| |
| # Setup data-type and data-layout files to include |
| cpu_operators = filelist['cpu']['operators'].keys() |
| cpu_ops_to_build = resolve_operator_dependencies( |
| filelist, cpu_operators, 'cpu') |
| cpu_files = get_operator_backend_files( |
| filelist, cpu_ops_to_build, 'cpu', simd, attrs) |
| |
| # Shared among ALL CPU files |
| lib_files += cpu_files.get('common', []) |
| |
| # Arm® Neon™ specific files |
| lib_files += cpu_files.get('neon', []) |
| |
| # SVE files only |
| lib_files_sve = cpu_files.get('sve', []) |
| |
| # SVE2 files only |
| lib_files_sve2 = cpu_files.get('sve2', []) |
| |
| graph_files += glob.glob('src/graph/backends/NEON/*.cpp') |
| |
| # ------------------------------------- |
| |
| graph_files = sorted([path.replace("src/", "") for path in graph_files]) |
| lib_files_sve = sorted([path.replace("src/", "") for path in lib_files_sve]) |
| lib_files_sve2 = sorted([path.replace("src/", "") for path in lib_files_sve2]) |
| lib_files = sorted([path.replace("src/", "") for path in lib_files]) |
| |
| return graph_files, lib_files_sve, lib_files_sve2, lib_files |
| |
| |
| if "__main__" in __name__: |
| |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--bazel", action="store_true") |
| parser.add_argument("--cmake", action="store_true") |
| args = parser.parse_args() |
| |
| graph_files, lib_files_sve, lib_files_sve2, lib_files = gather_sources() |
| |
| if args.bazel: |
| # 8562a4ec: Remove CommonGraphOptions from Utils target and warnings |
| graph_files += ["//utils:CommonGraphOptions.cpp"] |
| |
| bazel_build_string = build_from_template_bazel( |
| graph_files, lib_files_sve, lib_files_sve2, lib_files) |
| with open("src/BUILD.bazel", "w") as fp: |
| fp.write(bazel_build_string) |
| |
| if args.cmake: |
| cmake_build_string = build_from_template_cmake( |
| graph_files, lib_files_sve, lib_files_sve2, lib_files) |
| with open("src/CMakeLists.txt", "w") as fp: |
| fp.write(cmake_build_string) |
| |
| if not args.cmake and not args.bazel: |
| print("Supply either --bazel or --cmake flag to generate build files for corresponding build") |