blob: f88cf1af447531fc0a1a2b846317c6b72bf2e6a8 [file] [log] [blame]
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2023-2024 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-2024 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")