blob: e37227b1cb8d811c4ba7a7c381fa12a0b8a8fe26 [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
26
27VERSION = "v17.03.1"
28
29vars = Variables('scons')
30vars.Add(EnumVariable('debug','Debug (default=0)', '0', allowed_values=('0','1')))
31vars.Add(EnumVariable('asserts','Enable asserts (This flag is forced to 1 for debug=1) (default=0)', '0', allowed_values=('0','1')))
32vars.Add(EnumVariable('arch','Target Architecture (default=armv7a)', 'armv7a', allowed_values=('armv7a','arm64-v8a','arm64-v8.2-a','x86')))
33vars.Add(EnumVariable('os','Target OS (default=linux)', 'linux', allowed_values=('linux','android','bare_metal')))
34vars.Add(EnumVariable('build','Build type: (default=cross_compile)', 'cross_compile', allowed_values=('native','cross_compile')))
35vars.Add(EnumVariable('Werror','Enable/disable the -Werror compilation flag (Default=1)', '1', allowed_values=('0','1')))
36vars.Add(EnumVariable('opencl','Enable OpenCL support(Default=1)', '1', allowed_values=('0','1')))
37vars.Add(EnumVariable('neon','Enable Neon support(Default=0)', '0', allowed_values=('0','1')))
38vars.Add(EnumVariable('embed_kernels', 'Embed OpenCL kernels in library binary(Default=0)', '0', allowed_values=('0','1')))
39
40env = Environment(platform='posix', variables = vars, ENV = os.environ)
41
42flags = ['-D_GLIBCXX_USE_NANOSLEEP','-Wno-deprecated-declarations','-Wall','-DARCH_ARM',
43 '-Wextra','-Wno-unused-parameter','-pedantic','-Wdisabled-optimization','-Wformat=2',
44 '-Winit-self','-Wmissing-include-dirs','-Wstrict-overflow=2','-Wswitch-default',
45 '-fpermissive','-std=c++11','-Wno-vla','-Woverloaded-virtual',
46 '-Wctor-dtor-privacy','-Wsign-promo','-Weffc++','-Wno-format-nonliteral','-Wno-overlength-strings','-Wno-strict-overflow']
47
48Help(vars.GenerateHelpText(env))
49
50if env['neon'] == '1' and env['arch'] == 'x86':
51 print "Cannot compile Neon for x86"
52 Exit(1)
53
54
55if os.environ.get('CXX','g++') == 'clang++':
56 flags += ['-Wno-format-nonliteral','-Wno-deprecated-increment-bool','-Wno-vla-extension','-Wno-mismatched-tags']
57else:
58 flags += ['-Wlogical-op','-Wnoexcept','-Wstrict-null-sentinel']
59
60files_to_delete = []
61
62#Generate string with build options library version to embed in the library:
63git_hash="unknown"
64try:
65 git_hash = subprocess.check_output(["git", "rev-parse","HEAD"])
66except subprocess.CalledProcessError:
67 pass
68
69version_filename = "%s/arm_compute_version.embed" % os.path.dirname(Glob("src/core/*")[0].rstr())
70build_info = "\"arm_compute_version=%s Build options: %s Git hash=%s\"" % (VERSION, vars.args, git_hash.strip())
71open(version_filename,"w").write(build_info)
72files_to_delete.append( version_filename )
73
74def build_library(name, sources, libs, static=False):
75 if static:
76 obj = env.StaticLibrary(name, source = sources, LIBS=libs )
77 else:
78 obj = env.SharedLibrary(name, source = sources, LIBS=libs )
79 Default(obj)
80 return obj
81
82def resolve_includes(target, source, env):
83 # File collection
84 FileEntry = collections.namedtuple('FileEntry', 'target_name file_contents')
85
86 # Include pattern
87 pattern = re.compile("#include \"(.*)\"")
88
89 # Get file contents
90 files = []
91 for s in source:
92 name = s.rstr().split("/")[-1]
93 contents = s.get_contents().splitlines()
94 embed_target_name = s.abspath + "embed"
95 entry = FileEntry(target_name=embed_target_name, file_contents=contents)
96 files.append((name,entry))
97
98 # Create dictionary of tupled list
99 files_dict = dict(files)
100
101 # Check for includes (can only be files in the same folder)
102 final_files = []
103 for file in files:
104 done = False
105 tmp_file = file[1].file_contents
106 while not(done):
107 file_count = 0
108 updated_file = []
109 for line in tmp_file:
110 found = pattern.search(line)
111 if found:
112 include_file = found.group(1)
113 data = files_dict[include_file].file_contents
114 updated_file.extend(data)
115 else:
116 updated_file.append(line)
117 file_count += 1
118
119 # Check if all include are replaced.
120 if file_count == len(tmp_file):
121 done = True
122
123 # Update temp file
124 tmp_file = updated_file
125
126 # Append and prepend string literal identifiers and add expanded file to final list
127 tmp_file.insert(0, "R\"(\n")
128 tmp_file.append("\n)\"")
129 entry = FileEntry(target_name=file[1].target_name, file_contents=tmp_file)
130 final_files.append((file[0], entry))
131
132 # Write output files
133 for file in final_files:
134 out_file = open(file[1].target_name, 'w+')
135 contents = file[1].file_contents
136 for line in contents:
137 out_file.write("%s\n" % line)
138
139core_libs = []
140libs = []
141
142prefix=""
143
144if env['arch'] == 'armv7a':
145 flags += ['-march=armv7-a','-mthumb','-mfpu=neon']
146
147 if env['os'] in ['linux','bare_metal']:
148 prefix = "arm-linux-gnueabihf-"
149 flags += ['-mfloat-abi=hard']
150 elif env['os'] == 'android':
151 prefix = "arm-linux-androideabi-"
152 flags += ['-mfloat-abi=softfp']
153elif env['arch'] == 'arm64-v8a':
154 flags += ['-march=armv8-a']
155 if env['os'] in ['linux','bare_metal']:
156 prefix = "aarch64-linux-gnu-"
157 elif env['os'] == 'android':
158 prefix = "aarch64-linux-android-"
159elif env['arch'] == 'arm64-v8.2-a':
160 flags += ['-march=armv8.2-a+fp16+simd']
161 flags += ['-DARM_COMPUTE_ENABLE_FP16']
162 if env['os'] in ['linux','bare_metal']:
163 prefix = "aarch64-linux-gnu-"
164 elif env['os'] == 'android':
165 prefix = "aarch64-linux-android-"
166
167if env['build'] == 'native':
168 prefix = ""
169
170env['CC'] = prefix + os.environ.get('CC','gcc')
171env['CXX'] = prefix + os.environ.get('CXX','g++')
172env['LD'] = prefix + "ld"
173env['AS'] = prefix + "as"
174env['AR'] = prefix + "ar"
175env['RANLIB'] = prefix + "ranlib"
176
177def version_at_least(version, required):
178 end = min(len(version), len(required))
179
180 for i in range(0, end, 2):
181 if int(version[i]) < int(required[i]):
182 return False
183 elif int(version[i]) > int(required[i]):
184 return True
185
186 return True
187
188try:
189 compiler_ver = subprocess.check_output( [env['CXX'] , "-dumpversion"] ).strip()
190except OSError:
191 print "ERROR: Compiler not found"
192 compiler_ver = ""
193
194if compiler_ver != "":
195 if env['arch'] == 'arm64-v8.2-a' and not version_at_least(compiler_ver, '6.2.1'):
196 print "GCC 6.2.1 or newer is required to compile armv8.2-a code"
197 Exit(1)
198
199 if os.environ.get('CXX','g++') == 'g++':
200 if version_at_least(compiler_ver, '6.1'):
201 flags += ['-Wno-ignored-attributes']
202
203 if compiler_ver == '4.8.3':
204 flags += ['-Wno-array-bounds']
205
206 if env['Werror'] == '1':
207 flags += ['-Werror']
208
209 example_libs = []
210 if env['os'] == 'android':
211 flags += ['-DANDROID']
212 env.Append(LINKFLAGS=['-pie','-static-libstdc++'])
213 example_libs = ['arm_compute-static']
214 elif env['os'] == 'bare_metal':
215 env.Append(LINKFLAGS=['-static'])
216 flags += ['-fPIC','-DNO_MULTI_THREADING']
217 example_libs = ['arm_compute-static']
218 else:
219 libs += ['pthread']
220 example_libs = ['arm_compute']
221
222 if env['opencl'] == '1':
223 if env['os'] == 'bare_metal':
224 raise Exception("Cannot link OpenCL statically, which is required on bare metal")
225 core_libs += ['OpenCL']
226 if env['embed_kernels'] == '1':
227 flags += ['-DEMBEDDED_KERNELS']
228
229 if env['debug'] == '1':
230 env['asserts'] = '1'
231 flags += ['-O0','-g','-gdwarf-2']
232 else:
233 flags += ['-O3','-ftree-vectorize']
234
235 if env['asserts'] == '1':
236 flags += ['-DARM_COMPUTE_ASSERTS_ENABLED']
237
238 env.Append(CPPPATH=['.','#include'])
239 env.Append(LIBPATH=['#build','.'])
240 env.Append(CXXFLAGS=flags)
241
242 core_files = Glob('src/core/*.cpp')
243 core_files += Glob('src/core/CPP/*.cpp')
244
245 files = Glob('src/runtime/*.cpp')
246
247 embed_files = []
248 core_files += Glob('src/core/CPP/kernels/*.cpp')
249 # CLHarrisCorners uses the Scheduler to run multithreaded CPP kernels
250 core_files += Glob('src/runtime/CPP/CPPScheduler.cpp')
251 if env['opencl'] == '1':
252 example_libs += ['OpenCL']
253 core_files += Glob('src/core/CL/*.cpp')
254 core_files += Glob('src/core/CL/kernels/*.cpp')
255 files += Glob('src/runtime/CL/*.cpp')
256 files += Glob('src/runtime/CL/functions/*.cpp')
257 # Generate embed files
258 if env['embed_kernels'] == '1':
259 cl_files = Glob('src/core/CL/cl_kernels/*.cl') + Glob('src/core/CL/cl_kernels/*.h')
260 source_list = []
261 for file in cl_files:
262 source_name = file.rstr()
263 source_list.append(source_name)
264 embed_files.append(source_name + "embed")
265 generate_embed = env.Command(embed_files, source_list, action=resolve_includes)
266 Default(generate_embed)
267 files_to_delete += embed_files
268
269 if env['neon'] == '1':
270 core_files += Glob('src/core/NEON/*.cpp')
271 core_files += Glob('src/core/NEON/kernels/*.cpp')
272 files += Glob('src/runtime/NEON/*.cpp')
273 files += Glob('src/runtime/NEON/functions/*.cpp')
274
275 objects=[]
276 static_core_objects = [ env.StaticObject( f ) for f in core_files ]
277 shared_core_objects = [ env.SharedObject( f ) for f in core_files ]
278
279 arm_compute_core_a = build_library('arm_compute_core-static', static_core_objects, core_libs, static=True)
280 objects.append(arm_compute_core_a)
281 Export('arm_compute_core_a')
282
283 if env['os'] != 'bare_metal':
284 arm_compute_core_so = build_library('arm_compute_core', shared_core_objects, core_libs, static=False)
285 objects.append(arm_compute_core_so)
286 Export('arm_compute_core_so')
287 shared_objects = [ env.SharedObject( f ) for f in files ]
288 static_objects = [ env.StaticObject( f ) for f in files ]
289
290 arm_compute_a = build_library('arm_compute-static', static_core_objects + static_objects, libs, static=True)
291 objects.append(arm_compute_a)
292 Export('arm_compute_a')
293 if env['os'] != 'bare_metal':
294 arm_compute_so = build_library('arm_compute', shared_core_objects + shared_objects, libs, static=False)
295 objects.append(arm_compute_so)
296 Export('arm_compute_so')
297
298 # Delete produced embed files
299 clean_embed = env.Command('clean-embed', [], Delete(files_to_delete))
300 Default(clean_embed)
301 env.Depends(clean_embed, objects)
302 alias = env.Alias("arm_compute",objects)
303 Default(alias)
304
305 # Build examples
306 test_helpers = env.Object("test_helpers/Utils.cpp")
307
308 if env['opencl'] == '1' and env['neon'] == '1':
309 for file in Glob("examples/neoncl_*.cpp"):
310 example = os.path.basename( os.path.splitext(str(file))[0])
311 prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs)
312 alias = env.Alias(example, prog)
313 Depends(prog, objects)
314 Default( alias )
315
316 if env['opencl'] == '1':
317 for file in Glob("examples/cl_*.cpp"):
318 example = os.path.basename( os.path.splitext(str(file))[0])
319 prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs)
320 alias = env.Alias(example, prog)
321 Depends(prog, objects)
322 Default( alias )
323
324 if env['neon'] == '1':
325 for file in Glob("examples/neon_*.cpp"):
326 example = os.path.basename( os.path.splitext(str(file))[0])
327 prog = env.Program(example, ['examples/%s.cpp' % example, test_helpers], LIBS=example_libs)
328 alias = env.Alias(example, prog)
329 Depends(prog, objects)
330 Default( alias )