bake-toolkit 2.24.0 → 2.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,531 +1,521 @@
1
- require 'blocks/blockBase'
2
- require 'multithread/job'
3
- require 'common/process'
4
- require 'common/utils'
5
- require 'bake/toolchain/colorizing_formatter'
6
- require 'bake/config/loader'
7
-
8
- module Bake
9
-
10
- module Blocks
11
-
12
- class Compile < BlockBase
13
-
14
- attr_reader :objects, :include_list
15
-
16
- def initialize(block, config, referencedConfigs, tcs)
17
- super(block, config, referencedConfigs, tcs)
18
- @objects = []
19
- @object_files = {}
20
-
21
- calcFileTcs
22
- calcIncludes
23
- calcDefines # not for files with changed tcs
24
- calcFlags # not for files with changed tcs
25
- end
26
-
27
- def get_object_file(source)
28
-
29
- # until now all OBJECT_FILE_ENDING are equal in all three types
30
- adaptedSource = source.chomp(File.extname(source)).gsub(/\.\./, "##") + (Bake.options.prepro ? ".i" : @tcs[:COMPILER][:CPP][:OBJECT_FILE_ENDING])
31
- return adaptedSource if File.is_absolute?source
32
- File.join([@output_dir, adaptedSource])
33
- end
34
-
35
- def ignore?(type)
36
- Bake.options.linkOnly or (Bake.options.prepro and type == :ASM)
37
- end
38
-
39
- def needed?(source, object, type, dep_filename_conv)
40
- return "because analyzer toolchain is configured" if Bake.options.analyze
41
- return "because prepro was specified and source is no assembler file" if Bake.options.prepro
42
-
43
- return "because object does not exist" if not File.exist?(object)
44
- oTime = File.mtime(object)
45
-
46
- return "because source is newer than object" if oTime < File.mtime(source)
47
-
48
- if type != :ASM
49
- return "because dependency file does not exist" if not File.exist?(dep_filename_conv)
50
-
51
- begin
52
- File.readlines(dep_filename_conv).map{|line| line.strip}.each do |dep|
53
- if not File.exist?(dep)
54
- # we need a hack here. with some windows configurations the compiler prints unix paths
55
- # into the dep file which cannot be found easily. this will be true for system includes,
56
- # e.g. /usr/lib/...xy.h
57
- if (Bake::Utils::OS.windows? and dep.start_with?"/") or
58
- (not Bake::Utils::OS.windows? and dep.length > 1 and dep[1] == ":")
59
- puts "Dependency header file #{dep} ignored!" if Bake.options.debug
60
- else
61
- return "because dependent header #{dep} does not exist"
62
- end
63
- else
64
- return "because dependent header #{dep} is newer than object" if oTime < File.mtime(dep)
65
- end
66
- end
67
- rescue Exception => ex
68
- if Bake.options.debug
69
- puts "While reading #{dep_filename_conv}:"
70
- puts ex.message
71
- puts ex.backtrace
72
- end
73
- return "because dependency file could not be loaded"
74
- end
75
- end
76
-
77
- false
78
- end
79
-
80
- def calcCmdlineFile(object)
81
- object[0..-3] + ".cmdline"
82
- end
83
-
84
- def calcDepFile(object, type)
85
- dep_filename = nil
86
- if type != :ASM
87
- dep_filename = object[0..-3] + ".d"
88
- end
89
- dep_filename
90
- end
91
-
92
- def calcDepFileConv(dep_filename)
93
- dep_filename + ".bake"
94
- end
95
-
96
- def get_source_type(source)
97
- ex = File.extname(source)
98
- [:CPP, :C, :ASM].each do |t|
99
- return t if @tcs[:COMPILER][t][:SOURCE_FILE_ENDINGS].include?(ex)
100
- end
101
- nil
102
- end
103
-
104
- def compileFile(source)
105
- type = get_source_type(source)
106
- return if type.nil?
107
-
108
- object = @object_files[source]
109
-
110
- dep_filename = calcDepFile(object, type)
111
- dep_filename_conv = calcDepFileConv(dep_filename) if type != :ASM
112
-
113
- cmdLineCheck = false
114
- cmdLineFile = calcCmdlineFile(object)
115
-
116
- return if ignore?(type)
117
- reason = needed?(source, object, type, dep_filename_conv)
118
- if not reason
119
- cmdLineCheck = true
120
- reason = config_changed?(cmdLineFile)
121
- end
122
-
123
- if @fileTcs.include?(source)
124
- compiler = @fileTcs[source][:COMPILER][type]
125
- defines = getDefines(compiler)
126
- flags = getFlags(compiler)
127
- else
128
- compiler = @tcs[:COMPILER][type]
129
- defines = @define_array[type]
130
- flags = @flag_array[type]
131
- end
132
- includes = @include_array[type]
133
-
134
- if Bake.options.prepro and compiler[:PREPRO_FLAGS] == ""
135
- Bake.formatter.printError("Error: No preprocessor option available for " + source)
136
- raise SystemCommandFailed.new
137
- end
138
-
139
- cmd = Utils.flagSplit(compiler[:COMMAND], false)
140
- cmd += compiler[:COMPILE_FLAGS].split(" ")
141
-
142
- if dep_filename
143
- cmd += @tcs[:COMPILER][type][:DEP_FLAGS].split(" ")
144
- if @tcs[:COMPILER][type][:DEP_FLAGS_FILENAME]
145
- if @tcs[:COMPILER][type][:DEP_FLAGS_SPACE]
146
- cmd << dep_filename
147
- else
148
- if dep_filename.include?" "
149
- cmd[cmd.length-1] << "\"" + dep_filename + "\""
150
- else
151
- cmd[cmd.length-1] << dep_filename
152
- end
153
-
154
- end
155
- end
156
- end
157
-
158
- cmd += compiler[:PREPRO_FLAGS].split(" ") if Bake.options.prepro
159
- cmd += flags
160
- cmd += includes
161
- cmd += defines
162
-
163
- offlag = compiler[:OBJECT_FILE_FLAG]
164
- offlag = compiler[:PREPRO_FILE_FLAG] if compiler[:PREPRO_FILE_FLAG] and Bake.options.prepro
165
-
166
- if compiler[:OBJ_FLAG_SPACE]
167
- cmd << offlag
168
- cmd << object
169
- else
170
- if object.include?" "
171
- cmd << offlag + "\"" + object + "\""
172
- else
173
- cmd << offlag + object
174
- end
175
- end
176
- cmd << source
177
-
178
- if Bake.options.cc2j_filename
179
- cmdJson = cmd.is_a?(Array) ? cmd.join(' ') : cmd
180
- Blocks::CC2J << { :directory => @projectDir, :command => cmdJson, :file => source }
181
- end
182
-
183
- if not (cmdLineCheck and BlockBase.isCmdLineEqual?(cmd, cmdLineFile))
184
- BlockBase.prepareOutput(object)
185
- BlockBase.writeCmdLineFile(cmd, cmdLineFile)
186
- success, consoleOutput = ProcessHelper.run(cmd, false, false)
187
-
188
- outputType = Bake.options.analyze ? "Analyzing" : (Bake.options.prepro ? "Preprocessing" : "Compiling")
189
- incList = process_result(cmd, consoleOutput, compiler[:ERROR_PARSER], "#{outputType} #{source}", reason, success)
190
-
191
- if type != :ASM and not Bake.options.analyze and not Bake.options.prepro
192
- incList = Compile.read_depfile(dep_filename, @projectDir, @tcs[:COMPILER][:DEP_FILE_SINGLE_LINE]) if incList.nil?
193
- Compile.write_depfile(incList, dep_filename_conv)
194
- end
195
- check_config_file
196
- end
197
-
198
- Bake::Bundle.instance.addSource(source, @include_list, dep_filename_conv) if isMainProject?
199
-
200
- end
201
-
202
- def self.read_depfile(dep_filename, projDir, singleLine)
203
- deps = []
204
- begin
205
- if singleLine
206
- File.readlines(dep_filename).each do |line|
207
- splitted = line.split(": ")
208
- deps << splitted[1].gsub(/[\\]/,'/') if splitted.length > 1
209
- end
210
- else
211
- deps_string = File.read(dep_filename)
212
- deps_string = deps_string.gsub(/\\\n/,'')
213
- dep_splitted = deps_string.split(/([^\\]) /).each_slice(2).map(&:join)[2..-1]
214
- deps = dep_splitted.map { |d| d.gsub(/[\\] /,' ').gsub(/[\\]/,'/').strip }.delete_if {|d| d == "" }
215
- end
216
- rescue Exception => ex1
217
- Bake.formatter.printWarning("Could not read '#{dep_filename}'", projDir)
218
- puts ex1.message if Bake.options.debug
219
- return nil
220
- end
221
- deps
222
- end
223
-
224
- # todo: move to toolchain util file
225
- def self.write_depfile(deps, dep_filename_conv)
226
- if deps
227
- begin
228
- File.open(dep_filename_conv, 'wb') do |f|
229
- deps.each do |dep|
230
- f.puts(dep)
231
- end
232
- end
233
- rescue Exception
234
- Bake.formatter.printWarning("Could not write '#{dep_filename_conv}'", projDir)
235
- return nil
236
- end
237
- end
238
- end
239
-
240
- def mutex
241
- @mutex ||= Mutex.new
242
- end
243
-
244
- def execute
245
- Dir.chdir(@projectDir) do
246
-
247
- calcSources
248
- calcObjects
249
-
250
- @incWarns.each do |x|
251
- Bake.formatter.printInfo("IncludeDir '#{x[0].name}' will be converted to '#{x[1]}' although local path exists. If not intended, use './#{x[0].name}'.", x[0])
252
- end if Bake.options.verbose >= 1
253
-
254
- @error_strings = {}
255
-
256
- compileJobs = Multithread::Jobs.new(@source_files) do |jobs|
257
- while source = jobs.get_next_or_nil do
258
-
259
- if (jobs.failed and Bake.options.stopOnFirstError) or Bake::IDEInterface.instance.get_abort
260
- break
261
- end
262
-
263
- s = StringIO.new
264
- tmp = Thread.current[:stdout]
265
- Thread.current[:stdout] = s unless tmp
266
-
267
- result = false
268
- begin
269
- compileFile(source)
270
- result = true
271
- rescue Bake::SystemCommandFailed => scf # normal compilation error
272
- rescue SystemExit => exSys
273
- rescue Exception => ex1
274
- if not Bake::IDEInterface.instance.get_abort
275
- Bake.formatter.printError("Error: #{ex1.message}")
276
- puts ex1.backtrace if Bake.options.debug
277
- end
278
- end
279
-
280
- jobs.set_failed if not result
281
-
282
- Thread.current[:stdout] = tmp
283
-
284
- mutex.synchronize do
285
- if s.string.length > 0
286
- if Bake.options.stopOnFirstError and not result
287
- @error_strings[source] = s.string
288
- else
289
- puts s.string
290
- end
291
- end
292
- end
293
-
294
- end
295
- end
296
- compileJobs.join
297
-
298
- # can only happen in case of bail_on_first_error.
299
- # if not sorted, it may be confusing when builing more than once and the order of the error appearances changes from build to build
300
- # (it is not deterministic which file compilation finishes first)
301
- @error_strings.sort.each {|es| puts es[1]}
302
-
303
- raise SystemCommandFailed.new if compileJobs.failed
304
-
305
-
306
- end
307
- return true
308
- end
309
-
310
- def clean
311
- if Bake.options.filename or Bake.options.analyze
312
- Dir.chdir(@projectDir) do
313
- calcSources(true)
314
- @source_files.each do |source|
315
-
316
- type = get_source_type(source)
317
- next if type.nil?
318
- object = get_object_file(source)
319
- if File.exist?object
320
- puts "Deleting file #{object}" if Bake.options.verbose >= 2
321
- FileUtils.rm_rf(object)
322
- end
323
- if not Bake.options.analyze
324
- dep_filename = calcDepFile(object, type)
325
- if dep_filename and File.exist?dep_filename
326
- puts "Deleting file #{dep_filename}" if Bake.options.verbose >= 2
327
- FileUtils.rm_rf(dep_filename)
328
- end
329
- cmdLineFile = calcCmdlineFile(object)
330
- if File.exist?cmdLineFile
331
- puts "Deleting file #{cmdLineFile}" if Bake.options.verbose >= 2
332
- FileUtils.rm_rf(cmdLineFile)
333
- end
334
- end
335
- end
336
- end
337
- end
338
- return true
339
- end
340
-
341
- def calcObjects
342
- @source_files.each do |source|
343
- type = get_source_type(source)
344
- if not type.nil?
345
- object = get_object_file(source)
346
- if @objects.include?object
347
- @object_files.each do |k,v|
348
- if (v == object) # will be found exactly once
349
- Bake.formatter.printError("Source files '#{k}' and '#{source}' would result in the same object file", source)
350
- raise SystemCommandFailed.new
351
- end
352
- end
353
- end
354
- @object_files[source] = object
355
- @objects << object
356
- end
357
- end
358
- end
359
-
360
- def calcSources(cleaning = false, keep = false)
361
- return @source_files if @source_files and not @source_files.empty?
362
- Dir.chdir(@projectDir) do
363
- @source_files = []
364
-
365
- exclude_files = Set.new
366
- @config.excludeFiles.each do |p|
367
- Dir.glob(p.name).each {|f| exclude_files << f}
368
- end
369
-
370
- source_files = Set.new
371
- @config.files.each do |sources|
372
- p = sources.name
373
- res = Dir.glob(p).sort
374
- if res.length == 0 and cleaning == false
375
- if not p.include?"*" and not p.include?"?"
376
- Bake.formatter.printError("Source file '#{p}' not found", sources)
377
- raise SystemCommandFailed.new
378
- elsif Bake.options.verbose >= 1
379
- Bake.formatter.printInfo("Source file pattern '#{p}' does not match to any file", sources)
380
- end
381
- end
382
- res.each do |f|
383
- next if exclude_files.include?(f)
384
- next if source_files.include?(f)
385
- source_files << f
386
- @source_files << f
387
- end
388
- end
389
-
390
- if Bake.options.filename
391
- @source_files.keep_if do |source|
392
- source.include?Bake.options.filename
393
- end
394
- if @source_files.length == 0 and cleaning == false
395
- Bake.formatter.printInfo("#{Bake.options.filename} does not match to any source", @config)
396
- end
397
- end
398
-
399
- if Bake.options.eclipseOrder # directories reverse order, files in directories in alphabetical order
400
- dirs = []
401
- filemap = {}
402
- @source_files.sort.reverse.each do |o|
403
- d = File.dirname(o)
404
- if filemap.include?(d)
405
- filemap[d] << o
406
- else
407
- filemap[d] = [o]
408
- dirs << d
409
- end
410
- end
411
- @source_files = []
412
- dirs.each do |d|
413
- filemap[d].reverse.each do |f|
414
- @source_files << f
415
- end
416
- end
417
- end
418
- end
419
- @source_files
420
- end
421
-
422
- def mapInclude(inc, orgBlock)
423
-
424
- if inc.name == "___ROOTS___"
425
- return Bake.options.roots.map { |r| File.rel_from_to_project(@projectDir,r,false) }
426
- end
427
-
428
- i = orgBlock.convPath(inc,nil,true)
429
- if orgBlock != @block
430
- if not File.is_absolute?(i)
431
- i = File.rel_from_to_project(@projectDir,orgBlock.config.parent.get_project_dir) + i
432
- end
433
- end
434
-
435
- x = Pathname.new(i).cleanpath
436
- if orgBlock.warnConvValid
437
- @incWarns << [inc, x]
438
- end
439
- x
440
- end
441
-
442
- def calcIncludes
443
- @incWarns = []
444
-
445
- @include_list = @config.includeDir.uniq.map do |dir|
446
- mapInclude(dir, @block)
447
- end
448
-
449
- @block.getBlocks(:childs).each do |b|
450
- b.config.includeDir.each do |inc|
451
- if inc.inherit == true
452
- @include_list << mapInclude(inc, b)
453
- end
454
- end if b.config.respond_to?("includeDir")
455
- end
456
-
457
- @block.getBlocks(:parents).each do |b|
458
- if b.config.respond_to?("includeDir")
459
- include_list_front = []
460
- b.config.includeDir.each do |inc|
461
- if inc.inject == "front"
462
- include_list_front << mapInclude(inc, b)
463
- elsif inc.inject == "back"
464
- @include_list << mapInclude(inc, b)
465
- elsif inc.infix == "front"
466
- include_list_front << mapInclude(inc, b)
467
- elsif inc.infix == "back"
468
- @include_list << mapInclude(inc, b)
469
- end
470
- end
471
- @include_list = include_list_front + @include_list
472
- end
473
- end
474
-
475
- @include_list = @include_list.flatten.uniq
476
-
477
- @include_array = {}
478
- [:CPP, :C, :ASM].each do |type|
479
- @include_array[type] = @include_list.map {|k| "#{@tcs[:COMPILER][type][:INCLUDE_PATH_FLAG]}#{k}"}
480
- end
481
- end
482
-
483
- def getDefines(compiler)
484
- compiler[:DEFINES].map {|k| "#{compiler[:DEFINE_FLAG]}#{k}"}
485
- end
486
-
487
- def getFlags(compiler)
488
- Bake::Utils::flagSplit(compiler[:FLAGS],true)
489
- end
490
-
491
- def calcDefines
492
- @define_array = {}
493
- [:CPP, :C, :ASM].each do |type|
494
- @define_array[type] = getDefines(@tcs[:COMPILER][type])
495
- end
496
- end
497
- def calcFlags
498
- @flag_array = {}
499
- [:CPP, :C, :ASM].each do |type|
500
- @flag_array[type] = getFlags(@tcs[:COMPILER][type])
501
- end
502
- end
503
-
504
- def calcFileTcs
505
- @fileTcs = {}
506
- @config.files.each do |f|
507
- if (f.define.length > 0 or f.flags.length > 0)
508
- if f.name.include?"*"
509
- Bake.formatter.printWarning("Toolchain settings not allowed for file pattern #{f.name}", f)
510
- err_res = ErrorDesc.new
511
- err_res.file_name = @config.file_name
512
- err_res.line_number = f.line_number
513
- err_res.severity = ErrorParser::SEVERITY_WARNING
514
- err_res.message = "Toolchain settings not allowed for file patterns"
515
- Bake::IDEInterface.instance.set_errors([err_res])
516
- else
517
- @fileTcs[f.name] = integrateCompilerFile(Utils.deep_copy(@tcs),f)
518
- end
519
- end
520
- end
521
- end
522
-
523
- def tcs4source(source)
524
- @fileTcs[source] || @tcs
525
- end
526
-
527
-
528
- end
529
-
530
- end
1
+ require 'blocks/blockBase'
2
+ require 'multithread/job'
3
+ require 'common/process'
4
+ require 'common/utils'
5
+ require 'bake/toolchain/colorizing_formatter'
6
+ require 'bake/config/loader'
7
+
8
+ module Bake
9
+
10
+ module Blocks
11
+
12
+ class Compile < BlockBase
13
+
14
+ attr_reader :objects, :include_list
15
+
16
+ def initialize(block, config, referencedConfigs, tcs)
17
+ super(block, config, referencedConfigs, tcs)
18
+ @objects = []
19
+ @object_files = {}
20
+
21
+ calcFileTcs
22
+ calcIncludes
23
+ calcDefines # not for files with changed tcs
24
+ calcFlags # not for files with changed tcs
25
+ end
26
+
27
+ def get_object_file(source)
28
+
29
+ # until now all OBJECT_FILE_ENDING are equal in all three types
30
+ adaptedSource = source.chomp(File.extname(source)).gsub(/\.\./, "##") + (Bake.options.prepro ? ".i" : @tcs[:COMPILER][:CPP][:OBJECT_FILE_ENDING])
31
+ return adaptedSource if File.is_absolute?source
32
+ File.join([@output_dir, adaptedSource])
33
+ end
34
+
35
+ def ignore?(type)
36
+ Bake.options.linkOnly or (Bake.options.prepro and type == :ASM)
37
+ end
38
+
39
+ def needed?(source, object, type, dep_filename_conv)
40
+ return "because analyzer toolchain is configured" if Bake.options.analyze
41
+ return "because prepro was specified and source is no assembler file" if Bake.options.prepro
42
+
43
+ return "because object does not exist" if not File.exist?(object)
44
+ oTime = File.mtime(object)
45
+
46
+ return "because source is newer than object" if oTime < File.mtime(source)
47
+
48
+ if type != :ASM
49
+ return "because dependency file does not exist" if not File.exist?(dep_filename_conv)
50
+
51
+ begin
52
+ File.readlines(dep_filename_conv).map{|line| line.strip}.each do |dep|
53
+ if not File.exist?(dep)
54
+ # we need a hack here. with some windows configurations the compiler prints unix paths
55
+ # into the dep file which cannot be found easily. this will be true for system includes,
56
+ # e.g. /usr/lib/...xy.h
57
+ if (Bake::Utils::OS.windows? and dep.start_with?"/") or
58
+ (not Bake::Utils::OS.windows? and dep.length > 1 and dep[1] == ":")
59
+ puts "Dependency header file #{dep} ignored!" if Bake.options.debug
60
+ else
61
+ return "because dependent header #{dep} does not exist"
62
+ end
63
+ else
64
+ return "because dependent header #{dep} is newer than object" if oTime < File.mtime(dep)
65
+ end
66
+ end
67
+ rescue Exception => ex
68
+ if Bake.options.debug
69
+ puts "While reading #{dep_filename_conv}:"
70
+ puts ex.message
71
+ puts ex.backtrace
72
+ end
73
+ return "because dependency file could not be loaded"
74
+ end
75
+ end
76
+
77
+ false
78
+ end
79
+
80
+ def calcCmdlineFile(object)
81
+ object[0..-3] + ".cmdline"
82
+ end
83
+
84
+ def calcDepFile(object, type)
85
+ dep_filename = nil
86
+ if type != :ASM
87
+ dep_filename = object[0..-3] + ".d"
88
+ end
89
+ dep_filename
90
+ end
91
+
92
+ def calcDepFileConv(dep_filename)
93
+ dep_filename + ".bake"
94
+ end
95
+
96
+ def get_source_type(source)
97
+ ex = File.extname(source)
98
+ [:CPP, :C, :ASM].each do |t|
99
+ return t if @tcs[:COMPILER][t][:SOURCE_FILE_ENDINGS].include?(ex)
100
+ end
101
+ nil
102
+ end
103
+
104
+ def compileFile(source)
105
+ type = get_source_type(source)
106
+ return if type.nil?
107
+
108
+ object = @object_files[source]
109
+
110
+ dep_filename = calcDepFile(object, type)
111
+ dep_filename_conv = calcDepFileConv(dep_filename) if type != :ASM
112
+
113
+ cmdLineCheck = false
114
+ cmdLineFile = calcCmdlineFile(object)
115
+
116
+ return if ignore?(type)
117
+ reason = needed?(source, object, type, dep_filename_conv)
118
+ if not reason
119
+ cmdLineCheck = true
120
+ reason = config_changed?(cmdLineFile)
121
+ end
122
+
123
+ if @fileTcs.include?(source)
124
+ compiler = @fileTcs[source][:COMPILER][type]
125
+ defines = getDefines(compiler)
126
+ flags = getFlags(compiler)
127
+ else
128
+ compiler = @tcs[:COMPILER][type]
129
+ defines = @define_array[type]
130
+ flags = @flag_array[type]
131
+ end
132
+ includes = @include_array[type]
133
+
134
+ if Bake.options.prepro and compiler[:PREPRO_FLAGS] == ""
135
+ Bake.formatter.printError("Error: No preprocessor option available for " + source)
136
+ raise SystemCommandFailed.new
137
+ end
138
+
139
+ cmd = Utils.flagSplit(compiler[:COMMAND], false)
140
+ cmd += compiler[:COMPILE_FLAGS].split(" ")
141
+
142
+ if dep_filename
143
+ cmd += @tcs[:COMPILER][type][:DEP_FLAGS].split(" ")
144
+ if @tcs[:COMPILER][type][:DEP_FLAGS_FILENAME]
145
+ if @tcs[:COMPILER][type][:DEP_FLAGS_SPACE]
146
+ cmd << dep_filename
147
+ else
148
+ if dep_filename.include?" "
149
+ cmd[cmd.length-1] << "\"" + dep_filename + "\""
150
+ else
151
+ cmd[cmd.length-1] << dep_filename
152
+ end
153
+
154
+ end
155
+ end
156
+ end
157
+
158
+ cmd += compiler[:PREPRO_FLAGS].split(" ") if Bake.options.prepro
159
+ cmd += flags
160
+ cmd += includes
161
+ cmd += defines
162
+
163
+ offlag = compiler[:OBJECT_FILE_FLAG]
164
+ offlag = compiler[:PREPRO_FILE_FLAG] if compiler[:PREPRO_FILE_FLAG] and Bake.options.prepro
165
+
166
+ if compiler[:OBJ_FLAG_SPACE]
167
+ cmd << offlag
168
+ cmd << object
169
+ else
170
+ if object.include?" "
171
+ cmd << offlag + "\"" + object + "\""
172
+ else
173
+ cmd << offlag + object
174
+ end
175
+ end
176
+ cmd << source
177
+
178
+ if Bake.options.cc2j_filename
179
+ cmdJson = cmd.is_a?(Array) ? cmd.join(' ') : cmd
180
+ Blocks::CC2J << { :directory => @projectDir, :command => cmdJson, :file => source }
181
+ end
182
+
183
+ if not (cmdLineCheck and BlockBase.isCmdLineEqual?(cmd, cmdLineFile))
184
+ BlockBase.prepareOutput(object)
185
+ BlockBase.writeCmdLineFile(cmd, cmdLineFile)
186
+ success, consoleOutput = ProcessHelper.run(cmd, false, false)
187
+
188
+ outputType = Bake.options.analyze ? "Analyzing" : (Bake.options.prepro ? "Preprocessing" : "Compiling")
189
+ incList = process_result(cmd, consoleOutput, compiler[:ERROR_PARSER], "#{outputType} #{source}", reason, success)
190
+
191
+ if type != :ASM and not Bake.options.analyze and not Bake.options.prepro
192
+ incList = Compile.read_depfile(dep_filename, @projectDir, @tcs[:COMPILER][:DEP_FILE_SINGLE_LINE]) if incList.nil?
193
+ Compile.write_depfile(incList, dep_filename_conv)
194
+ end
195
+ check_config_file
196
+ end
197
+
198
+ Bake::Bundle.instance.addSource(source, @include_list, dep_filename_conv) if isMainProject?
199
+
200
+ end
201
+
202
+ def self.read_depfile(dep_filename, projDir, singleLine)
203
+ deps = []
204
+ begin
205
+ if singleLine
206
+ File.readlines(dep_filename).each do |line|
207
+ splitted = line.split(": ")
208
+ deps << splitted[1].gsub(/[\\]/,'/') if splitted.length > 1
209
+ end
210
+ else
211
+ deps_string = File.read(dep_filename)
212
+ deps_string = deps_string.gsub(/\\\n/,'')
213
+ dep_splitted = deps_string.split(/([^\\]) /).each_slice(2).map(&:join)[2..-1]
214
+ deps = dep_splitted.map { |d| d.gsub(/[\\] /,' ').gsub(/[\\]/,'/').strip }.delete_if {|d| d == "" }
215
+ end
216
+ rescue Exception => ex1
217
+ Bake.formatter.printWarning("Could not read '#{dep_filename}'", projDir)
218
+ puts ex1.message if Bake.options.debug
219
+ return nil
220
+ end
221
+ deps
222
+ end
223
+
224
+ # todo: move to toolchain util file
225
+ def self.write_depfile(deps, dep_filename_conv)
226
+ if deps
227
+ begin
228
+ File.open(dep_filename_conv, 'wb') do |f|
229
+ deps.each do |dep|
230
+ f.puts(dep)
231
+ end
232
+ end
233
+ rescue Exception
234
+ Bake.formatter.printWarning("Could not write '#{dep_filename_conv}'", projDir)
235
+ return nil
236
+ end
237
+ end
238
+ end
239
+
240
+ def mutex
241
+ @mutex ||= Mutex.new
242
+ end
243
+
244
+ def execute
245
+ Dir.chdir(@projectDir) do
246
+
247
+ calcSources
248
+ calcObjects
249
+
250
+ @error_strings = {}
251
+
252
+ compileJobs = Multithread::Jobs.new(@source_files) do |jobs|
253
+ while source = jobs.get_next_or_nil do
254
+
255
+ if (jobs.failed and Bake.options.stopOnFirstError) or Bake::IDEInterface.instance.get_abort
256
+ break
257
+ end
258
+
259
+ s = StringIO.new
260
+ tmp = Thread.current[:stdout]
261
+ Thread.current[:stdout] = s unless tmp
262
+
263
+ result = false
264
+ begin
265
+ compileFile(source)
266
+ result = true
267
+ rescue Bake::SystemCommandFailed => scf # normal compilation error
268
+ rescue SystemExit => exSys
269
+ rescue Exception => ex1
270
+ if not Bake::IDEInterface.instance.get_abort
271
+ Bake.formatter.printError("Error: #{ex1.message}")
272
+ puts ex1.backtrace if Bake.options.debug
273
+ end
274
+ end
275
+
276
+ jobs.set_failed if not result
277
+
278
+ Thread.current[:stdout] = tmp
279
+
280
+ mutex.synchronize do
281
+ if s.string.length > 0
282
+ if Bake.options.stopOnFirstError and not result
283
+ @error_strings[source] = s.string
284
+ else
285
+ puts s.string
286
+ end
287
+ end
288
+ end
289
+
290
+ end
291
+ end
292
+ compileJobs.join
293
+
294
+ # can only happen in case of bail_on_first_error.
295
+ # if not sorted, it may be confusing when builing more than once and the order of the error appearances changes from build to build
296
+ # (it is not deterministic which file compilation finishes first)
297
+ @error_strings.sort.each {|es| puts es[1]}
298
+
299
+ raise SystemCommandFailed.new if compileJobs.failed
300
+
301
+
302
+ end
303
+ return true
304
+ end
305
+
306
+ def clean
307
+ if Bake.options.filename or Bake.options.analyze
308
+ Dir.chdir(@projectDir) do
309
+ calcSources(true)
310
+ @source_files.each do |source|
311
+
312
+ type = get_source_type(source)
313
+ next if type.nil?
314
+ object = get_object_file(source)
315
+ if File.exist?object
316
+ puts "Deleting file #{object}" if Bake.options.verbose >= 2
317
+ FileUtils.rm_rf(object)
318
+ end
319
+ if not Bake.options.analyze
320
+ dep_filename = calcDepFile(object, type)
321
+ if dep_filename and File.exist?dep_filename
322
+ puts "Deleting file #{dep_filename}" if Bake.options.verbose >= 2
323
+ FileUtils.rm_rf(dep_filename)
324
+ end
325
+ cmdLineFile = calcCmdlineFile(object)
326
+ if File.exist?cmdLineFile
327
+ puts "Deleting file #{cmdLineFile}" if Bake.options.verbose >= 2
328
+ FileUtils.rm_rf(cmdLineFile)
329
+ end
330
+ end
331
+ end
332
+ end
333
+ end
334
+ return true
335
+ end
336
+
337
+ def calcObjects
338
+ @source_files.each do |source|
339
+ type = get_source_type(source)
340
+ if not type.nil?
341
+ object = get_object_file(source)
342
+ if @objects.include?object
343
+ @object_files.each do |k,v|
344
+ if (v == object) # will be found exactly once
345
+ Bake.formatter.printError("Source files '#{k}' and '#{source}' would result in the same object file", source)
346
+ raise SystemCommandFailed.new
347
+ end
348
+ end
349
+ end
350
+ @object_files[source] = object
351
+ @objects << object
352
+ end
353
+ end
354
+ end
355
+
356
+ def calcSources(cleaning = false, keep = false)
357
+ return @source_files if @source_files and not @source_files.empty?
358
+ Dir.chdir(@projectDir) do
359
+ @source_files = []
360
+
361
+ exclude_files = Set.new
362
+ @config.excludeFiles.each do |p|
363
+ Dir.glob(p.name).each {|f| exclude_files << f}
364
+ end
365
+
366
+ source_files = Set.new
367
+ @config.files.each do |sources|
368
+ p = sources.name
369
+ res = Dir.glob(p).sort
370
+ if res.length == 0 and cleaning == false
371
+ if not p.include?"*" and not p.include?"?"
372
+ Bake.formatter.printError("Source file '#{p}' not found", sources)
373
+ raise SystemCommandFailed.new
374
+ elsif Bake.options.verbose >= 1
375
+ Bake.formatter.printInfo("Source file pattern '#{p}' does not match to any file", sources)
376
+ end
377
+ end
378
+ res.each do |f|
379
+ next if exclude_files.include?(f)
380
+ next if source_files.include?(f)
381
+ source_files << f
382
+ @source_files << f
383
+ end
384
+ end
385
+
386
+ if Bake.options.filename
387
+ @source_files.keep_if do |source|
388
+ source.include?Bake.options.filename
389
+ end
390
+ if @source_files.length == 0 and cleaning == false
391
+ Bake.formatter.printInfo("#{Bake.options.filename} does not match to any source", @config)
392
+ end
393
+ end
394
+
395
+ if Bake.options.eclipseOrder # directories reverse order, files in directories in alphabetical order
396
+ dirs = []
397
+ filemap = {}
398
+ @source_files.sort.reverse.each do |o|
399
+ d = File.dirname(o)
400
+ if filemap.include?(d)
401
+ filemap[d] << o
402
+ else
403
+ filemap[d] = [o]
404
+ dirs << d
405
+ end
406
+ end
407
+ @source_files = []
408
+ dirs.each do |d|
409
+ filemap[d].reverse.each do |f|
410
+ @source_files << f
411
+ end
412
+ end
413
+ end
414
+ end
415
+ @source_files
416
+ end
417
+
418
+ def mapInclude(inc, orgBlock)
419
+
420
+ if inc.name == "___ROOTS___"
421
+ return Bake.options.roots.map { |r| File.rel_from_to_project(@projectDir,r,false) }
422
+ end
423
+
424
+ i = orgBlock.convPath(inc,nil,true)
425
+ if orgBlock != @block
426
+ if not File.is_absolute?(i)
427
+ i = File.rel_from_to_project(@projectDir,orgBlock.config.parent.get_project_dir) + i
428
+ end
429
+ end
430
+
431
+ Pathname.new(i).cleanpath
432
+ end
433
+
434
+ def calcIncludes
435
+ @include_list = @config.includeDir.uniq.map do |dir|
436
+ mapInclude(dir, @block)
437
+ end
438
+
439
+ @block.getBlocks(:childs).each do |b|
440
+ b.config.includeDir.each do |inc|
441
+ if inc.inherit == true
442
+ @include_list << mapInclude(inc, b)
443
+ end
444
+ end if b.config.respond_to?("includeDir")
445
+ end
446
+
447
+ @block.getBlocks(:parents).each do |b|
448
+ if b.config.respond_to?("includeDir")
449
+ include_list_front = []
450
+ b.config.includeDir.each do |inc|
451
+ if inc.inject == "front"
452
+ include_list_front << mapInclude(inc, b)
453
+ elsif inc.inject == "back"
454
+ @include_list << mapInclude(inc, b)
455
+ elsif inc.infix == "front"
456
+ include_list_front << mapInclude(inc, b)
457
+ elsif inc.infix == "back"
458
+ @include_list << mapInclude(inc, b)
459
+ end
460
+ end
461
+ @include_list = include_list_front + @include_list
462
+ end
463
+ end
464
+
465
+ @include_list = @include_list.flatten.uniq
466
+
467
+ @include_array = {}
468
+ [:CPP, :C, :ASM].each do |type|
469
+ @include_array[type] = @include_list.map {|k| "#{@tcs[:COMPILER][type][:INCLUDE_PATH_FLAG]}#{k}"}
470
+ end
471
+ end
472
+
473
+ def getDefines(compiler)
474
+ compiler[:DEFINES].map {|k| "#{compiler[:DEFINE_FLAG]}#{k}"}
475
+ end
476
+
477
+ def getFlags(compiler)
478
+ Bake::Utils::flagSplit(compiler[:FLAGS],true)
479
+ end
480
+
481
+ def calcDefines
482
+ @define_array = {}
483
+ [:CPP, :C, :ASM].each do |type|
484
+ @define_array[type] = getDefines(@tcs[:COMPILER][type])
485
+ end
486
+ end
487
+ def calcFlags
488
+ @flag_array = {}
489
+ [:CPP, :C, :ASM].each do |type|
490
+ @flag_array[type] = getFlags(@tcs[:COMPILER][type])
491
+ end
492
+ end
493
+
494
+ def calcFileTcs
495
+ @fileTcs = {}
496
+ @config.files.each do |f|
497
+ if (f.define.length > 0 or f.flags.length > 0)
498
+ if f.name.include?"*"
499
+ Bake.formatter.printWarning("Toolchain settings not allowed for file pattern #{f.name}", f)
500
+ err_res = ErrorDesc.new
501
+ err_res.file_name = @config.file_name
502
+ err_res.line_number = f.line_number
503
+ err_res.severity = ErrorParser::SEVERITY_WARNING
504
+ err_res.message = "Toolchain settings not allowed for file patterns"
505
+ Bake::IDEInterface.instance.set_errors([err_res])
506
+ else
507
+ @fileTcs[f.name] = integrateCompilerFile(Utils.deep_copy(@tcs),f)
508
+ end
509
+ end
510
+ end
511
+ end
512
+
513
+ def tcs4source(source)
514
+ @fileTcs[source] || @tcs
515
+ end
516
+
517
+
518
+ end
519
+
520
+ end
531
521
  end