rake-builder 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -0
- data/lib/compiler.rb +84 -0
- data/lib/rake/builder/qt_builder.rb +103 -25
- data/lib/rake/builder.rb +75 -34
- metadata +7 -5
data/Rakefile
CHANGED
data/lib/compiler.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
module Compiler
|
3
|
+
|
4
|
+
class Base
|
5
|
+
EXTRA_PATHS = [ '/opt/local/include' ]
|
6
|
+
|
7
|
+
def self.for( compiler )
|
8
|
+
COMPILERS[ compiler ].new
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@paths = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def include_paths( headers )
|
16
|
+
paths = []
|
17
|
+
headers.each do | header |
|
18
|
+
path = find_header( header )
|
19
|
+
raise "Can't find header '#{ header }' in any known include path" if path.nil?
|
20
|
+
paths << path
|
21
|
+
end
|
22
|
+
paths.uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def find_header( header )
|
28
|
+
EXTRA_PATHS.each do | path |
|
29
|
+
if File.exist?( "#{ path }/#{ header }" )
|
30
|
+
return path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class GCC < Base
|
39
|
+
|
40
|
+
def self.framework_path( framework, qt_major )
|
41
|
+
"/Library/Frameworks/#{ framework }.framework/Versions/#{ qt_major }/Headers"
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_include_paths( language )
|
45
|
+
return @paths[ language ] if @paths[ language ]
|
46
|
+
|
47
|
+
paths = []
|
48
|
+
# Below is the recommended(!) way of getting standard serach paths from GCC
|
49
|
+
output = `echo | gcc -v -x #{ language } -E - 2>&1 1>/dev/null`
|
50
|
+
collecting = false
|
51
|
+
output.each_line do | line |
|
52
|
+
case
|
53
|
+
when line =~ /#include <\.\.\.> search starts here:/
|
54
|
+
collecting = true
|
55
|
+
when line =~ /End of search list\./
|
56
|
+
collecting = false
|
57
|
+
when line =~ / \(framework directory\)/
|
58
|
+
# Skip frameworks
|
59
|
+
else
|
60
|
+
paths << line.strip if collecting
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
@paths[ language ] = paths
|
65
|
+
end
|
66
|
+
|
67
|
+
def missing_headers( include_paths, source_files )
|
68
|
+
include_path = include_paths.map { | path | "-I#{ path }" }.join( ' ' )
|
69
|
+
command = "makedepend -f- -- #{ include_path } -- #{ source_files.join( ' ' ) } 2>&1 1>/dev/null"
|
70
|
+
output = `#{ command }`
|
71
|
+
missing = []
|
72
|
+
output.each_line do | line |
|
73
|
+
match = line.match( /cannot find include file "([^"]*)"/m )
|
74
|
+
missing << match[ 1 ] if match
|
75
|
+
end
|
76
|
+
|
77
|
+
missing
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
COMPILERS = { :gcc => GCC }
|
83
|
+
|
84
|
+
end
|
@@ -8,10 +8,10 @@ module Rake
|
|
8
8
|
# generate Info.plist
|
9
9
|
# package task
|
10
10
|
|
11
|
+
attr_accessor :qt_version
|
11
12
|
attr_accessor :frameworks
|
12
13
|
attr_accessor :resource_files
|
13
|
-
attr_accessor :
|
14
|
-
attr_accessor :architecture
|
14
|
+
attr_accessor :ui_files
|
15
15
|
|
16
16
|
def initialize( &block )
|
17
17
|
super( &block )
|
@@ -25,11 +25,20 @@ module Rake
|
|
25
25
|
super
|
26
26
|
@programming_language = 'c++'
|
27
27
|
@header_file_extension = 'h'
|
28
|
-
@frameworks = [
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
@frameworks = []
|
29
|
+
case
|
30
|
+
when RUBY_PLATFORM =~ /linux$/
|
31
|
+
@include_paths << '/usr/include/qt4'
|
32
|
+
@moc_defines = [ '-D__GNUC__' ]
|
33
|
+
when RUBY_PLATFORM =~ /darwin/i
|
34
|
+
@framework_paths = [ '/Library/Frameworks' ]
|
35
|
+
@moc_defines = [ '-D__APPLE__', '-D__GNUC__' ]
|
36
|
+
else
|
37
|
+
raise "Unrecognised platform"
|
38
|
+
end
|
39
|
+
@compilation_defines = [ '-DQT_GUI_LIB', '-DQT_CORE_LIB', '-DQT_SHARED' ]
|
32
40
|
@resource_files = []
|
41
|
+
@ui_files = []
|
33
42
|
end
|
34
43
|
|
35
44
|
def configure
|
@@ -39,37 +48,83 @@ module Rake
|
|
39
48
|
super
|
40
49
|
|
41
50
|
@resource_files = Rake::Path.expand_all_with_root( @resource_files, @rakefile_path )
|
51
|
+
@ui_files = Rake::Path.expand_all_with_root( @ui_files, @rakefile_path )
|
42
52
|
@compilation_options += [ '-pipe', '-g', '-gdwarf-2', '-Wall', '-W' ]
|
43
|
-
@
|
44
|
-
@architecture ||= 'i386'
|
45
|
-
@compilation_options += [ architecture_option ]
|
46
|
-
|
47
|
-
@frameworks.each do | framework |
|
48
|
-
@include_paths << "/Library/Frameworks/#{ framework }.framework/Versions/#{ qt_major }/Headers"
|
49
|
-
@include_paths << "/usr/include/#{ framework }"
|
50
|
-
end
|
53
|
+
@include_paths << @objects_path # for UI headers
|
51
54
|
end
|
52
55
|
|
53
56
|
def define
|
54
57
|
super
|
58
|
+
define_ui_tasks
|
55
59
|
define_moc_tasks
|
56
60
|
define_resource_tasks
|
57
61
|
end
|
58
62
|
|
59
63
|
def generated_files
|
60
|
-
super + moc_files + qrc_files
|
64
|
+
super + moc_files + ui_headers + qrc_files
|
65
|
+
end
|
66
|
+
|
67
|
+
def generated_headers
|
68
|
+
super + ui_headers
|
61
69
|
end
|
62
70
|
|
63
71
|
def source_files
|
64
72
|
( super + moc_files + qrc_files ).uniq
|
65
73
|
end
|
66
74
|
|
75
|
+
def header_files
|
76
|
+
( super + ui_headers ).uniq
|
77
|
+
end
|
78
|
+
|
67
79
|
def compiler_flags
|
68
|
-
|
80
|
+
flags = compilation_options + @compilation_defines + [ include_path ]
|
81
|
+
if RUBY_PLATFORM =~ /darwin/i
|
82
|
+
flags += framework_paths
|
83
|
+
flags << architecture_option
|
84
|
+
end
|
85
|
+
flags.join( ' ' )
|
69
86
|
end
|
70
87
|
|
71
88
|
def link_flags
|
72
|
-
|
89
|
+
flags = [ @linker_options, library_paths_list, library_dependencies_list ]
|
90
|
+
if RUBY_PLATFORM =~ /darwin/i
|
91
|
+
flags += [ '-headerpad_max_install_names', architecture_option ]
|
92
|
+
flags += framework_paths + framework_options
|
93
|
+
end
|
94
|
+
flags.join( ' ' )
|
95
|
+
end
|
96
|
+
|
97
|
+
# Exclude paths like QtFoo/Bar, but grab frameworks
|
98
|
+
def missing_headers
|
99
|
+
super
|
100
|
+
@missing_headers.reject! do | path |
|
101
|
+
m = path.match( /^(Qt\w+)\/(\w+?(?:\.h)?)$/ )
|
102
|
+
if m
|
103
|
+
framework = m[ 1 ]
|
104
|
+
@frameworks << framework
|
105
|
+
framework_path = Compiler::GCC.framework_path( framework, qt_major )
|
106
|
+
header_path = "#{ framework_path }/#{ m[ 2 ] }"
|
107
|
+
File.exist?( header_path )
|
108
|
+
else
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
@frameworks.each do | framework |
|
114
|
+
@include_paths << Compiler::GCC.framework_path( framework, qt_major )
|
115
|
+
@include_paths << "/usr/include/#{ framework }"
|
116
|
+
end
|
117
|
+
|
118
|
+
# Last chance: exclude headers of the form 'Aaaaaa' if found under frameworks
|
119
|
+
@missing_headers.reject! do | header |
|
120
|
+
@frameworks.any? do | framework |
|
121
|
+
framework_path = Compiler::GCC.framework_path( framework, qt_major )
|
122
|
+
header_path = "#{ framework_path }/#{ header }"
|
123
|
+
File.exist?( header_path )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
@missing_headers
|
73
128
|
end
|
74
129
|
|
75
130
|
# QT-specific
|
@@ -78,18 +133,38 @@ module Rake
|
|
78
133
|
@qt_version.match( /^(\d+)/ )[ 1 ]
|
79
134
|
end
|
80
135
|
|
81
|
-
def
|
82
|
-
"-
|
136
|
+
def framework_paths
|
137
|
+
@framework_paths.map { |p| "-F#{ p }" }
|
138
|
+
end
|
139
|
+
|
140
|
+
def framework_options
|
141
|
+
@frameworks.map { |p| "-framework #{ p }" }
|
83
142
|
end
|
84
143
|
|
85
|
-
|
86
|
-
|
144
|
+
# UI
|
145
|
+
# /Developer/Tools/Qt/uic ../scanner_cpp/mainwindow.ui -o ui_mainwindow.h
|
146
|
+
|
147
|
+
def define_ui_tasks
|
148
|
+
@ui_files.each do | ui_file |
|
149
|
+
ui_header = ui_header_path( ui_file )
|
150
|
+
file ui_header => [ @objects_path, ui_file ] do |t|
|
151
|
+
command = "uic #{ ui_file } -o #{ ui_header }"
|
152
|
+
shell command
|
153
|
+
end
|
154
|
+
end
|
87
155
|
end
|
88
156
|
|
89
|
-
def
|
90
|
-
@
|
157
|
+
def ui_headers
|
158
|
+
@ui_files.collect do | ui_file |
|
159
|
+
ui_header_path( ui_file )
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def ui_header_path( ui_file )
|
164
|
+
header_name = 'ui_' + File.basename( ui_file ).gsub( '.ui', '.h' )
|
165
|
+
Rake::Path.expand_with_root( header_name, @objects_path )
|
91
166
|
end
|
92
|
-
|
167
|
+
|
93
168
|
# MOC
|
94
169
|
|
95
170
|
def define_moc_tasks
|
@@ -98,7 +173,10 @@ module Rake
|
|
98
173
|
moc = moc_pathname( header_file )
|
99
174
|
|
100
175
|
file moc => [ header_file ] do |t|
|
101
|
-
|
176
|
+
options = @compilation_defines
|
177
|
+
options += framework_paths if RUBY_PLATFORM =~ /darwin/i
|
178
|
+
options += @moc_defines
|
179
|
+
command = "moc #{ options.join( ' ' ) } #{ header_file } -o #{ moc }"
|
102
180
|
shell command
|
103
181
|
end
|
104
182
|
|
data/lib/rake/builder.rb
CHANGED
@@ -5,6 +5,7 @@ require 'rake'
|
|
5
5
|
require 'rake/tasklib'
|
6
6
|
require 'rake/path'
|
7
7
|
require 'rake/file_task_alias'
|
8
|
+
require 'compiler'
|
8
9
|
|
9
10
|
module Rake
|
10
11
|
|
@@ -23,8 +24,8 @@ module Rake
|
|
23
24
|
module VERSION #:nodoc:
|
24
25
|
MAJOR = 0
|
25
26
|
MINOR = 0
|
26
|
-
TINY =
|
27
|
-
|
27
|
+
TINY = 13
|
28
|
+
|
28
29
|
STRING = [ MAJOR, MINOR, TINY ].join('.')
|
29
30
|
end
|
30
31
|
|
@@ -39,7 +40,10 @@ module Rake
|
|
39
40
|
# The types of file that can be built
|
40
41
|
TARGET_TYPES = [ :executable, :static_library, :shared_library ]
|
41
42
|
|
42
|
-
#
|
43
|
+
# processor type: 'i386', 'x86_64', 'ppc' or 'ppc64'.
|
44
|
+
attr_accessor :architecture
|
45
|
+
|
46
|
+
# The programming language: 'c++', 'c' or 'objective-c' (default 'c++')
|
43
47
|
# This also sets defaults for source_file_extension
|
44
48
|
attr_accessor :programming_language
|
45
49
|
|
@@ -165,6 +169,8 @@ module Rake
|
|
165
169
|
end
|
166
170
|
|
167
171
|
def initialize_attributes
|
172
|
+
@architecture = 'i386'
|
173
|
+
@compiler_data = Compiler::Base.for( :gcc )
|
168
174
|
@logger = Logger.new( STDOUT )
|
169
175
|
@logger.level = Logger::UNKNOWN
|
170
176
|
@logger.formatter = Formatter.new
|
@@ -179,9 +185,13 @@ module Rake
|
|
179
185
|
@target = 'a.out'
|
180
186
|
@generated_files = []
|
181
187
|
@compilation_options = []
|
188
|
+
@include_paths = []
|
182
189
|
end
|
183
190
|
|
184
191
|
def configure
|
192
|
+
@compilation_options += [ architecture_option ] if RUBY_PLATFORM =~ /apple/i
|
193
|
+
@compilation_options.uniq!
|
194
|
+
|
185
195
|
@programming_language.downcase!
|
186
196
|
raise "Don't know how to build '#{ @programming_language }' programs" if KNOWN_LANGUAGES[ @programming_language ].nil?
|
187
197
|
@compiler ||= KNOWN_LANGUAGES[ @programming_language ][ :compiler ]
|
@@ -201,7 +211,8 @@ module Rake
|
|
201
211
|
@install_path ||= default_install_path( @target_type )
|
202
212
|
|
203
213
|
@linker_options ||= ''
|
204
|
-
@include_paths
|
214
|
+
@include_paths += []
|
215
|
+
@include_paths = Rake::Path.expand_all_with_root( @include_paths.uniq, @rakefile_path )
|
205
216
|
@generated_files = Rake::Path.expand_all_with_root( @generated_files, @rakefile_path )
|
206
217
|
|
207
218
|
@default_task ||= :build
|
@@ -209,32 +220,9 @@ module Rake
|
|
209
220
|
|
210
221
|
@makedepend_file = @objects_path + '/.' + target_basename + '.depend.mf'
|
211
222
|
|
212
|
-
load_local_config
|
213
|
-
|
214
|
-
@include_paths = Rake::Path.expand_all_with_root( @include_paths, @rakefile_path )
|
215
|
-
|
216
223
|
raise "No source files found" if source_files.length == 0
|
217
224
|
end
|
218
225
|
|
219
|
-
def local_config
|
220
|
-
filename = '.rake-builder'
|
221
|
-
filename += '.' + @task_namespace.to_s if @task_namespace
|
222
|
-
Rake::Path.expand_with_root( filename, @rakefile_path )
|
223
|
-
end
|
224
|
-
|
225
|
-
def load_local_config
|
226
|
-
return if ! File.exist?( local_config )
|
227
|
-
|
228
|
-
config = YAML.load_file( local_config )
|
229
|
-
|
230
|
-
version = config[ :rake_builder ][ :config_file ][ :version ]
|
231
|
-
raise "Config file version missing" if version.nil?
|
232
|
-
|
233
|
-
@include_paths += config[ :include_paths ] if config[ :include_paths ]
|
234
|
-
rescue => e
|
235
|
-
raise "#{__FILE__}:#{__LINE__}: Failed to load local config file '#{ local_config }': #{ e.message }"
|
236
|
-
end
|
237
|
-
|
238
226
|
def define_tasks
|
239
227
|
if @task_namespace
|
240
228
|
namespace @task_namespace do
|
@@ -287,12 +275,38 @@ module Rake
|
|
287
275
|
|
288
276
|
directory @objects_path
|
289
277
|
|
290
|
-
file
|
278
|
+
file local_config => scoped_task( :missing_headers ) do
|
279
|
+
added_includes = @compiler_data.include_paths( missing_headers )
|
280
|
+
config = { :rake_builder => { :config_file => { :version=> '1.0' } },
|
281
|
+
:include_paths => added_includes }
|
282
|
+
File.open( local_config, 'w' ) do | file |
|
283
|
+
file.write config.to_yaml
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
file @makedepend_file => [ scoped_task( :load_local_config ),
|
288
|
+
scoped_task( :missing_headers ),
|
289
|
+
@objects_path,
|
290
|
+
*project_files ] do
|
291
291
|
@logger.add( Logger::DEBUG, "Analysing dependencies" )
|
292
292
|
command = "makedepend -f- -- #{ include_path } -- #{ file_list( source_files ) } 2>/dev/null > #{ @makedepend_file }"
|
293
293
|
shell command
|
294
294
|
end
|
295
295
|
|
296
|
+
task :load_local_config => local_config do
|
297
|
+
config = YAML.load_file( local_config )
|
298
|
+
|
299
|
+
version = config[ :rake_builder ][ :config_file ][ :version ]
|
300
|
+
raise "Config file version missing" if version.nil?
|
301
|
+
|
302
|
+
config[ :include_paths ] ||= []
|
303
|
+
@include_paths += Rake::Path.expand_all_with_root( config[ :include_paths ], @rakefile_path )
|
304
|
+
end
|
305
|
+
|
306
|
+
task :missing_headers => [ *generated_headers ] do
|
307
|
+
missing_headers
|
308
|
+
end
|
309
|
+
|
296
310
|
# Reimplemented mkdepend file loading to make objects depend on
|
297
311
|
# sources with the correct paths:
|
298
312
|
# the standard rake mkdepend loader doesn't do what we want,
|
@@ -355,6 +369,10 @@ module Rake
|
|
355
369
|
end
|
356
370
|
end
|
357
371
|
|
372
|
+
def generated_headers
|
373
|
+
[]
|
374
|
+
end
|
375
|
+
|
358
376
|
def scoped_task( task )
|
359
377
|
if @task_namespace
|
360
378
|
"#{ task_namespace }:#{ task }"
|
@@ -368,7 +386,8 @@ module Rake
|
|
368
386
|
@generated_files << object
|
369
387
|
file object => [ source ] do |t|
|
370
388
|
@logger.add( Logger::INFO, "Compiling '#{ source }'" )
|
371
|
-
|
389
|
+
command = "#{ @compiler } -c #{ compiler_flags } -o #{ object } #{ source }"
|
390
|
+
shell command
|
372
391
|
end
|
373
392
|
end
|
374
393
|
|
@@ -395,6 +414,15 @@ module Rake
|
|
395
414
|
end
|
396
415
|
end
|
397
416
|
|
417
|
+
# Discovery
|
418
|
+
|
419
|
+
def missing_headers
|
420
|
+
return @missing_headers if @missing_headers
|
421
|
+
default_includes = @compiler_data.default_include_paths( @programming_language )
|
422
|
+
all_includes = default_includes + @include_paths
|
423
|
+
@missing_headers = @compiler_data.missing_headers( all_includes, source_files )
|
424
|
+
end
|
425
|
+
|
398
426
|
# Compiling and linking parameters
|
399
427
|
|
400
428
|
def include_path
|
@@ -402,21 +430,34 @@ module Rake
|
|
402
430
|
end
|
403
431
|
|
404
432
|
def compiler_flags
|
405
|
-
include_path + ' ' + compilation_options.join(
|
433
|
+
flags = include_path + ' ' + compilation_options.join( ' ' )
|
434
|
+
flags << ' ' << architecture_option if RUBY_PLATFORM =~ /darwin/i
|
435
|
+
flags
|
436
|
+
end
|
437
|
+
|
438
|
+
def architecture_option
|
439
|
+
"-arch #{ @architecture }"
|
406
440
|
end
|
407
441
|
|
408
442
|
def link_flags
|
409
|
-
[ @linker_options, library_paths_list, library_dependencies_list ]
|
443
|
+
flags = [ @linker_options, architecture_option, library_paths_list, library_dependencies_list ]
|
444
|
+
flags << architecture_option if RUBY_PLATFORM =~ /darwin/i
|
445
|
+
flags.join( " " )
|
410
446
|
end
|
411
447
|
|
412
448
|
# Paths
|
413
449
|
|
450
|
+
def local_config
|
451
|
+
filename = '.rake-builder'
|
452
|
+
Rake::Path.expand_with_root( filename, @rakefile_path )
|
453
|
+
end
|
454
|
+
|
414
455
|
def save_rakefile_info( block )
|
415
456
|
if RUBY_VERSION < '1.9'
|
416
457
|
# Hack the path from the block String representation
|
417
458
|
@rakefile = block.to_s.match( /@([^\:]+):/ )[ 1 ]
|
418
459
|
else
|
419
|
-
@rakefile = block.source_location
|
460
|
+
@rakefile = block.source_location[ 0 ]
|
420
461
|
end
|
421
462
|
@rakefile_path = File.expand_path( File.dirname( @rakefile ) )
|
422
463
|
end
|
@@ -455,8 +496,8 @@ module Rake
|
|
455
496
|
source_files + header_files
|
456
497
|
end
|
457
498
|
|
458
|
-
def file_list( files )
|
459
|
-
files.join(
|
499
|
+
def file_list( files, delimiter = ' ' )
|
500
|
+
files.join( delimiter )
|
460
501
|
end
|
461
502
|
|
462
503
|
def library_paths_list
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rake-builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 13
|
10
|
+
version: 0.0.13
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joe Yates
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-02-20 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -43,6 +43,7 @@ extensions: []
|
|
43
43
|
extra_rdoc_files:
|
44
44
|
- COPYING
|
45
45
|
- README.rdoc
|
46
|
+
- lib/compiler.rb
|
46
47
|
- lib/rake/builder/qt_builder.rb
|
47
48
|
- lib/rake/builder.rb
|
48
49
|
- lib/rake/file_task_alias.rb
|
@@ -59,6 +60,7 @@ files:
|
|
59
60
|
- COPYING
|
60
61
|
- Rakefile
|
61
62
|
- README.rdoc
|
63
|
+
- lib/compiler.rb
|
62
64
|
- lib/rake/builder/qt_builder.rb
|
63
65
|
- lib/rake/builder.rb
|
64
66
|
- lib/rake/file_task_alias.rb
|
@@ -131,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
133
|
version: "0"
|
132
134
|
requirements: []
|
133
135
|
|
134
|
-
rubyforge_project:
|
136
|
+
rubyforge_project: nowarning
|
135
137
|
rubygems_version: 1.3.7
|
136
138
|
signing_key:
|
137
139
|
specification_version: 3
|