-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathrakefile
515 lines (388 loc) · 15.7 KB
/
rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# Copyright (c) Torrox GmbH & Co KG. All rights reserved.
# Please note that the content of this file is confidential or protected by law.
# Any unauthorised copying or unauthorised distribution of the information contained herein is prohibited.
require 'pathname'
require 'rake/clean'
require 'yaml'
require 'rbconfig'
require_relative 'build/run_mocha'
LIBRARY_EXCEPTIONS = %w( sioux tests )
CPP = ENV["SIOUX_CPP"] || ENV["CPP"] || 'g++'
ADDITIONAL_DEFINES = ENV[ 'SIOUX_DEFINES' ]
SOURCE_FOLDER = File.expand_path 'source'
INCLUDE_FOLDER = SOURCE_FOLDER
SOURCE_FOLDER_PATHNAME = Pathname.new SOURCE_FOLDER
OUTPUT_FOLDER = File.expand_path 'obj'
EXE_FOLDER = File.expand_path 'bin'
LIBRARY_FOLDER = File.expand_path 'lib'
DOCUMENTATION_FOLDER = File.expand_path 'documentation'
HOME_FOLDER = Dir.home
BOOST_LIBRARY_FOLDER = File.absolute_path("/usr/lib/x86_64-linux-gnu")
BOOST_INCLUDE_FOLDER = File.absolute_path("/usr/include/boost")
RUBY_INSTALL_FOLDER = File.absolute_path("#{HOME_FOLDER}/.rvm/rubies/ruby-1.9.3-p547/include/ruby-1.9.1/")
RUBY_H_FOLDER = File.absolute_path("#{RUBY_INSTALL_FOLDER}/x86_64-linux")
OBJECT_FILE_EXT = 'o'
DEPENDS_FILE_EXT = 'd'
SHARED_LIBRARY_FILE_EXT = RbConfig::CONFIG['DLEXT'] || 'so'
INCLUDE_PATH = ['.', INCLUDE_FOLDER]
FLAVORS = %w{debug release coverage}
ADDITIONAL_DEFINES_FLAG = ADDITIONAL_DEFINES.nil? ? '' : "-D#{ADDITIONAL_DEFINES}"
HAS_GCOV = false # system "#{CPP} -lgcov"
COMPILER_FLAGS = "-Wall -pedantic -Wno-parentheses -Wno-sign-compare -std=c++11 \
-fPIC -c -pipe -I#{INCLUDE_FOLDER} \
-I #{BOOST_INCLUDE_FOLDER} \
-I #{RUBY_INSTALL_FOLDER} \
-I #{RUBY_H_FOLDER} \
#{ADDITIONAL_DEFINES_FLAG}"
COMPILER_FLAGS_BY_FLAVORS = {
'debug' => '-ggdb -O0',
'release' => '-O3 -DNDEBUG',
'coverage' => HAS_GCOV ? '-ggdb -O0 -fno-inline -fprofile-arcs -ftest-coverage' : '-ggdb -O0'
}
LINK_FLAGS = "-ggdb -fPIC"
LINK_FLAGS_BY_FLAVORS = {
'debug' => '',
'release' => '',
'coverage' => HAS_GCOV ? '-fprofile-arcs -ftest-coverage -lgcov' : '',
}
def test_system_library library
Regexp.new( "-l#{library}(\s|$)" ).match( RbConfig::CONFIG[ 'LIBS' ] ) ? "-l#{library} " : ''
end
SYSTEMLIBRARIES = test_system_library( 'pthread' ) + test_system_library( 'rt' )
if RbConfig::CONFIG['host_os'] =~ /darwin|mac os/
SHARED_LINK_FLAGS = "-shared -flat_namespace #{LINK_FLAGS}"
else
SHARED_LINK_FLAGS = "-shared #{LINK_FLAGS}"
end
SHARED_LINK_FLAGS_BY_FLAVORS = LINK_FLAGS_BY_FLAVORS
RUBY_EXTENSION_LINK_FLAGS = '-static'
BOOST_LIBRARY_PREFIX=ENV['SIOUX_BOOST_VERSION_PREFIX'].nil? ? '' : "-#{ENV['SIOUX_BOOST_VERSION_PREFIX']}"
BOOST_LIBRARY_POST_FIX_BY_FLAVOR = {
'debug' => "-mt#{BOOST_LIBRARY_PREFIX}",
'release' => "-mt#{BOOST_LIBRARY_PREFIX}",
'coverage' => "-mt#{BOOST_LIBRARY_PREFIX}"
}
GCOV_PATTERN = ['*.gcda', '*.gcov', '*.gcno']
GCOV_PATTERN.each { |p| CLEAN.include p }
DEVICE_NULL = '/dev/null'
ALL_TEST_NAMES = []
UNIT_TEST_NAMES = []
ALL_EXAMPLE_NAMES = []
COMPONENT_TEST_NAMES = []
SHARED_LIBARARIES = []
def library_name_from_path path
Pathname.new(path).basename.to_s
end
def library_file_name lib, flavor
File.expand_path(File.join(LIBRARY_FOLDER, flavor, 'lib' + lib)) + '.a'
end
def shared_library_file_name lib, flavor
File.expand_path(File.join(LIBRARY_FOLDER, flavor, lib)) + '.' + SHARED_LIBRARY_FILE_EXT
end
def external_library_file_name lib, flavor
lib.start_with?('boost') ?
"#{lib}#{BOOST_LIBRARY_POST_FIX_BY_FLAVOR[flavor]}" : lib
end
def exe_file_name exe, flavor
File.expand_path(File.join(EXE_FOLDER, flavor, exe)) + '.exe'
end
# converts /Users/todi/sioux/source/tools/time.cpp, 'debug', 'o' to /Users/todi/sioux/bin/debug/tools/time.o
def object_file_name_from_source source_file_name, flavor, extension
file = File.basename(source_file_name, '.cpp')
source_dir = Pathname.new(File.dirname(File.absolute_path(source_file_name)))
rel_dir = source_dir.relative_path_from(SOURCE_FOLDER_PATHNAME)
File.join(OUTPUT_FOLDER, flavor, rel_dir, file) + '.' + extension
end
def check_flavor flavor
raise "flavor #{flavor} not configured" unless FLAVORS.include?(flavor)
end
def expand_include_file_name file_name, path_list, current_path
path_list.each do |path|
result = File.join(File.realpath(path, current_path), file_name)
return result if File.exists?(result)
end
throw "unable to determine dependencies for inlude \"#{file_name}\"; current_path: #{current_path}"
end
INCLUDE_LINE_PATTERN = /^#[ \t]*include[ \t]*"(.+)"/
SCAN_FILE_CACHE = {}
def scan_file_for_includes source_file, current_path
dependent_include_files = {}
cache_key = [current_path, source_file]
cache = SCAN_FILE_CACHE.fetch(cache_key, nil)
return cache if cache
rake_output_message("scanning #{source_file}\n")
File.foreach(source_file) do |line|
if INCLUDE_LINE_PATTERN.match(line) then
dependent_include_files[expand_include_file_name($1, INCLUDE_PATH, current_path)] = 1
end
end
SCAN_FILE_CACHE[cache_key] = dependent_include_files
dependent_include_files
end
def build_dependencies source_file, dependency_file, flavor
already_scanned = {}
to_be_scanned = {source_file => 1}
while !to_be_scanned.empty?
file_name = to_be_scanned.first[0]
to_be_scanned.delete file_name
unless already_scanned.has_key? file_name then
already_scanned[file_name] = 1
to_be_scanned.merge! scan_file_for_includes(file_name, File.split(source_file)[0] )
end
end
File.open(dependency_file, 'w') do |output|
YAML.dump(already_scanned.keys, output)
end
end
def build_object source_file, object_file, flavor, dependency_file
sh "#{CPP} #{source_file} -o #{object_file} #{COMPILER_FLAGS} #{COMPILER_FLAGS_BY_FLAVORS[flavor]}"
end
def read_dependencies dependency_file
begin
[*YAML.load_file(dependency_file)]
rescue
[]
end
end
def build_library library_file, object_files, flavor
File.delete(library_file) if File.exist?(library_file)
sh "ar -q #{library_file} #{object_files.join(' ')}"
end
def build_ruby_extension shared_lib, objects, libraries, external_libraries, flavor
File.delete( shared_lib ) if File.exist?(shared_lib)
sh "#{CPP} -o #{shared_lib} #{SHARED_LINK_FLAGS} #{SHARED_LINK_FLAGS_BY_FLAVORS[flavor]} " +
"-L#{File.join(LIBRARY_FOLDER, flavor)} " +
"#{objects.join(' ')} " +
"#{libraries.collect{ | lib | "-l#{ lib }" }.join(' ') } " +
"#{external_libraries.collect{ | lib | "-l#{ lib }" }.join(' ') } " +
"#{SYSTEMLIBRARIES}"
end
def build_executable executable, objects, libraries, external_libraries, flavor
File.delete(executable) if File.exist?(executable)
sh "#{CPP} #{objects.join(' ')} -o #{executable} #{LINK_FLAGS} #{LINK_FLAGS_BY_FLAVORS[flavor]} " +
"-L#{File.join(LIBRARY_FOLDER, flavor)} " +
"-L#{BOOST_LIBRARY_FOLDER} " +
"#{libraries.collect{ | lib | "-l#{ lib }" }.join(' ') } " +
"#{external_libraries.collect{ | lib | "-l#{ lib }" }.join(' ') } " +
"#{SYSTEMLIBRARIES}"
end
# List of libraries in the source folder
ALL_SOURCE_FOLDERS = FileList[File.join(SOURCE_FOLDER, '*')].reject do |file|
!File.directory?(file)
end
LIBRARIES = ALL_SOURCE_FOLDERS.collect do | lib_path |
library_name_from_path lib_path
end.reject do | lib_name |
LIBRARY_EXCEPTIONS.include? lib_name
end
FLAVORS.each do |flavor|
directory File.join(LIBRARY_FOLDER, flavor)
LIBRARIES.each do |lib|
CLEAN.include File.join(OUTPUT_FOLDER, flavor, lib, '*.' + OBJECT_FILE_EXT)
CLEAN.include File.join(OUTPUT_FOLDER, flavor, lib, '*.' + DEPENDS_FILE_EXT)
GCOV_PATTERN.each { |p| CLEAN.include File.join(OUTPUT_FOLDER, flavor, lib, p) }
end
CLOBBER.include File.join(LIBRARY_FOLDER, flavor, '*');
end
def create_object_to_source_dependencies source_files, flavor
[source_files].flatten.collect do |source_file|
object_file = object_file_name_from_source source_file, flavor, OBJECT_FILE_EXT
depends_file = object_file_name_from_source source_file, flavor, DEPENDS_FILE_EXT
object_dir = File.dirname object_file
directory object_dir
depends = [source_file] + read_dependencies(depends_file) + [object_dir]
file object_file => depends + [depends_file] do
build_object(source_file, object_file, flavor, depends_file)
end
file depends_file => depends do
build_dependencies(source_file, depends_file, flavor)
end
object_file
end
end
# create libraries task
LIBRARY_TASKS = LIBRARIES.collect do |library_name|
libs_by_name_and_flavor = {}
source_files = FileList[File.join(SOURCE_FOLDER, library_name, '*.cpp')].exclude(/.*test\.cpp/)
FLAVORS.each do |flavor|
object_files = create_object_to_source_dependencies source_files, flavor
lib_file = library_file_name(library_name, flavor)
lib_dir = File.dirname lib_file
directory lib_dir
file lib_file => lib_dir
libs_by_name_and_flavor[[library_name, flavor]] = file lib_file => object_files do |lib|
build_library lib.name, object_files, flavor unless object_files.empty?
end
end
task library_name.to_sym, [:flavor] do |t, args|
args.with_defaults(:flavor => 'debug')
check_flavor args.flavor
libs_by_name_and_flavor[[library_name, args.flavor]].invoke
end
end
def run_test test_name, flavor, single_test
sh exe_file_name( test_name, flavor ), single_test == 'all' ? '' : "--run_test=#{single_test}"
end
def create_executable_task name, *dependencies
params = {:libraries => [], :extern_libs => [], :sources => [] }
dependencies.each {|p| params.merge! p }
sources = params.delete(:sources)
libraries = params.delete(:libraries)
external_libs = params.delete(:extern_libs)
raise "unrecongnized parameters to 'test': #{params}" unless params.empty?
tests_by_flavor = {}
FLAVORS.each do |flavor|
exe_name = exe_file_name name, flavor
exe_dir = File.dirname exe_name
object_files = create_object_to_source_dependencies sources, flavor
libraries_files = libraries.collect {|l| library_file_name l, flavor }
external_lib_files = external_libs.collect {|e| external_library_file_name e, flavor }
directory exe_dir
file exe_name => exe_dir
tests_by_flavor[flavor] = file exe_name => object_files + libraries_files do |exe|
build_executable exe_name, object_files, libraries, external_lib_files, flavor
end
end
task name.to_sym, [ :flavor, :single_test ] do |t, args|
args.with_defaults(:flavor => 'debug', :single_test => 'all' )
check_flavor args.flavor
tests_by_flavor[args.flavor].invoke
run_test( name, args.flavor, args.single_test )
end
end
# builds tasks for the given test_name.
def test test_name, *dependencies
ALL_TEST_NAMES << test_name
UNIT_TEST_NAMES << test_name
create_executable_task test_name, *dependencies
end
def js_test test_name, *options
ALL_TEST_NAMES << test_name
UNIT_TEST_NAMES << test_name
mocha_test_task test_name, *options
end
def build_example example_name, *dependencies
ALL_EXAMPLE_NAMES << example_name
create_executable_task example_name, *dependencies
end
def component_test test_name, *dependencies
ALL_TEST_NAMES << test_name
COMPONENT_TEST_NAMES << test_name
params = { :sources => [], :dependencies => [] }
dependencies.each {|p| params.merge! p }
sources = params.delete( :sources )
dependencies = params.delete( :dependencies )
raise "unrecongnized parameters to 'component_test': #{params}" unless params.empty?
task test_name.to_sym, [ :argument ] => dependencies do | t, args |
single_test = args[ :argument ]
sources.each do | source |
require source
runargs = single_test.nil? ? [] : [ '-n' , "#{single_test}" ]
error_cnt = MiniTest::Unit.new.run runargs
raise "#{error_cnt} errors occured while running \'#{test_name}\'" if error_cnt != 0
end
end
end
# build tasks for a ruby extension
def ruby_extension lib_name, *dependencies
SHARED_LIBARARIES << lib_name
params = { :libraries => [], :extern_libs => [], :sources => [] }
dependencies.each { | p | params.merge! p }
sources = params.delete( :sources )
libraries = params.delete( :libraries )
external_libs = params.delete( :extern_libs )
raise "unrecongnized parameters to 'shared_library': #{params}" unless params.empty?
FLAVORS.each do | flavor |
lib_file = shared_library_file_name lib_name, flavor
lib_dir = File.dirname lib_file
object_files = create_object_to_source_dependencies sources, flavor
libraries_files = libraries.collect {|l| library_file_name l, flavor }
external_lib_files = external_libs.collect {|e| external_library_file_name e, flavor }
directory lib_dir
file lib_file => lib_dir
file lib_file => object_files + libraries_files do | args |
build_ruby_extension lib_file, object_files, libraries, external_lib_files, flavor
end
end
task lib_name.to_sym, [ :flavor ] do |t, args|
args.with_defaults( :flavor => 'debug' )
check_flavor args.flavor
Rake::Task[ shared_library_file_name lib_name, args.flavor ].invoke
end
end
# include sub rake files (this will populate ALL_TEST_NAMES)
ALL_SOURCE_FOLDERS.each do |folder|
rakefile = File.join( folder, 'rakefile')
if File.exist? rakefile then
load rakefile
end
end
desc 'build all libraries used to build a sioux server'
task :libs, [:flavor] do |t, args|
args.with_defaults(:flavor => 'debug')
check_flavor args.flavor
LIBRARY_TASKS.each { |t| t.invoke(args.flavor) }
end
desc 'run all unit tests'
task :unit_tests, [ :flavor ] => UNIT_TEST_NAMES
desc 'run all component tests'
task :component_tests, [ :flavor ] => COMPONENT_TEST_NAMES
desc 'run all tests'
task :tests, [ :flavor ] => [ :unit_tests, :component_tests ]
desc 'lists the available unit tests'
task :list_unit_tests do
puts "list of all available unit tests:\n\n"
puts UNIT_TEST_NAMES.collect{|t| "\t#{t}" }.join("\n")
puts <<EOD
All unit tests have two parameters. The first parameter is the build flavor, the second parameter is the test case to be run.
The default for the flavor is 'debug'. The default for the test case is 'all'. Example:
\trake json_test[debug,json_string_test]
builds the json_test and executes just the json_string_test test case out of the json_test.
EOD
end
desc 'list the available component tests'
task :list_component_tests do
puts "list of all available component tests:\n\n"
COMPONENT_TEST_NAMES.each{ | test | puts "\t#{test}\n" };
puts <<EOD.gsub( /^ /, '' )
All component take a single test name as an optional parameter to execute just a single test. Example:
\trake bayeux_network_test[test_simple_subscribe_publish]
EOD
end
desc 'lists the available tests'
task :list_tests do
puts "list of all available tests:\n\n"
puts ALL_TEST_NAMES.collect{|t| "\t#{t}" }.join("\n")
puts
end
desc 'lists the available examples'
task :list_examples do
puts "list of all available examples:\n\n"
puts ALL_EXAMPLE_NAMES.collect{ |t| "\t#{t}" }.join( "\n" )
puts
end
directory DOCUMENTATION_FOLDER
desc 'build html documentation'
task :docu => DOCUMENTATION_FOLDER do
sh "doxygen #{ File.expand_path( File.join( SOURCE_FOLDER, 'doxybuild' ) ) } 1> #{ DEVICE_NULL }"
end
desc 'build a \'documentation.zip\' file.'
task :docu_zip => :docu do
sh "zip -jDr9 documentation.zip #{DOCUMENTATION_FOLDER}"
end
desc 'lists all flavors and there meaning'
task :list_flavors do
print <<STOP
'debug' With asserts anabled, without optimization
and with debug symbols.
'release' With full optimization enabled, without debug
symbols, without asserts.
'coverage' Like 'debug', but with instrumentation for
coverage analysis.
STOP
end
desc 'run travis-lint'
task 'travis-lint' do
sh 'travis-lint'
end
task :default => [:libs]