Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / native_client / src / trusted / validator_ragel / build.scons
1 # -*- python -*-
2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import json
7 import hashlib
8 import os
9 import platform
10 import sys
11
12 Import('env')
13
14 # We only want to export validator functions when we build with scons, because
15 # they are used for tests.
16 # On the other hand, exporting unnecessary symbols causes chromium build
17 # problems, see https://code.google.com/p/nativeclient/issues/detail?id=3367 ,
18 # so we leave VALIDATOR_EXPORT undefined in gyp build.
19 env = env.Clone()
20 env.Append(CCFLAGS=['-DVALIDATOR_EXPORT=DLLEXPORT'])
21
22 # Dir to place c files generated by 'dfagen' target (it's under source control).
23 gen_dir = '$MAIN_DIR/src/trusted/validator_ragel/gen'
24 # Currently we only provide ragel pre-built for Linux.
25 ragel_binary = '$MAIN_DIR/../third_party/ragel/ragel.linux'
26
27 INSTRUCTION_DEFINITIONS = map(env.File, [
28     'instruction_definitions/general_purpose_instructions.def',
29     'instruction_definitions/system_instructions.def',
30     'instruction_definitions/x87_instructions.def',
31     'instruction_definitions/mmx_instructions.def',
32     'instruction_definitions/xmm_instructions.def',
33     'instruction_definitions/nops.def',
34     'instruction_definitions/tls.def',
35 ])
36
37 # These objects are included in both dfa_validate_x86_xx and rdfa_validator
38 # libraries, so we have to introduce intermediate scons nodes.
39 validator32 = env.ComponentObject('gen/validator_x86_32.c')
40 validator64 = env.ComponentObject('gen/validator_x86_64.c')
41
42 features = [
43     env.ComponentObject('validator_features_all.c'),
44     env.ComponentObject('validator_features_validator.c')
45 ]
46
47 # Glue library called from service runtime. The source file depends on the
48 # target architecture.  In library_deps.py this library is marked as
49 # dependant of dfa_validate_x86_xx.
50 if env.Bit('target_x86'):
51   caller_lib = 'dfa_validate_caller_x86_%s' % env.get('TARGET_SUBARCH')
52   env.ComponentLibrary(
53       caller_lib,
54       ['dfa_validate_%s.c' % env.get('TARGET_SUBARCH'),
55        {'32': validator32, '64': validator64}[env.get('TARGET_SUBARCH')],
56        'dfa_validate_common.c',
57        features])
58
59 # Low-level platform-independent interface supporting both 32 and 64 bit,
60 # used in ncval and in validator_benchmark.
61 env.ComponentLibrary('rdfa_validator',
62                      [validator32, validator64] + features)
63
64 validator_benchmark = env.ComponentProgram(
65     'rdfa_validator_benchmark',
66     ['validator_benchmark.cc'],
67     EXTRA_LIBS=['rdfa_validator', 'platform', 'elf_load']
68 )
69
70 run_benchmark = env.AutoDepsCommand(
71     'run_validator_ragel_benchmark.out',
72     [validator_benchmark, env.GetIrtNexe(), '10000']
73 )
74
75 env.AlwaysBuild(env.Alias('dfavalidatorbenchmark', run_benchmark))
76
77 # We don't run this test under qemu because it attempts to execute host python
78 # binary.
79 gen_dfa_test = env.CommandTest(
80     'gen_dfa_test.out',
81     ['${PYTHON}', env.File('gen_dfa_test.py')] + INSTRUCTION_DEFINITIONS,
82     direct_emulation=False)
83
84 env.AddNodeToTestSuite(
85     gen_dfa_test,
86     ['small_tests', 'validator_tests'],
87     'run_gen_dfa_test')
88
89 dll_env = env.Clone(COMPONENT_STATIC=False)
90
91 if env.Bit('windows'):
92   # On windows we don't need to recompile specifically for dynamic linking.
93   validator32_dll = validator32
94   validator64_dll = validator64
95   features_dll = features
96 else:
97   dll_env.Append(CCFLAGS=['-fPIC'])
98   validator32_dll = dll_env.ComponentObject('gen/validator_x86_32.c')
99   validator64_dll = dll_env.ComponentObject('gen/validator_x86_64.c')
100
101   features_dll = [
102       dll_env.ComponentObject('validator_features_all.c'),
103       dll_env.ComponentObject('validator_features_validator.c')
104   ]
105
106 dll_utils = dll_env.ComponentObject('dll_utils.c')
107
108 validator_dll = dll_env.ComponentLibrary(
109     'rdfa_validator_dll',
110     [validator32_dll, validator64_dll, dll_utils] + features_dll)
111
112 # Here for simplicity we make assumption that python used to run scons
113 # is the same as the one invoked by scons to run tests.
114 python_bitness = {2**31 - 1: 32, 2**63 - 1: 64}[sys.maxsize]
115 bitness = int(env.get('TARGET_SUBARCH'))
116 assert bitness in [32, 64]
117 # Note that it is possible that we build on one machine and run tests on another
118 # one, so we build DLL even if python bitness does not match target bitness.
119 # Asan does not instrument DLLs (only executables), so we will be unable to
120 # run tests under asan.
121 python_can_load_dll = python_bitness == bitness and not env.Bit('asan')
122
123 # On ARM we still have some test bots that are running an armel system
124 # image (with armhf runtime supported added) which means that the python
125 # executable is armel and cannot load binaries built by the arm cross
126 # compiler that we use.
127 # TODO(sbc): remove this once the testbots get upgraded to armhf:
128 # https://code.google.com/p/nativeclient/issues/detail?id=3716
129 if platform.machine() == 'armv7l':
130   with open(sys.executable) as infile:
131     if '/lib/ld-linux-armhf' not in infile.read(1024):
132       python_can_load_dll = False
133
134 if python_can_load_dll:
135   validator_py_test = env.AutoDepsCommand(
136         'validator_py_out',
137         ['${PYTHON}',
138          env.File('validator.py'),
139          validator_dll])
140
141   env.AddNodeToTestSuite(
142       validator_py_test,
143       ['small_tests', 'validator_tests'],
144       'run_validator_py_test')
145
146 # Source generation:
147 #
148 #   dfagen : Regenerate any autogenerated source files.
149
150 ragel_targets = set([
151     'dfagen', 'dfacheckdecoder', 'dfacheckvalidator'])
152 ragel_involved = ragel_targets.intersection(COMMAND_LINE_TARGETS)
153
154 gas = env.MakeUntrustedNativeEnv()['AS']
155 objdump = env.MakeUntrustedNativeEnv()['OBJDUMP']
156
157 if ragel_involved:
158   if not env.Bit('host_linux'):
159     raise Exception('Right now DFA generation is only supported on Linux')
160
161   # Source generation step 1: Build generator of ragel files.
162   #
163   # We have generator which reads .def files and produced automaton definition.
164   #
165   # Ragel is included in most Linux distributions, but it's not standard tool
166   # on MacOS/Windows thus we only support gneration of automata under Linux.
167   # This also means that we don't need to make sure gen_dfa.cc is portable to
168   # non-POSIX platforms (in particular it's not Windows compatible).
169
170   env_gen_dfa = env.Clone()
171   env_gen_dfa.Append(CCFLAGS=['-DNACL_TRUSTED_BUT_NOT_TCB'])
172
173   # Generate byte_machines.rl from byte_machines.py
174   (byte_machines,) = env.AutoDepsCommand(
175       'byte_machines.rl',
176       ['${PYTHON}',
177        env.File('byte_machines.py'),
178        '>${TARGET}'])
179
180   # Source generation step 2: Generate decoder automata.
181   #
182   # Now we are back to conditionally defining the large automata generated
183   # by gen_dfa.
184
185   def MakeAutomaton(bits, automaton):
186     (rl_instruction_file,) = env.AutoDepsCommand(
187         '%s_x86_%s_instruction.rl' % (automaton, bits),
188         ['${PYTHON}',
189          env.File('gen_dfa.py'),
190          '--bitness', bits,
191          '--mode', automaton,
192          '>${TARGET}',
193          ] + INSTRUCTION_DEFINITIONS)
194
195     include_dir = rl_instruction_file.dir.get_abspath()
196     assert include_dir == byte_machines.dir.get_abspath()
197
198     # Don't use env.File because filename is written "as is" in XML file.
199     # We want relative path there, not an absolute one!
200     rl_file = 'src/trusted/validator_ragel/%s_x86_%s.rl' % (automaton, bits)
201
202     (xml_file,) = env.AutoDepsCommand(
203         '%s/%s_x86_%s.xml' % (gen_dir, automaton, bits),
204         [ragel_binary,
205          '-x',
206          '-LL',
207          '-I..',
208          '-I%s' % include_dir,
209          rl_file,
210          '-o', '${TARGET}'],
211         extra_deps=[byte_machines, rl_instruction_file])
212
213     c_file = env.AutoDepsCommand(
214         '%s/%s_x86_%s.c' % (gen_dir, automaton, bits),
215         ['${PYTHON}',
216          env.File('codegen.py'),
217          rl_file,
218          xml_file,
219          '${TARGET}'])
220     # We need to at least update timestamp if dfagen is started.
221     # It is needed for presubmit script.
222     env.AlwaysBuild(c_file)
223
224     return c_file, xml_file
225
226   (decoder32, decoder32_xml) = MakeAutomaton(
227       '32', 'decoder')
228   (validator32, validator32_xml) = MakeAutomaton(
229       '32', 'validator')
230   (decoder64, decoder64_xml) = MakeAutomaton(
231       '64', 'decoder')
232   (validator64, validator64_xml) = MakeAutomaton(
233       '64', 'validator')
234
235   automata = [decoder32, validator32, decoder64, validator64]
236
237   # Prepair 'dfacheckdecoder' test.
238   #
239   # In this test, all acceptable instructions are enumerated
240   # by DFA traversal, and for each one objdump output and
241   # DFA-based decoder output are compared.
242   # It takes few hours to run the test, so it's not included
243   # into any suits and is supposed to be run manually when
244   # changes are made to DFA definitions.
245   # Also, since DFA generation is currently linux-only,
246   # this test is somewhat platform-dependent as well.
247
248   decoder32_obj = dll_env.ComponentObject(decoder32)
249   decoder64_obj = dll_env.ComponentObject(decoder64)
250   decoder_test_obj = dll_env.ComponentObject('unreviewed/decoder_test.cc')
251
252   # Command-line decoder.
253   decoder_test = env.ComponentProgram(
254       'decoder_test',
255       [decoder_test_obj, decoder32_obj, decoder64_obj])
256
257   decoder_dll = dll_env.ComponentLibrary(
258       'rdfa_decoder_dll',
259       [decoder_test_obj, decoder32_obj, decoder64_obj])
260
261   check_decoders = []
262
263   for bits, xml in [('32', decoder32_xml), ('64', decoder64_xml)]:
264     check_decoder = env.AutoDepsCommand(
265         'check_decoder_test_results_%s' % bits,
266         ['${PYTHON}',
267          env.File('check_decoder.py'),
268          xml,
269          '--gas', gas,
270          '--objdump', objdump,
271          '--decoder', decoder_test,
272          '--bits', bits])
273
274     check_decoders.append(check_decoder)
275
276   # Never run decoder tests in parallel because they can take all CPU.
277   SideEffect('check_decoder', check_decoders)
278
279   env.AlwaysBuild(env.Alias('dfagen', automata))
280   env.AlwaysBuild(env.Alias('dfacheckdecoder', check_decoders))
281
282   if python_can_load_dll:
283
284     dfacheckvalidator_tests = []
285
286     for bitness, xml in [('32', validator32_xml), ('64', validator64_xml)]:
287       superinstruction = env.AutoDepsCommand(
288           'superinstructions_x86_%s.txt' % bitness,
289           ['${PYTHON}',
290            env.File('verify_validators_dfa.py'),
291            xml,
292            '-o', '${TARGET}'])
293
294       superinstruction_verified = env.AutoDepsCommand(
295         'superinstructions_verified_x86_%s.out' % bitness,
296         ['${PYTHON}',
297          env.File('verify_superinstructions.py'),
298          '--bitness', bitness,
299          '--gas', gas,
300          '--objdump', objdump,
301          '--validator_dll', validator_dll,
302          superinstruction])
303
304       env.AlwaysBuild(superinstruction_verified)
305       dfacheckvalidator_tests.append(superinstruction_verified)
306
307       regular_instructions_test = env.AutoDepsCommand(
308         'regular_instructions_test_x86_%s.out' % bitness,
309         ['${PYTHON}',
310          env.File('verify_regular_instructions.py'),
311          xml,
312          '--bitness', bitness,
313          '--validator_dll', validator_dll,
314          '--decoder_dll', decoder_dll])
315
316       # Never run exhaustive tests in parallel because they can take all CPU.
317       env.AlwaysBuild(regular_instructions_test)
318       SideEffect('check_validator', regular_instructions_test)
319       dfacheckvalidator_tests.append(regular_instructions_test)
320
321       env32 = env.Clone(BUILD_TARGET_NAME='x86-32')
322       env64 = env.Clone(BUILD_TARGET_NAME='x86-64')
323       old_ncval32 = env32.File('$STAGING_DIR/ncval$PROGSUFFIX')
324       old_ncval64 = env64.File('$STAGING_DIR/ncval$PROGSUFFIX')
325
326       regular_instructions_old_test = env.AutoDepsCommand(
327         'regular_instructions_old_test_x86_%s.out' % bitness,
328         ['${PYTHON}',
329          env.File('verify_regular_instructions_old.py'),
330          xml,
331          '--bitness', bitness,
332          '--gas', gas,
333          '--objdump', objdump,
334          '--validator_dll', validator_dll,
335          '--ncval32', old_ncval32,
336          '--ncval64', old_ncval64,
337          '--errors', '${TARGET}'])
338
339       # Never run exhaustive tests in parallel because they can take all CPU.
340       env.AlwaysBuild(regular_instructions_old_test)
341       SideEffect('check_validator', regular_instructions_old_test)
342       dfacheckvalidator_tests.append(regular_instructions_old_test)
343
344     # There are three categories of protected files: validator files, generated
345     # files (those of validator files that lie withing 'gen' directory) and
346     # generating files (programs and input data used to produce generated
347     # files).
348     #
349     # When validator files are changed, exhaustive validator tests have to be
350     # rerun.  When generating files are changed, dfagen must be run to ensure
351     # that generated files are up to date. Presubmit script uses
352     # protected_files.json to perform these checks.
353
354     # Files which directly affect the validator behavior.
355     protected_files = [
356       'gen/validator_x86_32.c',
357       'gen/validator_x86_32.xml',
358       'gen/validator_x86_64.c',
359       'gen/validator_x86_64.xml',
360       'validator_internal.h',
361       'decoder.h',
362       'decoding.h',
363       'validator.h']
364
365     # Files which are used to generate gen/{decoder,validator}_x86_{32,64}.c
366     # files.
367     generating_files = [
368       INSTRUCTION_DEFINITIONS,
369       'validator_x86_32.rl',
370       'validator_x86_64.rl',
371       'parse_instruction.rl',
372       'byte_machines.py',
373       'gen_dfa.py',
374       'codegen.py']
375
376     def CanonicalizeFilename(filename):
377       return 'native_client/' + env.File(filename).get_path().replace('\\', '/')
378
379     def CalculateHashSums(target, source, env):
380       target_filename = target[0].get_abspath()
381       protected_files = {
382         'validator':{},
383         'generated':[],
384         'generating':[]
385       }
386       for filename in source:
387         canonical_filename = CanonicalizeFilename(filename)
388         with open(filename.get_abspath(), 'r') as file_stream:
389           sha512 = hashlib.sha512(file_stream.read()).hexdigest()
390           protected_files['validator'][canonical_filename] = sha512
391         if canonical_filename.rsplit('/', 2)[1] == 'gen':
392           protected_files['generated'].append(canonical_filename)
393       for filename in Flatten(generating_files):
394         canonical_filename = CanonicalizeFilename(filename)
395         protected_files['generating'].append(canonical_filename)
396       with open(target_filename, 'w') as target_file:
397         json.dump(protected_files, target_file, sort_keys=True, indent=2)
398
399     calculate_hash_sums = env.Command(
400         target='%s/protected_files.json' % gen_dir,
401         source=protected_files,
402         action=CalculateHashSums)
403     env.Depends(calculate_hash_sums, dfacheckvalidator_tests)
404
405     env.Alias('dfacheckvalidator', calculate_hash_sums)
406
407 # Targeted tests: RDFA validator test, dis section checker and spec_val test.
408
409 for bits in ['32', '64']:
410   tests_mask = (
411       '${MAIN_DIR}/src/trusted/validator_ragel/testdata/%s/*.test' % bits)
412
413   if env.Bit('regenerate_golden'):
414     update_option = ['--update']
415   else:
416     update_option = []
417
418   if env.Bit('target_x86'):
419     # In principle we can run these tests on ARM too, but first, there is no
420     # reason to, and second, there are problems with those bots that build
421     # on one machine and run tests on another (build machine does not recognize
422     # than new ncval is needed, so it's not copied to the test machine).
423     rdfaval = '$STAGING_DIR/ncval_new$PROGSUFFIX'
424     rdfa_targeted_test = env.AutoDepsCommand(
425         'run_rdfa_targeted_tests_%s.out' % bits,
426         ['${PYTHON}',
427          env.File('run_rdfa_validator_tests.py'),
428          '--rdfaval', env.File(rdfaval),
429          '--bits', bits,
430          tests_mask] + update_option)
431
432     env.AddNodeToTestSuite(
433         rdfa_targeted_test,
434         ['small_tests', 'validator_tests'],
435         node_name='run_rdfa_targeted_tests_%s' % bits)
436
437     if env.Bit('regenerate_golden'):
438       # Don't want these tests run in parallel because they write
439       # to .test files.
440       SideEffect(tests_mask, rdfa_targeted_test)
441
442     dis_section_test = env.AutoDepsCommand(
443         'run_dis_section_test_%s.out' % bits,
444         ['${PYTHON}',
445          env.File('check_dis_section.py'),
446          '--objdump', objdump,
447          '--bits', bits,
448          tests_mask] + update_option)
449
450     env.AddNodeToTestSuite(
451         dis_section_test,
452         ['small_tests', 'validator_tests'],
453         node_name='run_dis_section_test_%s' % bits)
454
455     if env.Bit('regenerate_golden'):
456       # Don't want these tests run in parallel because they write
457       # to .test files.
458       SideEffect(tests_mask, dis_section_test)
459
460   spec_test = env.AutoDepsCommand(
461       'run_spec_val_test_%s.out' % bits,
462       ['${PYTHON}',
463        env.File('spec_val_test.py'),
464        '--bits', bits,
465        tests_mask] + update_option)
466
467   env.AddNodeToTestSuite(
468       spec_test,
469       ['small_tests', 'validator_tests'],
470       node_name='run_spec_val_test_%s' % bits)
471
472   if env.Bit('regenerate_golden'):
473     # Don't want these tests run in parallel because they write
474     # to .test files.
475     SideEffect(tests_mask, spec_test)
476
477     # Spec test uses @dis section in test files, which in turn is updated
478     # by dis_section_test.
479     if env.Bit('target_x86'):
480       env.Depends(spec_test, dis_section_test)