Class: RakeOE::Toolchain

Inherits:
Object
  • Object
show all
Defined in:
lib/rakeoe/toolchain.rb

Overview

Toolchain specific key value reader

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Toolchain

Initializes object

Parameters:



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
# File 'lib/rakeoe/toolchain.rb', line 21

def initialize(config)
  raise 'Configuration failure' unless config.checks_pass?

  @config = config

  begin
    @kvr = KeyValueReader.new(config.platform)
  rescue Exception => e
    puts e.message
    raise
  end

  @settings = @kvr.env
  fixup_env

  # save target platform of our compiler (gcc specific)
  if RbConfig::CONFIG["host_os"] != "mingw32"
    @target=`export PATH=#{@settings['PATH']} && #{@settings['CC']} -dumpmachine`.chop
  else
    @target=`PATH = #{@settings['PATH']} & #{@settings['CC']} -dumpmachine`.chop
  end

  @settings['TOUCH'] = 'touch'
  # XXX DS: we should only instantiate @qt if we have any qt settings
  @qt = QtSettings.new(self)
  set_build_vars()

  init_test_frameworks
  sanity
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



15
16
17
# File 'lib/rakeoe/toolchain.rb', line 15

def config
  @config
end

#qtObject (readonly)

Returns the value of attribute qt.



15
16
17
# File 'lib/rakeoe/toolchain.rb', line 15

def qt
  @qt
end

#settingsObject (readonly)

Returns the value of attribute settings.



15
16
17
# File 'lib/rakeoe/toolchain.rb', line 15

def settings
  @settings
end

#targetObject (readonly)

Returns the value of attribute target.



15
16
17
# File 'lib/rakeoe/toolchain.rb', line 15

def target
  @target
end

Instance Method Details

#app(params = {}) ⇒ Object

Creates application

Parameters:

  • params (Hash) (defaults to: {})

Options Hash (params):

  • :objects (Array)

    array of object file paths

  • :libs (Array)

    array of libraries that should be linked against

  • :app (String)

    application filename path

  • :settings (Hash)

    project specific settings

  • :includes (Array)

    include paths used



507
508
509
510
511
512
513
514
515
516
517
# File 'lib/rakeoe/toolchain.rb', line 507

def app(params = {})
  incs    = compiler_incs_for(params[:includes])
  ldflags = params[:settings]['ADD_LDFLAGS'] + ' ' + @settings['LDFLAGS']
  objs    = params[:objects].join(' ')
  dep_libs = (params[:libs] + libs_for_binary(params[:app])).uniq
  libs    = linker_line_for(dep_libs)

  sh "#{@settings['SIZE']} #{objs} >#{params[:app]}.size" if @settings['SIZE']
  sh "#{@settings['CXX']} #{incs} #{objs} #{ldflags} #{libs} -o #{params[:app]} -Wl,-Map,#{params[:app]}.map"
  sh "#{@settings['OBJCOPY']} -O binary #{params[:app]} #{params[:app]}.bin"
end

#app_setting(name, setting) ⇒ Object

returns app project setting



125
126
127
# File 'lib/rakeoe/toolchain.rb', line 125

def app_setting(name, setting)
  @apps.get(name, setting)
end

#as_source_extensionsObject

returns assembler source extensions



140
141
142
# File 'lib/rakeoe/toolchain.rb', line 140

def as_source_extensions
  @config.suffixes[:as_sources].uniq
end

#build_dirObject

returns the build directory



70
71
72
# File 'lib/rakeoe/toolchain.rb', line 70

def build_dir
  "#{@config.directories[:build]}/#{@target}/#{@config.release}"
end

#c_header_extensionsObject

returns c header extensions



155
156
157
# File 'lib/rakeoe/toolchain.rb', line 155

def c_header_extensions
  @config.suffixes[:c_headers].uniq
end

#c_source_extensionsObject

returns c source extensions



135
136
137
# File 'lib/rakeoe/toolchain.rb', line 135

def c_source_extensions
  @config.suffixes[:c_sources].uniq
end

#compiler_incs_for(paths) ⇒ String

Generates compiler include line from given include path list

Parameters:

  • paths (Array)

    Paths to be used for include file search

Returns:

  • (String)

    Compiler include line



286
287
288
# File 'lib/rakeoe/toolchain.rb', line 286

def compiler_incs_for(paths)
  paths.each_with_object('') {|path, str| str << " -I#{path}"}
end

#config_empty_test_frameworkObject

Configures empty test framework



95
96
97
98
99
100
101
# File 'lib/rakeoe/toolchain.rb', line 95

def config_empty_test_framework
  @@test_framework[''] = TestFramework.new(:name         => '',
                                           :binary_path  => '',
                                           :include_dir  => '',
                                           :cflags       => '')

end

#cpp_header_extensionsObject

returns c++ header extensions



150
151
152
# File 'lib/rakeoe/toolchain.rb', line 150

def cpp_header_extensions
  (@config.suffixes[:cplus_headers] + [@config.suffixes[:moc_header]]).uniq
end

#cpp_source_extensionsObject

returns c++ source extensions



130
131
132
# File 'lib/rakeoe/toolchain.rb', line 130

def cpp_source_extensions
  (@config.suffixes[:cplus_sources] + [@config.suffixes[:moc_source]]).uniq
end

#current_platform_any?(platforms) ⇒ Boolean

Tests given list of platforms if any of those matches the current platform

Returns:

  • (Boolean)


276
277
278
# File 'lib/rakeoe/toolchain.rb', line 276

def current_platform_any?(platforms)
  ([@target] & platforms).any?
end

#default_test_frameworkObject

Returns default test framework or nil if none defined



104
105
106
# File 'lib/rakeoe/toolchain.rb', line 104

def default_test_framework
  test_framework(@config.test_fw) || test_framework('')
end

#dep(params = {}) ⇒ Object

Creates dependency

Parameters:

  • params (Hash) (defaults to: {})

Options Hash (params):

  • :source (String)

    source filename with path

  • :dep (String)

    dependency filename path

  • :settings (Hash)

    project specific settings

  • :includes (Array)

    include paths used



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/rakeoe/toolchain.rb', line 435

def dep(params = {})
  extension = File.extname(params[:source])
  dep       = params[:dep]
  source    = params[:source]
  incs      = compiler_incs_for(params[:includes]) + " #{@settings['LIB_INC']}"
  case
    when cpp_source_extensions.include?(extension)
      flags = @settings['CXXFLAGS'] + ' ' + params[:settings]['ADD_CXXFLAGS']
      compiler = "#{@settings['CXX']} -x c++ "
    when c_source_extensions.include?(extension)
      flags  = @settings['CFLAGS'] + ' ' + params[:settings]['ADD_CFLAGS']
      compiler = "#{@settings['CC']} -x c "
    when as_source_extensions.include?(extension)
      flags = ''
      compiler = "#{@settings['AS']}"
    else
      raise "unsupported source file extension (#{extension}) for creating dependency!"
  end
  sh "#{compiler} -MM #{flags} #{incs} -c #{source} -MT #{dep.ext('.o')} -MF #{dep}", silent: true
end

#diagnose_buildability(projects) ⇒ Object



366
367
368
369
370
371
372
# File 'lib/rakeoe/toolchain.rb', line 366

def diagnose_buildability(projects)
  projects.each do |project|

    RakeOE::PrjFileCache.project_entry_buildable?(entry, platform)
  end

end

#dumpObject



540
541
542
543
544
545
# File 'lib/rakeoe/toolchain.rb', line 540

def dump
  puts '**************************'
  puts '* Platform configuration *'
  puts '**************************'
  @kvr.dump
end

#fixup_envObject

Specific fixups for toolchain



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/rakeoe/toolchain.rb', line 170

def fixup_env
  # set system PATH if no PATH defined
  @settings['PATH'] ||= ENV['PATH']

  # replace $PATH
  @settings['PATH'] = @settings['PATH'].gsub('$PATH', ENV['PATH'])

  # create ARCH
  @settings['ARCH'] = "#{@settings['TARGET_PREFIX']}".chop

  # remove optimizations, we set these explicitly
  @settings['CXXFLAGS'] = "#{@settings['CXXFLAGS']} -DPROGRAM_VERSION=\\\"#{@config.sw_version}\\\"".gsub('-O2', '')
  @settings['CFLAGS'] = "#{@settings['CFLAGS']} -DPROGRAM_VERSION=\\\"#{@config.sw_version}\\\"".gsub('-O2', '')
  KeyValueReader.substitute_dollar_symbols!(@settings)
end

#init_test_frameworksObject

Initializes definitions for test framework TODO: Add possibility to configure test framework specific CFLAGS/CXXFLAGS



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rakeoe/toolchain.rb', line 77

def init_test_frameworks()
  @@test_framework ||= Hash.new

  config_empty_test_framework

  if @config.test_fw.size > 0
    if PrjFileCache.contain?('LIB', @config.test_fw)
      @@test_framework[@config.test_fw] = TestFramework.new(:name         => @config.test_fw,
                                                            :binary_path  => "#{@settings['LIB_OUT']}/lib#{@config.test_fw}.a",
                                                            :include_dir  => PrjFileCache.exported_lib_incs(@config.test_fw),
                                                            :cflags       => '')
    else
        puts "WARNING: Configured test framework (#{@config.test_fw}) does not exist in project!"
    end
  end
end

#lib(params = {}) ⇒ Object

Creates library

Parameters:

  • params (Hash) (defaults to: {})

Options Hash (params):

  • :objects (Array)

    object filename paths

  • :lib (String)

    library filename path

  • :settings (Hash)

    project specific settings



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/rakeoe/toolchain.rb', line 478

def lib(params = {})
  ldflags   = params[:settings]['ADD_LDFLAGS'] + ' ' + @settings['LDFLAGS']
  objs      = params[:objects].join(' ')
  dep_libs = (params[:libs] + libs_for_binary(params[:lib])).uniq
  libs      = linker_line_for(dep_libs)
  extension = File.extname(params[:lib])

  case extension
    when ('.a')
      # need to use 'touch' for correct timestamp, ar doesn't update the timestamp
      # if archive hasn't changed
      sh "#{@settings['AR']} curv #{params[:lib]} #{objs} && #{@settings['TOUCH']} #{params[:lib]}"
    when '.so'
      sh "#{@settings['CXX']} -shared  #{ldflags} #{libs} #{objs} -o #{params[:lib]}"
    else
      raise "unsupported library extension (#{extension})!"
  end
end

#lib_setting(name, setting) ⇒ Object

returns library project setting



120
121
122
# File 'lib/rakeoe/toolchain.rb', line 120

def lib_setting(name, setting)
  @libs.get(name, setting)
end

#libs_for_binary(a_binary, visited = []) ⇒ Object

Return array of library prerequisites for given file



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/rakeoe/toolchain.rb', line 334

def libs_for_binary(a_binary, visited=[])
  return [] if visited.include?(a_binary)
  visited << a_binary
  pre = Rake::Task[a_binary].prerequisites
  rv = []
  pre.each do |p|

    next if (File.extname(p) != '.a') && (File.extname(p) != '.so')
    next if p =~ /\-app\.a/

    rv << File.basename(p).gsub(/(\.a|\.so|^lib)/, '')
    rv += libs_for_binary(p, visited)   # Recursive call
  end

  reduce_libs_to_bare_minimum(rv.uniq)
end

#linker_line_for(libs) ⇒ String

Generates linker line from given library list. The linker line normally will be like -l<lib1> -l<lib2>, …

If a library has specific platform specific setting in the platform file with a specific -l<lib> alternative, this will be used instead.

Parameters:

  • libs (Array)

    Libraries to be used for linker line

Returns:

  • (String)

    Linker line



300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/rakeoe/toolchain.rb', line 300

def linker_line_for(libs)
  return '' if (libs.nil? || libs.empty?)

  libs.map do |lib|
    settings = platform_settings_for(lib)
    if settings[:LDFLAGS].nil? || settings[:LDFLAGS].empty?
      # automatic linker line if no platform specific LDFLAGS exist
      "-l#{lib}"
    else
      # only matches -l<libname> settings
      /(\s|^)+-l\S+/.match(settings[:LDFLAGS]).to_s
    end
  end.join(' ').strip
end

#moc(params = {}) ⇒ Object

Creates moc_ source file

Parameters:

  • params (Hash) (defaults to: {})

Options Hash (params):

  • :source (String)

    source filename with path

  • :moc (String)

    moc_XXX filename path

  • :settings (Hash)

    project specific settings



464
465
466
467
468
# File 'lib/rakeoe/toolchain.rb', line 464

def moc(params = {})
  moc_compiler = @settings['OE_QMAKE_MOC']
  raise 'No Qt Toolchain set' if moc_compiler.empty?
  sh "#{moc_compiler} -i -f#{File.basename(params[:source])} #{params[:source]} >#{params[:moc]}"
end

#moc_header_extensionObject

returns moc header extensions



160
161
162
# File 'lib/rakeoe/toolchain.rb', line 160

def moc_header_extension
  @config.suffixes[:moc_header]
end

#moc_sourceObject

returns c++ header extensions



165
166
167
# File 'lib/rakeoe/toolchain.rb', line 165

def moc_source
  @config.suffixes[:moc_source]
end

#obj(params = {}) ⇒ Object

Creates compilation object

Parameters:

  • params (Hash) (defaults to: {})

Options Hash (params):

  • :source (String)

    source filename with path

  • :object (String)

    object filename path

  • :settings (Hash)

    project specific settings

  • :includes (Array)

    include paths used



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/rakeoe/toolchain.rb', line 404

def obj(params = {})
  extension = File.extname(params[:source])
  object    = params[:object]
  source    = params[:source]
  incs      = compiler_incs_for(params[:includes]) + " #{@settings['LIB_INC']}"

  case
    when cpp_source_extensions.include?(extension)
      flags = @settings['CXXFLAGS'] + ' ' + params[:settings]['ADD_CXXFLAGS']
      compiler = "#{@settings['CXX']} -x c++ "
    when c_source_extensions.include?(extension)
      flags = @settings['CFLAGS'] + ' ' + params[:settings]['ADD_CFLAGS']
      compiler = "#{@settings['CC']} -x c "
    when as_source_extensions.include?(extension)
      flags = ''
      compiler = "#{@settings['AS']}"
    else
      raise "unsupported source file extension (#{extension}) for creating object!"
  end
  sh "#{compiler} #{flags} #{incs} -c #{source} -o #{object}"
end

#platform_settings_for(resource_name) ⇒ Hash

Returns platform specific settings of a resource (APP/LIB/SOLIB or external resource like e.g. an external library) as a hash with the keys CFLAGS, CXXFLAGS and LDFLAGS. The values are empty if no such resource settings exist inside the platform file. The resulting hash values can be used for platform specific compilation/linkage against the the resource.

Parameters:

  • resource_name (String)

    name of resource

Returns:

  • (Hash)

    Hash of compilation/linkage flags or empty hash if no settings are defined The returned hash has the following format: { :CFLAGS => ‘…’, :CXXFLAGS => ‘…’, :LDFLAGS => ‘…’}



385
386
387
388
389
390
391
392
393
394
# File 'lib/rakeoe/toolchain.rb', line 385

def platform_settings_for(resource_name)
  return {} if resource_name.empty?

  rv = Hash.new
  rv[:CFLAGS]  = @settings["#{resource_name}_CFLAGS"]
  rv[:CXXFLAGS]= @settings["#{resource_name}_CXXFLAGS"]
  rv[:LDFLAGS] = @settings["#{resource_name}_LDFLAGS"]
  rv = {} if rv.values.empty?
  rv
end

#reduce_libs_to_bare_minimum(libs) ⇒ Object

Reduces the given list of libraries to bare minimum, i.e. the minimum needed for actual platform

Returns:

  • reduced list of libraries



323
324
325
326
327
328
329
330
# File 'lib/rakeoe/toolchain.rb', line 323

def reduce_libs_to_bare_minimum(libs)
  rv = libs.clone
  lib_entries = RakeOE::PrjFileCache.get_lib_entries(libs)
  lib_entries.each_pair do |lib, entry|
    rv.delete(lib) unless RakeOE::PrjFileCache.project_entry_buildable?(entry, @target)
  end
  rv
end

#rm(files) ⇒ Object

Removes list of given files

Parameters:

  • files (String)

    List of files to be deleted



242
243
244
245
246
# File 'lib/rakeoe/toolchain.rb', line 242

def rm(files)
  if files
    Rake::sh "rm -f #{files}" unless files.empty?
  end
end

#run(binary) ⇒ Object

Executes a given binary

Parameters:

  • binary (String)

    Absolute path of the binary to be executed



253
254
255
256
257
258
259
260
# File 'lib/rakeoe/toolchain.rb', line 253

def run(binary)
  # compare ruby platform config and our target setting
  if @target[RbConfig::CONFIG["target_cpu"]]
    system "export LD_LIBRARY_PATH=#{@settings['LD_LIBRARY_PATH']} && #{binary}"
  else
    puts "Warning: Can't execute on this platform: #{binary}"
  end
end

#run_junit_test(binary) ⇒ Object

Executes a given test binary with test runner specific parameter(s)

Parameters:

  • binary (String)

    Absolute path of the binary to be executed



266
267
268
269
270
271
272
273
# File 'lib/rakeoe/toolchain.rb', line 266

def run_junit_test(binary)
  # compare ruby platform config and our target setting
  if @target[RbConfig::CONFIG["target_cpu"]]
    system "export LD_LIBRARY_PATH=#{@settings['LD_LIBRARY_PATH']} && #{binary} -o junit"
  else
    puts "Warning: Can't execute test on this platform: #{binary}"
  end
end

#sanityObject

Do some sanity checks



54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rakeoe/toolchain.rb', line 54

def sanity
  # TODO DS: check if libs and apps directories exist
  # TODO DS: check if test frameworks exist
  # check if target is valid

  if @settings['CC'].empty?
    raise "No Compiler specified. Either add platform configuration via RakeOE::Config object in Rakefile or use TOOLCHAIN_ENV environment variable"
  end

  if @target.nil? || @target.empty?
    raise "Compiler #{@settings['CC']} does not work. Fix platform settings or use TOOLCHAIN_ENV environment variable "
  end

end

#set_build_varsObject

Set common build variables



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
# File 'lib/rakeoe/toolchain.rb', line 189

def set_build_vars
  warning_flags = ' -W -Wall'
  if 'release' == @config.release
    optimization_flags = " #{@config.optimization_release} -DRELEASE"
  else
    optimization_flags = " #{@config.optimization_dbg} -g"
  end

  # we could make these also arrays of source directories ...
  @settings['APP_SRC_DIR'] = 'src/app'
  @settings['LIB_SRC_DIR'] = 'src/lib'

  # derived settings
  @settings['BUILD_DIR'] = "#{build_dir}"
  @settings['LIB_OUT'] = "#{@settings['BUILD_DIR']}/libs"
  @settings['APP_OUT'] = "#{@settings['BUILD_DIR']}/apps"
  unless @settings['OECORE_TARGET_SYSROOT'].nil? || @settings['OECORE_TARGET_SYSROOT'].empty?
    @settings['SYS_LFLAGS'] = "-L#{@settings['OECORE_TARGET_SYSROOT']}/lib -L#{@settings['OECORE_TARGET_SYSROOT']}/usr/lib"
  end

  # set LD_LIBRARY_PATH
  @settings['LD_LIBRARY_PATH'] = @settings['LIB_OUT']

  # standard settings
  @settings['CXXFLAGS'] += warning_flags + optimization_flags + " #{@config.language_std_cpp}"
  @settings['CFLAGS'] += warning_flags + optimization_flags + " #{@config.language_std_c}"
  if @settings['PRJ_TYPE'] == 'SOLIB'
    @settings['CXXFLAGS'] += ' -fPIC'
    @settings['CFLAGS'] += ' -fPIC'
  end
  # !! don't change order of the following string components without care !!
  @settings['LDFLAGS'] = @settings['LDFLAGS'] + " -L #{@settings['LIB_OUT']} #{@settings['SYS_LFLAGS']} -Wl,--no-as-needed -Wl,--start-group"
end

#sh(cmd, silent = false) ⇒ Object

Executes the command



224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/rakeoe/toolchain.rb', line 224

def sh(cmd, silent = false)

  if RbConfig::CONFIG["host_os"] != "mingw32"
    full_cmd = "export PATH=#{@settings['PATH']} && #{cmd}"
  else
    full_cmd = "PATH = #{@settings['PATH']} & #{cmd}"
  end

  if silent
    system full_cmd
  else
    Rake::sh full_cmd
  end
end

#source_extensionsObject

returns all source extensions



145
146
147
# File 'lib/rakeoe/toolchain.rb', line 145

def source_extensions
  cpp_source_extensions + c_source_extensions + as_source_extensions
end

#test(params = {}) ⇒ Object

Creates test

Parameters:

  • params (Hash) (defaults to: {})

Options Hash (params):

  • :objects (Array)

    array of object file paths

  • :libs (Array)

    array of libraries that should be linked against

  • :framework (String)

    test framework name

  • :test (String)

    test filename path

  • :settings (Hash)

    project specific settings

  • :includes (Array)

    include paths used



529
530
531
532
533
534
535
536
537
538
# File 'lib/rakeoe/toolchain.rb', line 529

def test(params = {})
  incs    = compiler_incs_for(params[:includes])
  ldflags = params[:settings]['ADD_LDFLAGS'] + ' ' +  @settings['LDFLAGS']
  objs    = params[:objects].join(' ')
  test_fw = linker_line_for([params[:framework]])
  dep_libs = (params[:libs] + libs_for_binary(params[:test])).uniq
  libs    = linker_line_for(dep_libs)

  sh "#{@settings['CXX']} #{incs} #{objs} #{test_fw} #{ldflags} #{libs} -o #{params[:test]}"
end

#test_all_files_exist?(files) ⇒ Boolean

Tests if all given files in given list exist

Returns:

  • (Boolean)

    true all file exist

  • false not all file exist



360
361
362
363
364
# File 'lib/rakeoe/toolchain.rb', line 360

def test_all_files_exist?(files)
  files.each do |file|
    raise "No such file: #{file}" unless File.exist?(file)
  end
end

#test_framework(name) ⇒ Object

Returns definitions of specific test framework or none if specified test framework doesn’t exist



110
111
112
# File 'lib/rakeoe/toolchain.rb', line 110

def test_framework(name)
  @@test_framework[name]
end

#test_frameworksObject

Returns list of all registered test framework names



115
116
117
# File 'lib/rakeoe/toolchain.rb', line 115

def test_frameworks
  @@test_framework.keys
end

#touch(file) ⇒ Object

Touches a file



352
353
354
# File 'lib/rakeoe/toolchain.rb', line 352

def touch(file)
  sh "#{@settings['TOUCH']} #{file}"
end