rake 0.4.11 → 0.8.7

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 (101) hide show
  1. data/CHANGES +288 -1
  2. data/README +101 -114
  3. data/Rakefile +269 -54
  4. data/TODO +1 -0
  5. data/bin/rake +26 -3
  6. data/doc/command_line_usage.rdoc +102 -0
  7. data/doc/jamis.rb +133 -106
  8. data/doc/rake.1.gz +0 -0
  9. data/doc/rakefile.rdoc +304 -4
  10. data/doc/release_notes/rake-0.4.14.rdoc +23 -0
  11. data/doc/release_notes/rake-0.4.15.rdoc +35 -0
  12. data/doc/release_notes/rake-0.5.0.rdoc +53 -0
  13. data/doc/release_notes/rake-0.5.3.rdoc +78 -0
  14. data/doc/release_notes/rake-0.5.4.rdoc +46 -0
  15. data/doc/release_notes/rake-0.6.0.rdoc +141 -0
  16. data/doc/release_notes/rake-0.7.0.rdoc +119 -0
  17. data/doc/release_notes/rake-0.7.1.rdoc +59 -0
  18. data/doc/release_notes/rake-0.7.2.rdoc +121 -0
  19. data/doc/release_notes/rake-0.7.3.rdoc +47 -0
  20. data/doc/release_notes/rake-0.8.0.rdoc +114 -0
  21. data/doc/release_notes/rake-0.8.2.rdoc +165 -0
  22. data/doc/release_notes/rake-0.8.3.rdoc +112 -0
  23. data/doc/release_notes/rake-0.8.4.rdoc +147 -0
  24. data/doc/release_notes/rake-0.8.5.rdoc +53 -0
  25. data/doc/release_notes/rake-0.8.6.rdoc +55 -0
  26. data/doc/release_notes/rake-0.8.7.rdoc +55 -0
  27. data/lib/rake/alt_system.rb +108 -0
  28. data/lib/rake/classic_namespace.rb +8 -0
  29. data/lib/rake/clean.rb +3 -1
  30. data/lib/rake/contrib/ftptools.rb +40 -26
  31. data/lib/rake/contrib/publisher.rb +1 -1
  32. data/lib/rake/contrib/rubyforgepublisher.rb +3 -3
  33. data/lib/rake/contrib/sshpublisher.rb +1 -1
  34. data/lib/rake/contrib/sys.rb +25 -23
  35. data/lib/rake/gempackagetask.rb +14 -15
  36. data/lib/rake/loaders/makefile.rb +42 -0
  37. data/lib/rake/packagetask.rb +65 -33
  38. data/lib/rake/rake_test_loader.rb +5 -0
  39. data/lib/rake/rdoctask.rb +112 -31
  40. data/lib/rake/ruby182_test_unit_fix.rb +23 -0
  41. data/lib/rake/runtest.rb +4 -4
  42. data/lib/rake/tasklib.rb +11 -12
  43. data/lib/rake/testtask.rb +67 -24
  44. data/lib/rake/win32.rb +55 -0
  45. data/lib/rake.rb +2087 -578
  46. data/test/capture_stdout.rb +26 -0
  47. data/test/check_expansion.rb +5 -0
  48. data/test/check_no_expansion.rb +5 -0
  49. data/test/data/chains/Rakefile +15 -0
  50. data/test/data/default/Rakefile +19 -0
  51. data/test/data/dryrun/Rakefile +22 -0
  52. data/test/data/file_creation_task/Rakefile +33 -0
  53. data/test/data/imports/Rakefile +19 -0
  54. data/test/data/imports/deps.mf +1 -0
  55. data/test/data/multidesc/Rakefile +17 -0
  56. data/test/data/namespace/Rakefile +57 -0
  57. data/test/data/rakelib/test1.rb +3 -0
  58. data/test/data/sample.mf +14 -0
  59. data/test/data/statusreturn/Rakefile +8 -0
  60. data/test/data/unittest/Rakefile +1 -0
  61. data/test/filecreation.rb +18 -12
  62. data/test/functional.rb +6 -73
  63. data/test/in_environment.rb +30 -0
  64. data/test/rake_test_setup.rb +24 -0
  65. data/test/reqfile.rb +3 -0
  66. data/test/reqfile2.rb +3 -0
  67. data/test/session_functional.rb +339 -0
  68. data/test/shellcommand.rb +0 -0
  69. data/test/test_application.rb +675 -0
  70. data/test/{testclean.rb → test_clean.rb} +1 -0
  71. data/test/test_definitions.rb +85 -0
  72. data/test/test_earlytime.rb +35 -0
  73. data/test/test_extension.rb +63 -0
  74. data/test/test_file_creation_task.rb +62 -0
  75. data/test/test_file_task.rb +143 -0
  76. data/test/test_filelist.rb +623 -0
  77. data/test/test_fileutils.rb +251 -0
  78. data/test/{testftp.rb → test_ftp.rb} +5 -1
  79. data/test/test_invocation_chain.rb +81 -0
  80. data/test/test_makefile_loader.rb +26 -0
  81. data/test/test_multitask.rb +45 -0
  82. data/test/test_namespace.rb +55 -0
  83. data/test/{testpackagetask.rb → test_package_task.rb} +49 -12
  84. data/test/test_pathmap.rb +210 -0
  85. data/test/test_pseudo_status.rb +26 -0
  86. data/test/test_rake.rb +41 -0
  87. data/test/test_rdoc_task.rb +88 -0
  88. data/test/test_require.rb +35 -0
  89. data/test/test_rules.rb +349 -0
  90. data/test/test_task_arguments.rb +89 -0
  91. data/test/test_task_manager.rb +173 -0
  92. data/test/test_tasklib.rb +12 -0
  93. data/test/test_tasks.rb +374 -0
  94. data/test/{testtesttask.rb → test_test_task.rb} +8 -2
  95. data/test/test_top_level_functions.rb +86 -0
  96. data/test/test_win32.rb +72 -0
  97. metadata +184 -84
  98. data/test/testfilelist.rb +0 -255
  99. data/test/testfileutils.rb +0 -83
  100. data/test/testtasks.rb +0 -371
  101. /data/test/contrib/{testsys.rb → test_sys.rb} +0 -0
data/lib/rake/rdoctask.rb CHANGED
@@ -8,38 +8,53 @@ module Rake
8
8
  # Create a documentation task that will generate the RDoc files for
9
9
  # a project.
10
10
  #
11
- # The PackageTask will create the following targets:
11
+ # The RDocTask will create the following targets:
12
12
  #
13
13
  # [<b><em>rdoc</em></b>]
14
14
  # Main task for this RDOC task.
15
15
  #
16
16
  # [<b>:clobber_<em>rdoc</em></b>]
17
- # Delete all the package files. This target is automatically
17
+ # Delete all the rdoc files. This target is automatically
18
18
  # added to the main clobber target.
19
19
  #
20
20
  # [<b>:re<em>rdoc</em></b>]
21
- # Rebuild the package files from scratch, even if they are not out
21
+ # Rebuild the rdoc files from scratch, even if they are not out
22
22
  # of date.
23
23
  #
24
24
  # Simple Example:
25
25
  #
26
- # RDocTask.new do |rd|
26
+ # Rake::RDocTask.new do |rd|
27
27
  # rd.main = "README.rdoc"
28
28
  # rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
29
29
  # end
30
30
  #
31
+ # The +rd+ object passed to the block is an RDocTask object. See the
32
+ # attributes list for the RDocTask class for available customization options.
33
+ #
34
+ # == Specifying different task names
35
+ #
31
36
  # You may wish to give the task a different name, such as if you are
32
37
  # generating two sets of documentation. For instance, if you want to have a
33
38
  # development set of documentation including private methods:
34
39
  #
35
- # RDocTask.new(:rdoc_dev) do |rd|
40
+ # Rake::RDocTask.new(:rdoc_dev) do |rd|
36
41
  # rd.main = "README.doc"
37
42
  # rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
38
43
  # rd.options << "--all"
39
44
  # end
40
45
  #
41
46
  # The tasks would then be named :<em>rdoc_dev</em>, :clobber_<em>rdoc_dev</em>, and
42
- # :re<em>rdoc_dev</em>.
47
+ # :re<em>rdoc_dev</em>.
48
+ #
49
+ # If you wish to have completely different task names, then pass a Hash as
50
+ # first argument. With the <tt>:rdoc</tt>, <tt>:clobber_rdoc</tt> and
51
+ # <tt>:rerdoc</tt> options, you can customize the task names to your liking.
52
+ # For example:
53
+ #
54
+ # Rake::RDocTask.new(:rdoc => "rdoc", :clobber_rdoc => "rdoc:clean", :rerdoc => "rdoc:force")
55
+ #
56
+ # This will create the tasks <tt>:rdoc</tt>, <tt>:rdoc_clean</tt> and
57
+ # <tt>:rdoc:force</tt>.
43
58
  #
44
59
  class RDocTask < TaskLib
45
60
  # Name of the main, top level task. (default is :rdoc)
@@ -48,30 +63,45 @@ module Rake
48
63
  # Name of directory to receive the html output files. (default is "html")
49
64
  attr_accessor :rdoc_dir
50
65
 
51
- # Title of RDoc documentation. (default is none)
66
+ # Title of RDoc documentation. (defaults to rdoc's default)
52
67
  attr_accessor :title
53
68
 
54
69
  # Name of file to be used as the main, top level file of the
55
70
  # RDoc. (default is none)
56
71
  attr_accessor :main
57
72
 
58
- # Name of template to be used by rdoc. (default is 'html')
73
+ # Name of template to be used by rdoc. (defaults to rdoc's default)
59
74
  attr_accessor :template
60
75
 
61
76
  # List of files to be included in the rdoc generation. (default is [])
62
77
  attr_accessor :rdoc_files
63
78
 
64
- # List of options to be passed rdoc. (default is [])
79
+ # Additional list of options to be passed rdoc. (default is [])
65
80
  attr_accessor :options
66
81
 
67
- # Create an RDoc task named <em>rdoc</em>. Default task name is +rdoc+.
68
- def initialize(name=:rdoc) # :yield: self
82
+ # Whether to run the rdoc process as an external shell (default is false)
83
+ attr_accessor :external
84
+
85
+ attr_accessor :inline_source
86
+
87
+ # Create an RDoc task with the given name. See the RDocTask class overview
88
+ # for documentation.
89
+ def initialize(name = :rdoc) # :yield: self
90
+ if name.is_a?(Hash)
91
+ invalid_options = name.keys.map { |k| k.to_sym } - [:rdoc, :clobber_rdoc, :rerdoc]
92
+ if !invalid_options.empty?
93
+ raise ArgumentError, "Invalid option(s) passed to RDocTask.new: #{invalid_options.join(", ")}"
94
+ end
95
+ end
96
+
69
97
  @name = name
70
98
  @rdoc_files = Rake::FileList.new
71
99
  @rdoc_dir = 'html'
72
100
  @main = nil
73
101
  @title = nil
74
- @template = 'html'
102
+ @template = nil
103
+ @external = false
104
+ @inline_source = true
75
105
  @options = []
76
106
  yield self if block_given?
77
107
  define
@@ -79,50 +109,101 @@ module Rake
79
109
 
80
110
  # Create the tasks defined by this task lib.
81
111
  def define
82
- if name.to_s != "rdoc"
83
- desc "Build the RDOC HTML Files"
112
+ if rdoc_task_name != "rdoc"
113
+ desc "Build the RDOC HTML Files"
114
+ else
115
+ desc "Build the #{rdoc_task_name} HTML Files"
84
116
  end
85
-
86
- desc "Build the #{name} HTML Files"
87
- task name
117
+ task rdoc_task_name
88
118
 
89
119
  desc "Force a rebuild of the RDOC files"
90
- task paste("re", name) => [paste("clobber_", name), name]
120
+ task rerdoc_task_name => [clobber_task_name, rdoc_task_name]
91
121
 
92
122
  desc "Remove rdoc products"
93
- task paste("clobber_", name) do
94
- rm_r rdoc_dir rescue nil
123
+ task clobber_task_name do
124
+ rm_r rdoc_dir rescue nil
95
125
  end
96
-
97
- task :clobber => [paste("clobber_", name)]
126
+
127
+ task :clobber => [clobber_task_name]
98
128
 
99
129
  directory @rdoc_dir
100
- task name => [rdoc_target]
101
- file rdoc_target => @rdoc_files + [$rakefile] do
102
- rm_r @rdoc_dir rescue nil
103
- opts = option_list.join(' ')
104
- sh %{rdoc -o #{@rdoc_dir} #{opts} #{@rdoc_files}}
130
+ task rdoc_task_name => [rdoc_target]
131
+ file rdoc_target => @rdoc_files + [Rake.application.rakefile] do
132
+ rm_r @rdoc_dir rescue nil
133
+ @before_running_rdoc.call if @before_running_rdoc
134
+ args = option_list + @rdoc_files
135
+ if @external
136
+ argstring = args.join(' ')
137
+ sh %{ruby -Ivendor vendor/rd #{argstring}}
138
+ else
139
+ require 'rdoc/rdoc'
140
+ RDoc::RDoc.new.document(args)
141
+ end
105
142
  end
106
143
  self
107
144
  end
108
145
 
109
146
  def option_list
110
147
  result = @options.dup
111
- result << "--main" << "'#{main}'" if main
112
- result << "--title" << "'#{title}'" if title
113
- result << "-T" << "'#{template}'" if template
148
+ result << "-o" << @rdoc_dir
149
+ result << "--main" << quote(main) if main
150
+ result << "--title" << quote(title) if title
151
+ result << "-T" << quote(template) if template
152
+ result << "--inline-source" if inline_source && !@options.include?("--inline-source") && !@options.include?("-S")
114
153
  result
115
154
  end
116
155
 
156
+ def quote(str)
157
+ if @external
158
+ "'#{str}'"
159
+ else
160
+ str
161
+ end
162
+ end
163
+
117
164
  def option_string
118
165
  option_list.join(' ')
119
166
  end
167
+
168
+ # The block passed to this method will be called just before running the
169
+ # RDoc generator. It is allowed to modify RDocTask attributes inside the
170
+ # block.
171
+ def before_running_rdoc(&block)
172
+ @before_running_rdoc = block
173
+ end
120
174
 
121
175
  private
122
-
176
+
123
177
  def rdoc_target
124
178
  "#{rdoc_dir}/index.html"
125
179
  end
180
+
181
+ def rdoc_task_name
182
+ case name
183
+ when Hash
184
+ (name[:rdoc] || "rdoc").to_s
185
+ else
186
+ name.to_s
187
+ end
188
+ end
189
+
190
+ def clobber_task_name
191
+ case name
192
+ when Hash
193
+ (name[:clobber_rdoc] || "clobber_rdoc").to_s
194
+ else
195
+ "clobber_#{name}"
196
+ end
197
+ end
198
+
199
+ def rerdoc_task_name
200
+ case name
201
+ when Hash
202
+ (name[:rerdoc] || "rerdoc").to_s
203
+ else
204
+ "re#{name}"
205
+ end
206
+ end
126
207
 
127
208
  end
128
209
  end
@@ -0,0 +1,23 @@
1
+ module Test
2
+ module Unit
3
+ module Collector
4
+ class Dir
5
+ undef collect_file
6
+ def collect_file(name, suites, already_gathered)
7
+ # loadpath = $:.dup
8
+ dir = File.dirname(File.expand_path(name))
9
+ $:.unshift(dir) unless $:.first == dir
10
+ if(@req)
11
+ @req.require(name)
12
+ else
13
+ require(name)
14
+ end
15
+ find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)}
16
+ ensure
17
+ # $:.replace(loadpath)
18
+ $:.delete_at $:.rindex(dir)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/rake/runtest.rb CHANGED
@@ -10,11 +10,11 @@ module Rake
10
10
  Dir["#{pattern}"].each { |fn|
11
11
  puts fn if log_enabled
12
12
  begin
13
- load fn
13
+ load fn
14
14
  rescue Exception => ex
15
- puts "Error in #{fn}: #{ex.message}"
16
- puts ex.backtrace
17
- assert false
15
+ puts "Error in #{fn}: #{ex.message}"
16
+ puts ex.backtrace
17
+ assert false
18
18
  end
19
19
  }
20
20
  end
data/lib/rake/tasklib.rb CHANGED
@@ -1,22 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'rake'
4
+
3
5
  module Rake
4
6
 
5
7
  # Base class for Task Libraries.
6
8
  class TaskLib
9
+ include Cloneable
7
10
 
8
- # Make a copy of a task.
9
- def clone
10
- sibling = self.class.new
11
- instance_variables.each do |ivar|
12
- value = self.instance_variable_get(ivar)
13
- sibling.instance_variable_set(ivar, value.dup) if value
14
- end
15
- sibling
16
- end
17
-
18
- # Make a symbol by pasting two strings together.
19
- def paste(a,b)
11
+ # Make a symbol by pasting two strings together.
12
+ #
13
+ # NOTE: DEPRECATED! This method is kinda stupid. I don't know why
14
+ # I didn't just use string interpolation. But now other task
15
+ # libraries depend on this so I can't remove it without breaking
16
+ # other people's code. So for now it stays for backwards
17
+ # compatibility. BUT DON'T USE IT.
18
+ def paste(a,b) # :nodoc:
20
19
  (a.to_s + b.to_s).intern
21
20
  end
22
21
  end
data/lib/rake/testtask.rb CHANGED
@@ -36,8 +36,6 @@ module Rake
36
36
  #
37
37
  class TestTask < TaskLib
38
38
 
39
- SEP = ''
40
-
41
39
  # Name of test task. (default is :test)
42
40
  attr_accessor :name
43
41
 
@@ -48,12 +46,29 @@ module Rake
48
46
  # True if verbose test output desired. (default is false)
49
47
  attr_accessor :verbose
50
48
 
51
- # Test options passed to the test suite. (default is NONE)
49
+ # Test options passed to the test suite. An explicit
50
+ # TESTOPTS=opts on the command line will override this. (default
51
+ # is NONE)
52
52
  attr_accessor :options
53
53
 
54
+ # Request that the tests be run with the warning flag set.
55
+ # E.g. warning=true implies "ruby -w" used to run the tests.
56
+ attr_accessor :warning
57
+
54
58
  # Glob pattern to match test files. (default is 'test/test*.rb')
55
59
  attr_accessor :pattern
56
60
 
61
+ # Style of test loader to use. Options are:
62
+ #
63
+ # * :rake -- Rake provided test loading script (default).
64
+ # * :testrb -- Ruby provided test loading script.
65
+ # * :direct -- Load tests using command line loader.
66
+ #
67
+ attr_accessor :loader
68
+
69
+ # Array of commandline options to pass to ruby when running test loader.
70
+ attr_accessor :ruby_opts
71
+
57
72
  # Explicitly define the list of test files to be included in a
58
73
  # test. +list+ is expected to be an array of file names (a
59
74
  # FileList is acceptable). If both +pattern+ and +test_files+ are
@@ -67,8 +82,12 @@ module Rake
67
82
  @name = name
68
83
  @libs = ["lib"]
69
84
  @pattern = nil
85
+ @options = nil
70
86
  @test_files = nil
71
87
  @verbose = false
88
+ @warning = false
89
+ @loader = :rake
90
+ @ruby_opts = []
72
91
  yield self if block_given?
73
92
  @pattern = 'test/test*.rb' if @pattern.nil? && @test_files.nil?
74
93
  define
@@ -79,40 +98,64 @@ module Rake
79
98
  lib_path = @libs.join(File::PATH_SEPARATOR)
80
99
  desc "Run tests" + (@name==:test ? "" : " for #{@name}")
81
100
  task @name do
82
- RakeFileUtils.verbose(@verbose) do
83
- ruby %{-I#{lib_path} -e0 #{SEP}#{required_files}#{option_list}}
84
- end
101
+ run_code = ''
102
+ RakeFileUtils.verbose(@verbose) do
103
+ run_code =
104
+ case @loader
105
+ when :direct
106
+ "-e 'ARGV.each{|f| load f}'"
107
+ when :testrb
108
+ "-S testrb #{fix}"
109
+ when :rake
110
+ rake_loader
111
+ end
112
+ @ruby_opts.unshift( "-I\"#{lib_path}\"" )
113
+ @ruby_opts.unshift( "-w" ) if @warning
114
+ ruby @ruby_opts.join(" ") +
115
+ " \"#{run_code}\" " +
116
+ file_list.collect { |fn| "\"#{fn}\"" }.join(' ') +
117
+ " #{option_list}"
118
+ end
85
119
  end
86
120
  self
87
121
  end
88
122
 
89
- def required_files # :nodoc:
90
- file_list.gsub(/^(.*)\.rb$/, ' -r\1').join(" #{SEP}")
91
- end
92
-
93
123
  def option_list # :nodoc:
94
- if get_options
95
- testoptions = " #{SEP} -- #{get_options}"
124
+ ENV['TESTOPTS'] || @options || ""
125
+ end
126
+
127
+ def file_list # :nodoc:
128
+ if ENV['TEST']
129
+ FileList[ ENV['TEST'] ]
96
130
  else
97
- testoptions = ''
131
+ result = []
132
+ result += @test_files.to_a if @test_files
133
+ result += FileList[ @pattern ].to_a if @pattern
134
+ FileList[result]
98
135
  end
99
136
  end
100
137
 
101
- def get_options # :nodoc:
102
- ENV['TESTOPTS'] || @options
138
+ def fix # :nodoc:
139
+ case RUBY_VERSION
140
+ when '1.8.2'
141
+ find_file 'rake/ruby182_test_unit_fix'
142
+ else
143
+ nil
144
+ end || ''
103
145
  end
104
146
 
105
- def file_list # :nodoc:
106
- if ENV['TEST']
107
- FileList[ ENV['TEST'] ]
108
- else
109
- result = []
110
- result += @test_files.to_a if @test_files
111
- result += FileList[ @pattern ].to_a if @pattern
112
- FileList[result]
147
+ def rake_loader # :nodoc:
148
+ find_file('rake/rake_test_loader') or
149
+ fail "unable to find rake test loader"
150
+ end
151
+
152
+ def find_file(fn) # :nodoc:
153
+ $LOAD_PATH.each do |path|
154
+ file_path = File.join(path, "#{fn}.rb")
155
+ return file_path if File.exist? file_path
113
156
  end
157
+ nil
114
158
  end
115
159
 
116
160
  end
117
161
  end
118
-
data/lib/rake/win32.rb ADDED
@@ -0,0 +1,55 @@
1
+
2
+ module Rake
3
+ require 'rake/alt_system'
4
+
5
+ # Win 32 interface methods for Rake. Windows specific functionality
6
+ # will be placed here to collect that knowledge in one spot.
7
+ module Win32
8
+
9
+ # Error indicating a problem in locating the home directory on a
10
+ # Win32 system.
11
+ class Win32HomeError < RuntimeError
12
+ end
13
+
14
+ class << self
15
+ # True if running on a windows system.
16
+ def windows?
17
+ AltSystem::WINDOWS
18
+ end
19
+
20
+ # Run a command line on windows.
21
+ def rake_system(*cmd)
22
+ AltSystem.system(*cmd)
23
+ end
24
+
25
+ # The standard directory containing system wide rake files on
26
+ # Win 32 systems. Try the following environment variables (in
27
+ # order):
28
+ #
29
+ # * HOME
30
+ # * HOMEDRIVE + HOMEPATH
31
+ # * APPDATA
32
+ # * USERPROFILE
33
+ #
34
+ # If the above are not defined, the return nil.
35
+ def win32_system_dir #:nodoc:
36
+ win32_shared_path = ENV['HOME']
37
+ if win32_shared_path.nil? && ENV['HOMEDRIVE'] && ENV['HOMEPATH']
38
+ win32_shared_path = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
39
+ end
40
+
41
+ win32_shared_path ||= ENV['APPDATA']
42
+ win32_shared_path ||= ENV['USERPROFILE']
43
+ raise Win32HomeError, "Unable to determine home path environment variable." if
44
+ win32_shared_path.nil? or win32_shared_path.empty?
45
+ normalize(File.join(win32_shared_path, 'Rake'))
46
+ end
47
+
48
+ # Normalize a win32 path so that the slashes are all forward slashes.
49
+ def normalize(path)
50
+ path.gsub(/\\/, '/')
51
+ end
52
+
53
+ end
54
+ end
55
+ end