blob: d3e71cda1b709b61e9d18e75e9eb850f96f72487 [file] [log] [blame]
Anthony Barbier871448e2017-03-24 14:54:29 +00001# Copyright (c) 2016, 2017 ARM Limited.
2#
3# SPDX-License-Identifier: MIT
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to
7# deal in the Software without restriction, including without limitation the
8# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9# sell copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22import collections
23import os.path
24import re
25import subprocess
Anthony Barbiera4376382017-04-12 15:12:46 +010026import SCons
Anthony Barbier871448e2017-03-24 14:54:29 +000027
Anthony Barbier46d59272017-05-04 09:15:15 +010028VERSION = "v17.05"
29SONAME_VERSION="2.0.0"
Anthony Barbier871448e2017-03-24 14:54:29 +000030
Anthony Barbier46d59272017-05-04 09:15:15 +010031Import('env')
32Import('vars')
Anthony Barbier871448e2017-03-24 14:54:29 +000033
Anthony Barbier871448e2017-03-24 14:54:29 +000034def version_at_least(version, required):
35 end = min(len(version), len(required))
36
37 for i in range(0, end, 2):
38 if int(version[i]) < int(required[i]):
39 return False
40 elif int(version[i]) > int(required[i]):
41 return True
42
43 return True
44
Anthony Barbier46d59272017-05-04 09:15:15 +010045def build_library(name, sources, libs, static=False):
46 if static:
47 obj = env.StaticLibrary(name, source = sources, LIBS=libs )
Anthony Barbiera4376382017-04-12 15:12:46 +010048 else:
Anthony Barbier46d59272017-05-04 09:15:15 +010049 if env['set_soname']:
50 obj = env.SharedLibrary(name, source = sources, LIBS=libs, SHLIBVERSION=SONAME_VERSION)
51 symlinks = []
52 # Manually delete symlinks or SCons will get confused:
53 directory = os.path.dirname( obj[0].path )
54 library_prefix = obj[0].path[:-(1+len(SONAME_VERSION))]
55 real_lib="%s.%s" % (library_prefix, SONAME_VERSION)
56 for f in Glob( "#%s*" % library_prefix):
57 if str(f) != real_lib:
58 symlinks.append("%s/%s" % (directory,str(f)))
59 clean = env.Command('clean-%s' % str(obj[0]), [], Delete(symlinks))
60 Default(clean)
61 Depends(obj, clean)
Anthony Barbiera4376382017-04-12 15:12:46 +010062 else:
Anthony Barbier46d59272017-05-04 09:15:15 +010063 obj = env.SharedLibrary(name, source = sources, LIBS=libs)
Anthony Barbiera4376382017-04-12 15:12:46 +010064
Anthony Barbier46d59272017-05-04 09:15:15 +010065 Default(obj)
66 return obj
Anthony Barbiera4376382017-04-12 15:12:46 +010067
Anthony Barbier46d59272017-05-04 09:15:15 +010068def resolve_includes(target, source, env):
69 # File collection
70 FileEntry = collections.namedtuple('FileEntry', 'target_name file_contents')
Anthony Barbiera4376382017-04-12 15:12:46 +010071
Anthony Barbier46d59272017-05-04 09:15:15 +010072 # Include pattern
73 pattern = re.compile("#include \"(.*)\"")
Anthony Barbiera4376382017-04-12 15:12:46 +010074
Anthony Barbier46d59272017-05-04 09:15:15 +010075 # Get file contents
76 files = []
77 for s in source:
78 name = s.rstr().split("/")[-1]
79 contents = s.get_contents().splitlines()
80 embed_target_name = s.abspath + "embed"
81 entry = FileEntry(target_name=embed_target_name, file_contents=contents)
82 files.append((name,entry))
Anthony Barbiera4376382017-04-12 15:12:46 +010083
Anthony Barbier46d59272017-05-04 09:15:15 +010084 # Create dictionary of tupled list
85 files_dict = dict(files)
Anthony Barbiera4376382017-04-12 15:12:46 +010086
Anthony Barbier46d59272017-05-04 09:15:15 +010087 # Check for includes (can only be files in the same folder)
88 final_files = []
89 for file in files:
90 done = False
91 tmp_file = file[1].file_contents
92 while not done:
93 file_count = 0
94 updated_file = []
95 for line in tmp_file:
96 found = pattern.search(line)
97 if found:
98 include_file = found.group(1)
99 data = files_dict[include_file].file_contents
100 updated_file.extend(data)
101 else:
102 updated_file.append(line)
103 file_count += 1
Anthony Barbiera4376382017-04-12 15:12:46 +0100104
Anthony Barbier46d59272017-05-04 09:15:15 +0100105 # Check if all include are replaced.
106 if file_count == len(tmp_file):
107 done = True
Anthony Barbiera4376382017-04-12 15:12:46 +0100108
Anthony Barbier46d59272017-05-04 09:15:15 +0100109 # Update temp file
110 tmp_file = updated_file
Anthony Barbiera4376382017-04-12 15:12:46 +0100111
Anthony Barbier46d59272017-05-04 09:15:15 +0100112 # Append and prepend string literal identifiers and add expanded file to final list
113 tmp_file.insert(0, "R\"(\n")
114 tmp_file.append("\n)\"")
115 entry = FileEntry(target_name=file[1].target_name, file_contents=tmp_file)
116 final_files.append((file[0], entry))
Anthony Barbiera4376382017-04-12 15:12:46 +0100117
Anthony Barbier46d59272017-05-04 09:15:15 +0100118 # Write output files
119 for file in final_files:
120 with open(file[1].target_name, 'w+') as out_file:
Anthony Barbiera4376382017-04-12 15:12:46 +0100121 contents = file[1].file_contents
122 for line in contents:
123 out_file.write("%s\n" % line)
124
Anthony Barbier46d59272017-05-04 09:15:15 +0100125if GetOption("help"):
126 Exit(0)
Anthony Barbiera4376382017-04-12 15:12:46 +0100127
Anthony Barbier46d59272017-05-04 09:15:15 +0100128flags = ['-D_GLIBCXX_USE_NANOSLEEP','-Wno-deprecated-declarations','-Wall','-DARCH_ARM',
129 '-Wextra','-Wno-unused-parameter','-pedantic','-Wdisabled-optimization','-Wformat=2',
130 '-Winit-self','-Wstrict-overflow=2','-Wswitch-default',
131 '-fpermissive','-std=gnu++11','-Wno-vla','-Woverloaded-virtual',
132 '-Wctor-dtor-privacy','-Wsign-promo','-Weffc++','-Wno-format-nonliteral','-Wno-overlength-strings','-Wno-strict-overflow']
Anthony Barbiera4376382017-04-12 15:12:46 +0100133
Anthony Barbier46d59272017-05-04 09:15:15 +0100134if env['neon'] and 'x86' in env['arch']:
135 print "Cannot compile NEON for x86"
136 Exit(1)
Anthony Barbiera4376382017-04-12 15:12:46 +0100137
Anthony Barbier46d59272017-05-04 09:15:15 +0100138if env['set_soname'] and not version_at_least(SCons.__version__, "2.4"):
139 print "Setting the library's SONAME / SHLIBVERSION requires SCons 2.4 or above"
140 print "Update your version of SCons or use set_soname=0"
141 Exit(1)
Anthony Barbiera4376382017-04-12 15:12:46 +0100142
Anthony Barbier46d59272017-05-04 09:15:15 +0100143if os.environ.get('CXX','g++') == 'clang++':
144 flags += ['-Wno-format-nonliteral','-Wno-deprecated-increment-bool','-Wno-vla-extension','-Wno-mismatched-tags']
145else:
146 flags += ['-Wlogical-op','-Wnoexcept','-Wstrict-null-sentinel']
Anthony Barbiera4376382017-04-12 15:12:46 +0100147
Anthony Barbier46d59272017-05-04 09:15:15 +0100148if env['cppthreads']:
149 flags += ['-DARM_COMPUTE_CPP_SCHEDULER=1']
Anthony Barbiera4376382017-04-12 15:12:46 +0100150
Anthony Barbier46d59272017-05-04 09:15:15 +0100151if env['openmp']:
152 if os.environ.get('CXX','g++') == 'clang++':
153 print "Clang does not support OpenMP. Use scheduler=cpp."
Anthony Barbier871448e2017-03-24 14:54:29 +0000154 Exit(1)
155
Anthony Barbier46d59272017-05-04 09:15:15 +0100156 flags += ['-DARM_COMPUTE_OPENMP_SCHEDULER=1','-fopenmp']
157 env.Append(LINKFLAGS=['-fopenmp'])
Anthony Barbiera4376382017-04-12 15:12:46 +0100158
Anthony Barbier46d59272017-05-04 09:15:15 +0100159files_to_delete = []
Anthony Barbiera4376382017-04-12 15:12:46 +0100160
Anthony Barbier46d59272017-05-04 09:15:15 +0100161# Generate string with build options library version to embed in the library:
162try:
163 git_hash = subprocess.check_output(["git", "rev-parse","HEAD"])
164except (OSError, subbprocess.CalledProcessError):
165 git_hash="unknown"
Anthony Barbier871448e2017-03-24 14:54:29 +0000166
Anthony Barbier46d59272017-05-04 09:15:15 +0100167version_filename = "%s/arm_compute_version.embed" % os.path.dirname(Glob("src/core/*")[0].rstr())
168build_info = "\"arm_compute_version=%s Build options: %s Git hash=%s\"" % (VERSION, vars.args, git_hash.strip())
Anthony Barbier871448e2017-03-24 14:54:29 +0000169
Anthony Barbier46d59272017-05-04 09:15:15 +0100170with open(version_filename, "w") as fd:
171 fd.write(build_info)
Anthony Barbier871448e2017-03-24 14:54:29 +0000172
Anthony Barbier46d59272017-05-04 09:15:15 +0100173files_to_delete.append(version_filename)
Anthony Barbier871448e2017-03-24 14:54:29 +0000174
Anthony Barbier46d59272017-05-04 09:15:15 +0100175core_libs = ['dl']
176libs = ['dl']
Anthony Barbier871448e2017-03-24 14:54:29 +0000177
Anthony Barbier46d59272017-05-04 09:15:15 +0100178prefix=""
Anthony Barbier871448e2017-03-24 14:54:29 +0000179
Anthony Barbier46d59272017-05-04 09:15:15 +0100180if env['arch'] == 'armv7a':
181 flags += ['-march=armv7-a','-mthumb','-mfpu=neon']
Anthony Barbier871448e2017-03-24 14:54:29 +0000182
Anthony Barbier46d59272017-05-04 09:15:15 +0100183 if env['os'] in ['linux','bare_metal']:
184 prefix = "arm-linux-gnueabihf-"
185 flags += ['-mfloat-abi=hard']
186 elif env['os'] == 'android':
187 prefix = "arm-linux-androideabi-"
188 flags += ['-mfloat-abi=softfp']
189elif env['arch'] == 'arm64-v8a':
190 flags += ['-march=armv8-a']
Anthony Barbier871448e2017-03-24 14:54:29 +0000191
Anthony Barbier46d59272017-05-04 09:15:15 +0100192 if env['os'] in ['linux','bare_metal']:
193 prefix = "aarch64-linux-gnu-"
194 elif env['os'] == 'android':
195 prefix = "aarch64-linux-android-"
196elif env['arch'] == 'arm64-v8.2-a':
197 flags += ['-march=armv8.2-a+fp16+simd']
198 flags += ['-DARM_COMPUTE_ENABLE_FP16']
Anthony Barbier871448e2017-03-24 14:54:29 +0000199
Anthony Barbier46d59272017-05-04 09:15:15 +0100200 if env['os'] in ['linux','bare_metal']:
201 prefix = "aarch64-linux-gnu-"
202 elif env['os'] == 'android':
203 prefix = "aarch64-linux-android-"
204elif env['arch'] == 'x86_32':
205 flags += ['-m32']
206elif env['arch'] == 'x86_64':
207 flags += ['-m64']
Anthony Barbier871448e2017-03-24 14:54:29 +0000208
Anthony Barbier46d59272017-05-04 09:15:15 +0100209if env['build'] == 'native':
210 prefix = ""
Anthony Barbiera4376382017-04-12 15:12:46 +0100211
Anthony Barbier46d59272017-05-04 09:15:15 +0100212env['CC'] = prefix + os.environ.get('CC','gcc')
213env['CXX'] = prefix + os.environ.get('CXX','g++')
214env['LD'] = prefix + "ld"
215env['AS'] = prefix + "as"
216env['AR'] = prefix + "ar"
217env['RANLIB'] = prefix + "ranlib"
Anthony Barbiera4376382017-04-12 15:12:46 +0100218
Anthony Barbier46d59272017-05-04 09:15:15 +0100219try:
220 compiler_ver = subprocess.check_output( [env['CXX'] , "-dumpversion"] ).strip()
221except OSError:
222 print "ERROR: Compiler '%s' not found" % env['CXX']
223 Exit(1)
Anthony Barbier871448e2017-03-24 14:54:29 +0000224
Anthony Barbier46d59272017-05-04 09:15:15 +0100225if os.environ.get('CXX','g++') == 'g++':
226 if env['arch'] == 'arm64-v8.2-a' and not version_at_least(compiler_ver, '6.2.1'):
227 print "GCC 6.2.1 or newer is required to compile armv8.2-a code"
228 Exit(1)
Anthony Barbier871448e2017-03-24 14:54:29 +0000229
Anthony Barbier46d59272017-05-04 09:15:15 +0100230 if env['arch'] == 'arm64-v8a' and not version_at_least(compiler_ver, '4.9'):
231 print "GCC 4.9 or newer is required to compile NEON code for AArch64"
232 Exit(1)
Anthony Barbier871448e2017-03-24 14:54:29 +0000233
Anthony Barbier46d59272017-05-04 09:15:15 +0100234 if version_at_least(compiler_ver, '6.1'):
235 flags += ['-Wno-ignored-attributes']
Anthony Barbier871448e2017-03-24 14:54:29 +0000236
Anthony Barbier46d59272017-05-04 09:15:15 +0100237 if compiler_ver == '4.8.3':
238 flags += ['-Wno-array-bounds']
Anthony Barbier871448e2017-03-24 14:54:29 +0000239
Anthony Barbier46d59272017-05-04 09:15:15 +0100240if env['Werror']:
241 flags += ['-Werror']
242
243example_libs = []
244
245if env['os'] == 'android':
246 flags += ['-DANDROID']
247 env.Append(LINKFLAGS=['-pie','-static-libstdc++'])
248 example_libs = ['arm_compute-static']
249elif env['os'] == 'bare_metal':
250 env.Append(LINKFLAGS=['-static'])
251 flags += ['-fPIC','-DNO_MULTI_THREADING']
252 example_libs = ['arm_compute-static']
253else:
254 libs += ['pthread']
255 example_libs = ['arm_compute']
256
257if env['opencl']:
258 if env['os'] == 'bare_metal':
259 raise Exception("Cannot link OpenCL statically, which is required on bare metal")
260 if env['embed_kernels']:
261 flags += ['-DEMBEDDED_KERNELS']
262
263if env['debug']:
264 env['asserts'] = True
265 flags += ['-O0','-g','-gdwarf-2']
266else:
267 flags += ['-O3','-ftree-vectorize']
268
269if env['asserts']:
270 flags += ['-DARM_COMPUTE_ASSERTS_ENABLED']
271
272env.Append(CPPPATH=['.','#include'])
273env.Append(LIBPATH=['#build/%s' % env['build_dir'],'.'])
274env.Append(CXXFLAGS=flags)
275env.Append(CXXFLAGS=env['extra_cxx_flags'])
276
277core_files = Glob('src/core/*.cpp')
278core_files += Glob('src/core/CPP/*.cpp')
279
280files = Glob('src/runtime/*.cpp')
281
282embed_files = []
283core_files += Glob('src/core/CPP/kernels/*.cpp')
284# CLHarrisCorners uses the Scheduler to run CPP kernels
285files += Glob('src/runtime/CPP/SingleThreadScheduler.cpp')
286
287if env['os'] == 'bare_metal':
288 if env['cppthreads'] or env['openmp']:
289 print "ERROR: OpenMP and C++11 threads not supported in bare_metal. Use cppthreads=0 openmp=0"
290 Exit(1)
291else:
292 if env['cppthreads']:
293 files += Glob('src/runtime/CPP/CPPScheduler.cpp')
294 if env['openmp']:
295 files += Glob('src/runtime/OMP/OMPScheduler.cpp')
296
297if env['opencl']:
298 core_files += Glob('src/core/CL/*.cpp')
299 core_files += Glob('src/core/CL/kernels/*.cpp')
300 files += Glob('src/runtime/CL/*.cpp')
301 files += Glob('src/runtime/CL/functions/*.cpp')
302
303 # Generate embed files
304 if env['embed_kernels']:
305 cl_files = Glob('src/core/CL/cl_kernels/*.cl') + Glob('src/core/CL/cl_kernels/*.h')
306 source_list = []
307 for file in cl_files:
308 source_name = file.rstr()
309 source_list.append(source_name)
310 embed_files.append(source_name + "embed")
311 generate_embed = env.Command(embed_files, source_list, action=resolve_includes)
312 Default(generate_embed)
313 files_to_delete += embed_files
314
315if env['neon']:
316 core_files += Glob('src/core/NEON/*.cpp')
317 core_files += Glob('src/core/NEON/kernels/*.cpp')
318 files += Glob('src/runtime/NEON/*.cpp')
319 files += Glob('src/runtime/NEON/functions/*.cpp')
320
321objects=[]
322static_core_objects = [ env.StaticObject( f ) for f in core_files ]
323shared_core_objects = [ env.SharedObject( f ) for f in core_files ]
324
325arm_compute_core_a = build_library('arm_compute_core-static', static_core_objects, core_libs, static=True)
326objects.append(arm_compute_core_a)
327Export('arm_compute_core_a')
328
329if env['os'] != 'bare_metal':
330 arm_compute_core_so = build_library('arm_compute_core', shared_core_objects, core_libs, static=False)
331 objects.append(arm_compute_core_so)
332 Export('arm_compute_core_so')
333
334shared_objects = [ env.SharedObject( f ) for f in files ]
335static_objects = [ env.StaticObject( f ) for f in files ]
336
337arm_compute_a = build_library('arm_compute-static', static_core_objects + static_objects, libs, static=True)
338objects.append(arm_compute_a)
339Export('arm_compute_a')
340
341if env['os'] != 'bare_metal':
342 arm_compute_so = build_library('arm_compute', shared_core_objects + shared_objects, libs, static=False)
343 objects.append(arm_compute_so)
344 Export('arm_compute_so')
Anthony Barbier871448e2017-03-24 14:54:29 +0000345
Anthony Barbiera4376382017-04-12 15:12:46 +0100346# Delete produced embed files
Anthony Barbier46d59272017-05-04 09:15:15 +0100347clean_embed = env.Command('clean-embed', [], Delete(files_to_delete))
348Default(clean_embed)
349env.Depends(clean_embed, objects)
350alias = env.Alias("arm_compute",objects)
351Default(alias)
Anthony Barbier871448e2017-03-24 14:54:29 +0000352
Anthony Barbiera4376382017-04-12 15:12:46 +0100353# Build examples
Anthony Barbier871448e2017-03-24 14:54:29 +0000354
Andrew Andersond619a8a2017-05-18 13:50:38 +0100355if env['examples']:
356 test_helpers = env.Object("test_helpers/Utils.cpp")
Anthony Barbier871448e2017-03-24 14:54:29 +0000357
Andrew Andersond619a8a2017-05-18 13:50:38 +0100358 if env['opencl'] and env['neon']:
359 for file in Glob("examples/neoncl_*.cpp"):
360 example = os.path.basename( os.path.splitext(str(file))[0])
361 prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs+['OpenCL'])
362 alias = env.Alias(example, prog)
363 Depends(prog, objects)
364 Default( alias )
Anthony Barbier871448e2017-03-24 14:54:29 +0000365
Andrew Andersond619a8a2017-05-18 13:50:38 +0100366 if env['opencl']:
367 for file in Glob("examples/cl_*.cpp"):
368 example = os.path.basename( os.path.splitext(str(file))[0])
369 prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs+['OpenCL'])
370 alias = env.Alias(example, prog)
371 Depends(prog, objects)
372 Default( alias )
373
374 if env['neon']:
375 for file in Glob("examples/neon_*.cpp"):
376 example = os.path.basename( os.path.splitext(str(file))[0])
377 prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs)
378 alias = env.Alias(example, prog)
379 Depends(prog, objects)
380 Default( alias )
Anthony Barbier46d59272017-05-04 09:15:15 +0100381
382Export('env')