blob: d3e71cda1b709b61e9d18e75e9eb850f96f72487 [file] [log] [blame]
# Copyright (c) 2016, 2017 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.
import collections
import os.path
import re
import subprocess
import SCons
VERSION = "v17.05"
SONAME_VERSION="2.0.0"
Import('env')
Import('vars')
def version_at_least(version, required):
end = min(len(version), len(required))
for i in range(0, end, 2):
if int(version[i]) < int(required[i]):
return False
elif int(version[i]) > int(required[i]):
return True
return True
def build_library(name, sources, libs, static=False):
if static:
obj = env.StaticLibrary(name, source = sources, LIBS=libs )
else:
if env['set_soname']:
obj = env.SharedLibrary(name, source = sources, LIBS=libs, SHLIBVERSION=SONAME_VERSION)
symlinks = []
# Manually delete symlinks or SCons will get confused:
directory = os.path.dirname( obj[0].path )
library_prefix = obj[0].path[:-(1+len(SONAME_VERSION))]
real_lib="%s.%s" % (library_prefix, SONAME_VERSION)
for f in Glob( "#%s*" % library_prefix):
if str(f) != real_lib:
symlinks.append("%s/%s" % (directory,str(f)))
clean = env.Command('clean-%s' % str(obj[0]), [], Delete(symlinks))
Default(clean)
Depends(obj, clean)
else:
obj = env.SharedLibrary(name, source = sources, LIBS=libs)
Default(obj)
return obj
def resolve_includes(target, source, env):
# File collection
FileEntry = collections.namedtuple('FileEntry', 'target_name file_contents')
# Include pattern
pattern = re.compile("#include \"(.*)\"")
# Get file contents
files = []
for s in source:
name = s.rstr().split("/")[-1]
contents = s.get_contents().splitlines()
embed_target_name = s.abspath + "embed"
entry = FileEntry(target_name=embed_target_name, file_contents=contents)
files.append((name,entry))
# Create dictionary of tupled list
files_dict = dict(files)
# Check for includes (can only be files in the same folder)
final_files = []
for file in files:
done = False
tmp_file = file[1].file_contents
while not done:
file_count = 0
updated_file = []
for line in tmp_file:
found = pattern.search(line)
if found:
include_file = found.group(1)
data = files_dict[include_file].file_contents
updated_file.extend(data)
else:
updated_file.append(line)
file_count += 1
# Check if all include are replaced.
if file_count == len(tmp_file):
done = True
# Update temp file
tmp_file = updated_file
# Append and prepend string literal identifiers and add expanded file to final list
tmp_file.insert(0, "R\"(\n")
tmp_file.append("\n)\"")
entry = FileEntry(target_name=file[1].target_name, file_contents=tmp_file)
final_files.append((file[0], entry))
# Write output files
for file in final_files:
with open(file[1].target_name, 'w+') as out_file:
contents = file[1].file_contents
for line in contents:
out_file.write("%s\n" % line)
if GetOption("help"):
Exit(0)
flags = ['-D_GLIBCXX_USE_NANOSLEEP','-Wno-deprecated-declarations','-Wall','-DARCH_ARM',
'-Wextra','-Wno-unused-parameter','-pedantic','-Wdisabled-optimization','-Wformat=2',
'-Winit-self','-Wstrict-overflow=2','-Wswitch-default',
'-fpermissive','-std=gnu++11','-Wno-vla','-Woverloaded-virtual',
'-Wctor-dtor-privacy','-Wsign-promo','-Weffc++','-Wno-format-nonliteral','-Wno-overlength-strings','-Wno-strict-overflow']
if env['neon'] and 'x86' in env['arch']:
print "Cannot compile NEON for x86"
Exit(1)
if env['set_soname'] and not version_at_least(SCons.__version__, "2.4"):
print "Setting the library's SONAME / SHLIBVERSION requires SCons 2.4 or above"
print "Update your version of SCons or use set_soname=0"
Exit(1)
if os.environ.get('CXX','g++') == 'clang++':
flags += ['-Wno-format-nonliteral','-Wno-deprecated-increment-bool','-Wno-vla-extension','-Wno-mismatched-tags']
else:
flags += ['-Wlogical-op','-Wnoexcept','-Wstrict-null-sentinel']
if env['cppthreads']:
flags += ['-DARM_COMPUTE_CPP_SCHEDULER=1']
if env['openmp']:
if os.environ.get('CXX','g++') == 'clang++':
print "Clang does not support OpenMP. Use scheduler=cpp."
Exit(1)
flags += ['-DARM_COMPUTE_OPENMP_SCHEDULER=1','-fopenmp']
env.Append(LINKFLAGS=['-fopenmp'])
files_to_delete = []
# Generate string with build options library version to embed in the library:
try:
git_hash = subprocess.check_output(["git", "rev-parse","HEAD"])
except (OSError, subbprocess.CalledProcessError):
git_hash="unknown"
version_filename = "%s/arm_compute_version.embed" % os.path.dirname(Glob("src/core/*")[0].rstr())
build_info = "\"arm_compute_version=%s Build options: %s Git hash=%s\"" % (VERSION, vars.args, git_hash.strip())
with open(version_filename, "w") as fd:
fd.write(build_info)
files_to_delete.append(version_filename)
core_libs = ['dl']
libs = ['dl']
prefix=""
if env['arch'] == 'armv7a':
flags += ['-march=armv7-a','-mthumb','-mfpu=neon']
if env['os'] in ['linux','bare_metal']:
prefix = "arm-linux-gnueabihf-"
flags += ['-mfloat-abi=hard']
elif env['os'] == 'android':
prefix = "arm-linux-androideabi-"
flags += ['-mfloat-abi=softfp']
elif env['arch'] == 'arm64-v8a':
flags += ['-march=armv8-a']
if env['os'] in ['linux','bare_metal']:
prefix = "aarch64-linux-gnu-"
elif env['os'] == 'android':
prefix = "aarch64-linux-android-"
elif env['arch'] == 'arm64-v8.2-a':
flags += ['-march=armv8.2-a+fp16+simd']
flags += ['-DARM_COMPUTE_ENABLE_FP16']
if env['os'] in ['linux','bare_metal']:
prefix = "aarch64-linux-gnu-"
elif env['os'] == 'android':
prefix = "aarch64-linux-android-"
elif env['arch'] == 'x86_32':
flags += ['-m32']
elif env['arch'] == 'x86_64':
flags += ['-m64']
if env['build'] == 'native':
prefix = ""
env['CC'] = prefix + os.environ.get('CC','gcc')
env['CXX'] = prefix + os.environ.get('CXX','g++')
env['LD'] = prefix + "ld"
env['AS'] = prefix + "as"
env['AR'] = prefix + "ar"
env['RANLIB'] = prefix + "ranlib"
try:
compiler_ver = subprocess.check_output( [env['CXX'] , "-dumpversion"] ).strip()
except OSError:
print "ERROR: Compiler '%s' not found" % env['CXX']
Exit(1)
if os.environ.get('CXX','g++') == 'g++':
if env['arch'] == 'arm64-v8.2-a' and not version_at_least(compiler_ver, '6.2.1'):
print "GCC 6.2.1 or newer is required to compile armv8.2-a code"
Exit(1)
if env['arch'] == 'arm64-v8a' and not version_at_least(compiler_ver, '4.9'):
print "GCC 4.9 or newer is required to compile NEON code for AArch64"
Exit(1)
if version_at_least(compiler_ver, '6.1'):
flags += ['-Wno-ignored-attributes']
if compiler_ver == '4.8.3':
flags += ['-Wno-array-bounds']
if env['Werror']:
flags += ['-Werror']
example_libs = []
if env['os'] == 'android':
flags += ['-DANDROID']
env.Append(LINKFLAGS=['-pie','-static-libstdc++'])
example_libs = ['arm_compute-static']
elif env['os'] == 'bare_metal':
env.Append(LINKFLAGS=['-static'])
flags += ['-fPIC','-DNO_MULTI_THREADING']
example_libs = ['arm_compute-static']
else:
libs += ['pthread']
example_libs = ['arm_compute']
if env['opencl']:
if env['os'] == 'bare_metal':
raise Exception("Cannot link OpenCL statically, which is required on bare metal")
if env['embed_kernels']:
flags += ['-DEMBEDDED_KERNELS']
if env['debug']:
env['asserts'] = True
flags += ['-O0','-g','-gdwarf-2']
else:
flags += ['-O3','-ftree-vectorize']
if env['asserts']:
flags += ['-DARM_COMPUTE_ASSERTS_ENABLED']
env.Append(CPPPATH=['.','#include'])
env.Append(LIBPATH=['#build/%s' % env['build_dir'],'.'])
env.Append(CXXFLAGS=flags)
env.Append(CXXFLAGS=env['extra_cxx_flags'])
core_files = Glob('src/core/*.cpp')
core_files += Glob('src/core/CPP/*.cpp')
files = Glob('src/runtime/*.cpp')
embed_files = []
core_files += Glob('src/core/CPP/kernels/*.cpp')
# CLHarrisCorners uses the Scheduler to run CPP kernels
files += Glob('src/runtime/CPP/SingleThreadScheduler.cpp')
if env['os'] == 'bare_metal':
if env['cppthreads'] or env['openmp']:
print "ERROR: OpenMP and C++11 threads not supported in bare_metal. Use cppthreads=0 openmp=0"
Exit(1)
else:
if env['cppthreads']:
files += Glob('src/runtime/CPP/CPPScheduler.cpp')
if env['openmp']:
files += Glob('src/runtime/OMP/OMPScheduler.cpp')
if env['opencl']:
core_files += Glob('src/core/CL/*.cpp')
core_files += Glob('src/core/CL/kernels/*.cpp')
files += Glob('src/runtime/CL/*.cpp')
files += Glob('src/runtime/CL/functions/*.cpp')
# Generate embed files
if env['embed_kernels']:
cl_files = Glob('src/core/CL/cl_kernels/*.cl') + Glob('src/core/CL/cl_kernels/*.h')
source_list = []
for file in cl_files:
source_name = file.rstr()
source_list.append(source_name)
embed_files.append(source_name + "embed")
generate_embed = env.Command(embed_files, source_list, action=resolve_includes)
Default(generate_embed)
files_to_delete += embed_files
if env['neon']:
core_files += Glob('src/core/NEON/*.cpp')
core_files += Glob('src/core/NEON/kernels/*.cpp')
files += Glob('src/runtime/NEON/*.cpp')
files += Glob('src/runtime/NEON/functions/*.cpp')
objects=[]
static_core_objects = [ env.StaticObject( f ) for f in core_files ]
shared_core_objects = [ env.SharedObject( f ) for f in core_files ]
arm_compute_core_a = build_library('arm_compute_core-static', static_core_objects, core_libs, static=True)
objects.append(arm_compute_core_a)
Export('arm_compute_core_a')
if env['os'] != 'bare_metal':
arm_compute_core_so = build_library('arm_compute_core', shared_core_objects, core_libs, static=False)
objects.append(arm_compute_core_so)
Export('arm_compute_core_so')
shared_objects = [ env.SharedObject( f ) for f in files ]
static_objects = [ env.StaticObject( f ) for f in files ]
arm_compute_a = build_library('arm_compute-static', static_core_objects + static_objects, libs, static=True)
objects.append(arm_compute_a)
Export('arm_compute_a')
if env['os'] != 'bare_metal':
arm_compute_so = build_library('arm_compute', shared_core_objects + shared_objects, libs, static=False)
objects.append(arm_compute_so)
Export('arm_compute_so')
# Delete produced embed files
clean_embed = env.Command('clean-embed', [], Delete(files_to_delete))
Default(clean_embed)
env.Depends(clean_embed, objects)
alias = env.Alias("arm_compute",objects)
Default(alias)
# Build examples
if env['examples']:
test_helpers = env.Object("test_helpers/Utils.cpp")
if env['opencl'] and env['neon']:
for file in Glob("examples/neoncl_*.cpp"):
example = os.path.basename( os.path.splitext(str(file))[0])
prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs+['OpenCL'])
alias = env.Alias(example, prog)
Depends(prog, objects)
Default( alias )
if env['opencl']:
for file in Glob("examples/cl_*.cpp"):
example = os.path.basename( os.path.splitext(str(file))[0])
prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs+['OpenCL'])
alias = env.Alias(example, prog)
Depends(prog, objects)
Default( alias )
if env['neon']:
for file in Glob("examples/neon_*.cpp"):
example = os.path.basename( os.path.splitext(str(file))[0])
prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs)
alias = env.Alias(example, prog)
Depends(prog, objects)
Default( alias )
Export('env')