rake-delphi 0.0.4

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.
Files changed (70) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +29 -0
  5. data/Rakefile.rb +12 -0
  6. data/lib/rake/common/chdirtask.rb +21 -0
  7. data/lib/rake/common/classes.rb +15 -0
  8. data/lib/rake/common/dsl.rb +10 -0
  9. data/lib/rake/common/echotask.rb +34 -0
  10. data/lib/rake/common/exectask.rb +20 -0
  11. data/lib/rake/common/git.rb +102 -0
  12. data/lib/rake/common/hashtask.rb +43 -0
  13. data/lib/rake/common/initask.rb +31 -0
  14. data/lib/rake/common/libstask.rb +35 -0
  15. data/lib/rake/common/sendmailtask.rb +45 -0
  16. data/lib/rake/common/ziptask.rb +98 -0
  17. data/lib/rake/delphi/dcc32.rb +260 -0
  18. data/lib/rake/delphi/envvariables.rb +41 -0
  19. data/lib/rake/delphi/liblist.rb +29 -0
  20. data/lib/rake/delphi/project.rb +68 -0
  21. data/lib/rake/delphi/projectinfo.rb +76 -0
  22. data/lib/rake/delphi/rc.rb +25 -0
  23. data/lib/rake/delphi/resources.rb +126 -0
  24. data/lib/rake/delphi/tool.rb +140 -0
  25. data/lib/rake/delphi/version.rb +7 -0
  26. data/lib/rake/delphi.rb +4 -0
  27. data/lib/rake/helpers/digest.rb +28 -0
  28. data/lib/rake/helpers/file.rb +35 -0
  29. data/lib/rake/helpers/filelist.rb +12 -0
  30. data/lib/rake/helpers/gemversion.rb +42 -0
  31. data/lib/rake/helpers/logger.rb +15 -0
  32. data/lib/rake/helpers/rake.rb +29 -0
  33. data/lib/rake/helpers/raketask.rb +39 -0
  34. data/lib/rake/helpers/string.rb +10 -0
  35. data/lib/rake/helpers/unittest.rb +17 -0
  36. data/lib/rake/templates/project.erb +60 -0
  37. data/rake-delphi.gemspec +23 -0
  38. data/test/helpers/consts.rb +4 -0
  39. data/test/resources/FakeDelphi/dcc32.exe +0 -0
  40. data/test/resources/FakeDelphi/rc.exe +0 -0
  41. data/test/resources/echo/file.in +1 -0
  42. data/test/resources/echo/file.out +1 -0
  43. data/test/resources/hashes/hash.2.file +1 -0
  44. data/test/resources/hashes/hash.file +1 -0
  45. data/test/resources/ini/file.ini +7 -0
  46. data/test/resources/libstask/lib/level-1/level-2-1/level-3-1/.gitkeep +0 -0
  47. data/test/resources/libstask/lib/level-1/level-2-1/level-3-2/.gitkeep +0 -0
  48. data/test/resources/libstask/lib/level-1/level-2-2/.gitkeep +0 -0
  49. data/test/resources/testproject/.gitignore +8 -0
  50. data/test/resources/testproject/ExplicitLib/ExplicitLibUnit.pas +17 -0
  51. data/test/resources/testproject/Rakefile.rb +54 -0
  52. data/test/resources/testproject/lib/AnyLib/LibUnit.pas +17 -0
  53. data/test/resources/testproject/local.resources.txt +1 -0
  54. data/test/resources/testproject/release.dcc.cfg +1 -0
  55. data/test/resources/testproject/resources.rc +1 -0
  56. data/test/resources/testproject/testproject.bdsproj +175 -0
  57. data/test/resources/testproject/testproject.cfg +39 -0
  58. data/test/resources/testproject/testproject.dpr +78 -0
  59. data/test/resources/testproject/testproject.dproj +77 -0
  60. data/test/resources/testproject/testproject.ico +0 -0
  61. data/test/test-delphi.rb +166 -0
  62. data/test/test-echo.rb +33 -0
  63. data/test/test-gemversion.rb +61 -0
  64. data/test/test-git.rb +144 -0
  65. data/test/test-hashes.rb +57 -0
  66. data/test/test-ini.rb +18 -0
  67. data/test/test-libstask.rb +54 -0
  68. data/test/test-projectinfo.rb +66 -0
  69. data/test/test-zip.rb +43 -0
  70. metadata +179 -0
@@ -0,0 +1,260 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake'
4
+ require 'rake/common/chdirtask'
5
+ require 'rake/delphi/envvariables'
6
+ require 'rake/delphi/resources'
7
+ require 'rake/delphi/tool'
8
+ require 'rake/helpers/rake'
9
+ require 'rake/helpers/raketask'
10
+
11
+ module Rake
12
+ module Delphi
13
+ class Dcc32Tool < CustomDelphiTool
14
+ def self.toolName
15
+ 'bin/dcc32.exe'
16
+ end
17
+
18
+ def readLibraryPaths
19
+ libpaths = self.class.readUserOption('Library', 'Search Path', self.version).split(';') \
20
+ | self.class.readUserOption('Library', 'SearchPath', self.version).split(';')
21
+ dev = EnvVariables.new(self.class.rootForVersion(self.version) + '\Environment Variables', self.delphidir)
22
+ libpaths.map! do |lp|
23
+ if !lp.nil? && !lp.empty?
24
+ lp = dev.expand(lp)
25
+ end
26
+ lp
27
+ end
28
+ return libpaths
29
+ end
30
+ end
31
+
32
+ class Dcc32Task < Rake::Task
33
+ attr_accessor :systempath, :mainicon, :_source, :exeoutput, :bin
34
+
35
+ private
36
+ @@symbols = [:quiet, :assertions, :build, :optimization, :debug, :defines,
37
+ :debuginfo, :localsymbols, :console, :warnings, :hints, :altercfg,
38
+ :includepaths, :writeableconst,
39
+ :map, :dcuoutput, :bploutput,
40
+ :dcpoutput, :dcu, :uselibrarypath, :uselibrarypath, :usecfg]
41
+ public
42
+ @@symbols.map do |sym|
43
+ attr_accessor sym
44
+ end
45
+
46
+ def initialize(name, application)
47
+ super
48
+ initvars
49
+ @arg_names = [:verbose]
50
+ @rc_template_task = application.define_task(RCTemplateTask, shortname + ':rc:template')
51
+ @rc_task = application.define_task(RCTask, shortname + ':rc')
52
+ enhance([@rc_template_task, @rc_task])
53
+ @dcc32Tool = Dcc32Tool.new
54
+ end
55
+
56
+ def versionInfoClass
57
+ @dcc32Tool.versionInfoClass
58
+ end
59
+
60
+ private
61
+ def initvars
62
+ @exeoutput = nil
63
+ @@symbols.map do |sym|
64
+ instance_variable_set("@#{sym.to_s}", nil)
65
+ end
66
+ end
67
+
68
+ def delphilibs
69
+ return [@dcc32Tool.delphidir + 'Lib'] | @dcc32Tool.readLibraryPaths
70
+ end
71
+
72
+ def _paths(ppaths)
73
+ ppaths.map! do |p|
74
+ a = []
75
+ ['U', 'I', 'R', 'O'].each do |s|
76
+ a << Rake.quotepath("-#{s}", p)
77
+ end
78
+ a
79
+ end
80
+ # unique paths only
81
+ ppaths.flatten!.uniq!
82
+ ppaths
83
+ end
84
+
85
+ def implicitpaths
86
+ ipaths = ['.', '..']
87
+ ipaths |= delphilibs if @uselibrarypath
88
+ _paths(ipaths)
89
+ end
90
+
91
+ def paths
92
+ @includepaths ||= []
93
+ _paths(@includepaths)
94
+ end
95
+
96
+ def debug?
97
+ return @debug ? '-$D+ -$L+ -$YD -$C+ -$Q+ -$R+ -$O- -GD' : ''
98
+ end
99
+
100
+ def build?
101
+ return @build ? '-B' : '-M'
102
+ end
103
+
104
+ def warnings?
105
+ return @warnings ? '-W-' : '-W+'
106
+ end
107
+
108
+ def hints?
109
+ return @hints ? '-H-' : '-H+'
110
+ end
111
+
112
+ def quiet?
113
+ return @quiet ? '-Q' : ''
114
+ end
115
+
116
+ def exeoutput
117
+ return @exeoutput || @bin
118
+ end
119
+
120
+ def dcuoutput
121
+ return @dcuoutput || @dcu || @_source.pathmap('%d%sdcu')
122
+ end
123
+
124
+ def map
125
+ # segments -> -GS
126
+ # publics -> -GP
127
+ # detailed -> -GD
128
+ return unless @map
129
+ segments = @map.to_s[0..0].upcase
130
+ return '-G' + segments
131
+ end
132
+
133
+ def alldebuginfo
134
+ return @debuginfo ? '-$D+ -$L+ -$YD' : '-$D- -$L- -$Y-'
135
+ end
136
+
137
+ def outputs
138
+ os = []
139
+ os << Rake.quotepath('-E', exeoutput)
140
+ os << Rake.quotepath('-N', dcuoutput)
141
+ os << Rake.quotepath('-LE', @bploutput)
142
+ return os
143
+ end
144
+
145
+ def _source
146
+ return Rake.quotepath('', @_source)
147
+ end
148
+
149
+ def defines
150
+ '-D' + @defines if @defines
151
+ end
152
+
153
+ def writeableconst
154
+ return '-$J' + (@writeableconst ? '+' : '-')
155
+ end
156
+
157
+ def build_args
158
+ args = []
159
+ args << build? << warnings? << hints? << quiet? << debug? << alldebuginfo << map
160
+ args << defines << writeableconst
161
+ args << _source << outputs << implicitpaths
162
+ args.flatten
163
+ end
164
+
165
+ public
166
+ def init(properties)
167
+ pp properties if trace?
168
+ properties.map do |key, value|
169
+ instance_variable_set("@#{key}", value)
170
+ end
171
+ @_source = properties[:projectfile].pathmap('%X.dpr')
172
+ src = @_source.gsub('\\', '/')
173
+ dcu = src.pathmap('%d%sdcu')
174
+ # make sure to create dir for output dcu
175
+ # for now default is <PROJECTDIR>/dcu
176
+ directory dcu
177
+ enhance([dcu])
178
+ # mainicon is usually requested by RCTemplate
179
+ @mainicon ||= Rake.quotepath('', src.pathmap('%X.ico'))
180
+ @rc_template_task.output = src
181
+ @rc_template_task[:version] = properties[:version]
182
+ @rc_template_task[:releaseCandidate] = properties[:releaseCandidate]
183
+ @rc_task.input = src
184
+ @rc_task.is_rc = properties[:releaseCandidate]
185
+ return unless properties[:resources_additional]
186
+ res_add = properties[:resources_additional]
187
+ if res_add.kind_of?(String)
188
+ res_add = res_add.split(';')
189
+ end
190
+ c = 0
191
+ res_add.each do |res|
192
+ if res.kind_of?(Symbol)
193
+ rc_task_add = res
194
+ else
195
+ c = c.next
196
+ rc_task_add = application.define_task(RCTask, shortname + ':rc:add' + c.to_s)
197
+ rc_task_add.input = src.pathmap('%d%s') + res
198
+ end
199
+ enhance([rc_task_add])
200
+ end
201
+ end
202
+
203
+ def init_libs(libs = nil)
204
+ unless libs
205
+ # call parent to find libs
206
+ application[name.gsub(/:dcc32$/, '')].init_libs
207
+ else
208
+ # called from parent
209
+ # set libs
210
+ @includepaths = libs
211
+ end
212
+ end
213
+
214
+ def execute(opts=nil)
215
+ @dcc32Tool.class.checkToolFailure(@dcc32Tool.toolpath)
216
+ fail "Could not find #{_source} to compile" unless @_source && File.exists?(@_source)
217
+ init_libs
218
+ args = build_args
219
+ # on cygwin $D is assumed as shell var
220
+ # so escape $
221
+ args.map! { |a| a.gsub('$', '\$') if a.kind_of?(String) } unless application.windows?
222
+ args.compact!
223
+ cmd = Rake.quotepath('', @dcc32Tool.toolpath)
224
+ cmd << ([''] | args).join(' ')
225
+ ChDir.new(self, File.dirname(@_source)) do |dir|
226
+ RakeFileUtils.verbose(trace?) do
227
+ begin
228
+ unless @usecfg
229
+ cfg = @systempath.pathmap('%X.cfg')
230
+ bak_cfg = @systempath.pathmap('%X.rake.cfg')
231
+ mv cfg, bak_cfg
232
+ if @altercfg
233
+ cp @altercfg, cfg
234
+ end
235
+ # on Windows there is some limit on command line parameters length
236
+ # so we just append path parameters to config file
237
+ File.open(cfg, 'a+') do |f|
238
+ paths.each do |p|
239
+ f.write(p + "\n")
240
+ end
241
+ f.close
242
+ end
243
+ end
244
+ sh cmd
245
+ ensure
246
+ unless @usecfg
247
+ begin
248
+ cp cfg, cfg + '.1' if trace?
249
+ ensure
250
+ mv bak_cfg, cfg
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ puts '' # make one empty string to separate from further lines
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ module Rake
4
+ module Delphi
5
+ class EnvVariable
6
+ attr_reader :name
7
+ attr_accessor :value
8
+
9
+ def initialize(name, value)
10
+ @name, @value = name, value
11
+ end
12
+ end
13
+
14
+ class EnvVariables < ::Array
15
+ def initialize(regpath, delphidir)
16
+ _dir = delphidir.gsub(/\/$/, '')
17
+ add('DELPHI', _dir)
18
+ add('BDS', _dir)
19
+ expand_vars
20
+ end
21
+
22
+ def add(var, value)
23
+ self << EnvVariable.new(var, value)
24
+ end
25
+
26
+ def expand(value)
27
+ self.each do |ev|
28
+ value.gsub!("$(#{ev.name})", ev.value)
29
+ end
30
+ value
31
+ end
32
+
33
+ def expand_vars
34
+ self.map! do |v|
35
+ v.value = expand(v.value)
36
+ v
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake/helpers/filelist'
4
+
5
+ module Rake
6
+ module Delphi
7
+ class LibList < FileList
8
+ def read_ignored_libs
9
+ libs = []
10
+ file = (ENV['RAKE_DIR'] || Rake.original_dir) + '/.rake.ignored.libs'
11
+ return libs unless File.exists?(file)
12
+ IO.readlines(file).each do |line|
13
+ # skip comment lines (started with # or ;)
14
+ next if /^\s*[#;]/.match(line)
15
+ libs << FileList.get_ignored_dir_pattern(line.chomp)
16
+ end
17
+ libs
18
+ end
19
+
20
+ alias_method :initialize_base, :initialize
21
+
22
+ def initialize(*patterns)
23
+ initialize_base(patterns)
24
+ @exclude_patterns |= read_ignored_libs
25
+ @exclude_procs << proc { |fn| File.file?(fn) }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake'
4
+ require 'rake/common/classes'
5
+ require 'rake/common/libstask'
6
+ require 'rake/delphi/dcc32'
7
+ require 'rake/helpers/file'
8
+ require 'rake/helpers/raketask'
9
+ require 'rake/helpers/string'
10
+ require 'pp'
11
+
12
+ module Rake
13
+ module Delphi
14
+ class Project < Rake::Task
15
+ attr_accessor :properties
16
+
17
+ def initialize(name, app)
18
+ super
19
+ initvars
20
+ @dcc = application.define_task(Dcc32Task, shortname + ':dcc32')
21
+ @libs = LibsTask.define('all-delphi-libs', application)
22
+ @level = 1
23
+ enhance([@libs, @dcc])
24
+ end
25
+
26
+ def initvars
27
+ @properties = {
28
+ :build => true,
29
+ :warnings => false,
30
+ :hints => false,
31
+ :includepaths => nil
32
+ }
33
+ @cdir = ''
34
+ end
35
+
36
+ def init_libs
37
+ self[:includepaths] = ['.'] unless self[:includepaths]
38
+ self[:includepaths] |= @libs.libs_relative(@level)
39
+ @dcc.init_libs(self[:includepaths])
40
+ end
41
+
42
+ def init(module_name, rake_file, vars, level = 1)
43
+ @level = level
44
+ module_name = module_name.dup.pop.to_s
45
+ self[:projectlabel] = eval("#{module_name}::PROJECT_NAME")
46
+ projectfile = eval("#{module_name}::PROJECT_FILE")
47
+ self[:projectfile] = File.dirname2(rake_file) + File.separator + projectfile
48
+ self[:sourcename] = File.dirname2(rake_file)
49
+ @cdir = File.dirname(rake_file)
50
+ self[:systempath] = @cdir + '/' + projectfile
51
+ self[:altercfg].prepend(self[:sourcename] + '/') if self[:altercfg]
52
+ [:version, :bin, :build, :dcu, :alldebuginfo, :map, :defines, :releaseCandidate].each do |k|
53
+ self[k] = vars[k] if vars.has_key?(k)
54
+ end if vars
55
+
56
+ @dcc.init(@properties)
57
+ end
58
+
59
+ def []=(key, value)
60
+ @properties[key.to_sym] = value
61
+ end
62
+
63
+ def [](key)
64
+ return @properties[key]
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ require 'xmlsimple'
4
+ require 'rake/helpers/rake'
5
+
6
+ module Rake
7
+ module Delphi
8
+ class ProjectVersionInfo
9
+ def initialize(task)
10
+ @info = Hash.new
11
+ @file = task.systempath.pathmap('%X.' + self._ext)
12
+ do_getcontent
13
+ end
14
+
15
+ def do_getcontent
16
+ @content = nil
17
+ end
18
+
19
+ def _ext
20
+ ''
21
+ end
22
+
23
+ def [](key)
24
+ @info[key.to_sym]
25
+ end
26
+
27
+ def method_missing(name, *args, &block)
28
+ @info[name]
29
+ end
30
+ end
31
+
32
+ class BDSVersionInfo < ProjectVersionInfo
33
+ def initialize(task)
34
+ super(task)
35
+ content = @content['Delphi.Personality']['VersionInfoKeys']['VersionInfoKeys']
36
+ use_encode = String.new.respond_to?(:encode)
37
+ encoding = self.class.encoding
38
+ if encoding && ! use_encode
39
+ require 'iconv'
40
+ iconv = Iconv.new(encoding, 'UTF-8')
41
+ end
42
+ content.each do |v|
43
+ cv = v['content']
44
+ cv = (use_encode ? cv.encode(encoding, 'UTF-8') : iconv.iconv(cv)) if encoding
45
+ @info[v['Name'].to_sym] = cv
46
+ end
47
+ end
48
+
49
+ def self.encoding
50
+ # override to set your own encoding
51
+ nil
52
+ end
53
+
54
+ def do_getcontent
55
+ @content = XmlSimple.xml_in(@file, :ForceArray => false)
56
+ end
57
+
58
+ def _ext
59
+ return 'bdsproj'
60
+ end
61
+ end
62
+
63
+ class RAD2007VersionInfo < BDSVersionInfo
64
+ def _ext
65
+ return 'dproj'
66
+ end
67
+
68
+ def do_getcontent
69
+ super
70
+ # .dproj file has more nesting levels
71
+ @content = @content['ProjectExtensions']['BorlandProject']['BorlandProject']
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake/delphi/tool'
4
+
5
+ module Rake
6
+ module Delphi
7
+ class RCResourceCompiler < CustomDelphiTool
8
+ def self.toolName
9
+ 'bin/rc.exe'
10
+ end
11
+ end
12
+
13
+ class BorlandResourceCompiler < RCResourceCompiler
14
+ def self.toolName
15
+ 'bin/brcc32.exe'
16
+ end
17
+ end
18
+
19
+ class GOResourceCompiler < RCResourceCompiler
20
+ def self.toolName
21
+ 'bin/gorc.exe'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,126 @@
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'rake/common/classes'
5
+ require 'rake/delphi/rc'
6
+ require 'rake/helpers/gemversion'
7
+ require 'rake/helpers/rake'
8
+
9
+ module Rake
10
+ module Delphi
11
+ class RCTemplate
12
+ attr_reader :language, :sublanguage, :lang, :codepage, :filetype
13
+ alias :method_missing_base :method_missing
14
+
15
+ def initialize(owner)
16
+ @owner = owner
17
+ # English
18
+ @language = '0x19'
19
+ @sublanguage = '0x01'
20
+ # Russian
21
+ @lang = '0419'
22
+ # Russian
23
+ @codepage = '04E3'
24
+ # exe
25
+ @filetype = '0x1'
26
+ @versioninfo = nil
27
+ @extra = {}
28
+ @main_owner_task_name = @owner.name.gsub(/:rc:template$/, '')
29
+ @main_owner_task = nil
30
+ end
31
+
32
+ def get_binding
33
+ binding
34
+ end
35
+
36
+ def main_owner_task
37
+ @main_owner_task ||= @owner.application[@main_owner_task_name]
38
+ end
39
+
40
+ def mainicon
41
+ # take dcc33 task
42
+ icon = main_owner_task.mainicon
43
+ end
44
+
45
+ def version
46
+ Gem::VersionImproved.new(@extra[:version])
47
+ end
48
+
49
+ def versioninfo
50
+ @versioninfo ||= main_owner_task.versionInfoClass.new(main_owner_task)
51
+ end
52
+
53
+ def product
54
+ Gem::VersionImproved.new(self.versioninfo['ProductVersion'])
55
+ end
56
+
57
+ def []=(key, value)
58
+ @extra[key] = value || ''
59
+ end
60
+
61
+ def method_missing(name, *args, &block)
62
+ if args.empty? && @extra[name]
63
+ @extra[name]
64
+ elsif
65
+ @source.name
66
+ end
67
+ end
68
+ end
69
+
70
+ class RCTemplateTask < Rake::Task
71
+ def initialize(name, app)
72
+ super
73
+ @output = nil
74
+ @template_file = File.expand_path('../../templates/project.erb', __FILE__)
75
+ @template_obj = RCTemplate.new(self)
76
+ end
77
+
78
+ def output=(value)
79
+ @output = value.pathmap('%X.rc')
80
+ end
81
+
82
+ def []=(key, value)
83
+ @template_obj[key] = value
84
+ end
85
+
86
+ def execute(args=nil)
87
+ erb = ERB.new(IO.read(@template_file))
88
+ text = erb.result(@template_obj.get_binding)
89
+ File.open(@output, 'w') do |f|
90
+ f.write(text)
91
+ end
92
+ end
93
+ end
94
+
95
+ class RCTask < Rake::Task
96
+ attr_accessor :output, :input
97
+ def initialize(name, app)
98
+ super
99
+ @output = nil
100
+ @is_rc = false
101
+ end
102
+
103
+ def input=(value)
104
+ @input = value.pathmap('%X.rc')
105
+ @output = @input.pathmap('%X.res')
106
+ end
107
+
108
+ def is_rc=(value)
109
+ @is_rc = ! value.to_s.empty?
110
+ end
111
+
112
+ def execute(args=nil)
113
+ v, path, tool = RCResourceCompiler.find(true)
114
+ a = []
115
+ a << '/dRC' if @is_rc
116
+ a |= ['/fo', Rake.quotepath('', output), '/r', Rake.quotepath('', input) ]
117
+ opts = { :args => a }
118
+ opts.merge!(args)
119
+ cmd = ([Rake.quotepath('', tool)] | opts[:args]).join(' ')
120
+ RakeFileUtils.verbose(trace?) do
121
+ sh cmd
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end