passenger 5.0.1 → 5.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (42) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +10 -0
  5. data/CHANGELOG +12 -0
  6. data/CONTRIBUTING.md +10 -0
  7. data/CONTRIBUTORS +1 -0
  8. data/Gemfile +10 -10
  9. data/build/basics.rb +5 -1
  10. data/build/common_library.rb +33 -55
  11. data/build/debian.rb +2 -4
  12. data/build/packaging.rb +3 -3
  13. data/build/preprocessor.rb +3 -204
  14. data/debian.template/{control.template → control.erb} +18 -18
  15. data/debian.template/{locations.ini.template → locations.ini.erb} +0 -0
  16. data/debian.template/{passenger-dev.install.template → passenger-dev.install.erb} +0 -0
  17. data/debian.template/passenger-doc.install.erb +2 -0
  18. data/debian.template/{passenger.install.template → passenger.install.erb} +0 -0
  19. data/debian.template/{rules.template → rules.erb} +24 -23
  20. data/doc/users_guide_snippets/tips.txt +9 -1
  21. data/ext/common/Constants.h +3 -1
  22. data/ext/common/ServerKit/Channel.h +22 -4
  23. data/ext/common/ServerKit/Context.h +3 -1
  24. data/ext/common/ServerKit/FdSinkChannel.h +9 -1
  25. data/ext/common/ServerKit/FdSourceChannel.h +9 -1
  26. data/ext/common/ServerKit/FileBufferedChannel.h +19 -7
  27. data/ext/common/Utils/SystemMetricsCollector.h +0 -1
  28. data/ext/common/Utils/VariantMap.h +22 -1
  29. data/ext/common/agents/HelperAgent/Main.cpp +15 -0
  30. data/ext/common/agents/HelperAgent/OptionParser.h +6 -1
  31. data/ext/common/agents/HelperAgent/RequestHandler.h +3 -0
  32. data/lib/phusion_passenger.rb +1 -1
  33. data/lib/phusion_passenger/common_library.rb +13 -5
  34. data/lib/phusion_passenger/config/restart_app_command.rb +8 -4
  35. data/lib/phusion_passenger/constants.rb +1 -0
  36. data/lib/phusion_passenger/standalone/command.rb +1 -1
  37. data/lib/phusion_passenger/standalone/start_command.rb +8 -0
  38. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +1 -0
  39. data/test/cxx/ServerKit/FileBufferedChannelTest.cpp +179 -4
  40. metadata +8 -8
  41. metadata.gz.asc +7 -7
  42. data/debian.template/passenger-doc.install.template +0 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MzQxOGI3N2E5ZWJiMjU3MGY2YmI4NzAxYWQ1ZTdlYTA3Yjk5N2FiMQ==
4
+ N2JiN2YxMTU2OTZmZTQzMzIwMTk1MWM4ZGZkYzZlZmJmMjdiZTM3NA==
5
5
  data.tar.gz: !binary |-
6
- ZjYxYTNjYjU0M2IxZWVjNzFmN2VmZGM2NWRkODg2NjliMTRiODE5Nw==
6
+ YWQ0YzJmMDg0MzMwYzBkOGQwZjExYWIzMjA1ZGM4MTAxMDE5NDNkZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NWNmNjM1MWEzYWU3ZjA3MjM3NmI2ODNlOWQ1MDcxYzUwMTU2NWEyZjJjZjc0
10
- NDlhMDJjMTdlOWJlYjcxYmI5OGU0ODczOWU2Y2NjZjQzOWMwM2I3YmY0Y2Rm
11
- NzZhNWY0MzY0M2VhMzRhMDQ0MjExNzdlYTM2OGU3YzdmNmU0NDU=
9
+ ZGZhMjdiMTZhYTk1MDRiMTYxNTlhMjhiZjNmZDI3OTBmYWZkZmEzM2ZkMDY5
10
+ ZTU4MDRmZWU1NjdiOTYxZjk2OGM0YmE3ZmZkNzU2YjRmZGIwMzI1M2UxYmU2
11
+ YTBmYWEyNzdlZWYzMzQ0YzczYmRiNjcwYTVjMDQzMTVjNjBiMmM=
12
12
  data.tar.gz: !binary |-
13
- ZTg5MGY4MTZjYTdkNmMzYjFlNDNhM2U3M2U1ODc1M2QxY2I2NDI1MTBlYzIz
14
- NGU4MjVkN2JjZmNlMzg0MDNjNzhiOWZkNGRlMDMxMGZlYzc2NDVlMzkwN2Iy
15
- MTBkYTA2OWFhOTM4MWU5MmUyZDRhMDE4MmJlN2U4NmNmNDc3MzM=
13
+ MzliMDhiMjI5MzM2NTZjNmUwYmQ4MWVhNmZiY2EwYTczNGY0NzI1YzAwMzQ4
14
+ MmQwZTQwZTI0Y2U2NjBiY2ZmZGQzZTI1YmVjN2MzMjNkNTUwOTk0NDJiZTI3
15
+ ZThhZTk0MzFjODY2NjQ1NWFlMzBiOTkwZWM0NTNmYmI4MjljYzY=
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJU9xtNAAoJECrHRaUKISqMJQAIAJrl0J6b4TJ5HELJjdVBV3Qq
6
- nZXFQt1JW6nf2i2/vAabc1LPG4RcMakOuzKmn3HLFuxasaZXHhrNtAMLboaIAs7n
7
- Hk2PuOh01nvbLm/mSVajzfRL9EkUfjJpoFtLfwlRt5c7J++PmbdFdNucsAYzIYgl
8
- 12nRnu6CUc7SxVki6QC5mZwJeKcR/QvzCWG7bJ2NDvvQWoKcmkksHlBmSOhPQWC9
9
- lExE5ZgZSjmjMeBd1peheNLPW62SVlZBKOgA32yA5wNg/KqZ88YalxQp82Pit44N
10
- 9iCA7A3PW+dUcrjbmXRYKDYSOg0SIAJvjCdRhHdcssbQPlGVbFrEZfRHrWxaRVw=
11
- =xWOs
5
+ iQEcBAABAgAGBQJU+skQAAoJECrHRaUKISqMCYEIALFilhp8+SVXIUbPuDbRWt+0
6
+ V8JijFYyXw1Jiy5VFW5V20Zu70okzRXd9V+u3UvgdRkftqrIPsp4b+vW18pAeg4i
7
+ Gse7Wjr53xMvNthIZKqC0BY6zi9FurzkgP4r0hkd2cLmcjB53NNui7nPuYt9mMwH
8
+ +WplJeAazcISRhQRJnlBLMDrVWesgJMj+xP/8SaKGsIYfVju8m78TI+hl0X+XT0T
9
+ qt9zZDLRd7EpJUS5BovYAFiU7ljLXeFt9bmvN23kZ1MB49GpaPe9hA9L8pRhZmOT
10
+ 7oyLImaMKFprjwL+vPS8JYM0b+NVYkT1ALki08yEoKIpwtnujkNFr6KpHAyGSFc=
11
+ =o0Sc
12
12
  -----END PGP SIGNATURE-----
data.tar.gz.asc CHANGED
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJU9xtNAAoJECrHRaUKISqMRNsIAJ3SdZ3briMF68aVBwnDb9Ht
6
- z4uRqGROnKA/FX7EpVRtxEypVaArGgvfSI+C2jho91GIBWoEL5S5YEbRL3RwUci0
7
- Pr4Ts+g9OYvmzYFrTmzM75W8pF2nht1hm9bxu1xxdfQMo9ts8+J9vpFjPv31+d6z
8
- o1JnVIHZXUw9NPeAUZzBN0DMozpDqY7JH1DXYzwR7uejikz3bDbTmnPq5xIv1RAO
9
- 7aR1VfOrHm3HCs8HMPXO46vfdg4bnhrzhVQgj8xKiMXQJrd6Si/Tx6kDURDGt0zr
10
- BBxiuYcCeVVG5LDCY6PCvaUNMal7m/98xG4AFCYpe3LirpthzAo6OZTDbaI8Dbk=
11
- =1Ov4
5
+ iQEcBAABAgAGBQJU+skQAAoJECrHRaUKISqM3w8IALm2kOp7hWd1Qy4mvAl7czSb
6
+ b+V/QGy14Mlt+v24GIuVjl88CaX4qA3eHGzsBNb807+L3pc/PYPi7qd0pf0hEN6x
7
+ kL730RkjnSFvpuLE37kuemtStVUKHnO8jxMbGWMUZEdEOLThw4LkeDKJPAjm2M2V
8
+ dWuHzzdFsA3tb79dy6dL8SSkpqwpfHhpeCdHbgZXhMq+PhllCieeICuwBvZeSOvE
9
+ mOwAqt5Toq1KNoKPE/gOPiywGsNQgLYn6HXYTuHrCz/p8YMjJOiTEMODTJD4io4V
10
+ cF6byuXfikIouVBgkZQYbwuItMlpJr9e4oZidjBZ7trFmqOT1FnnFiMUehsaHzw=
11
+ =h4HJ
12
12
  -----END PGP SIGNATURE-----
@@ -53,6 +53,16 @@ indent_style = space
53
53
  indent_size = 2
54
54
  trim_trailing_whitespace = true
55
55
 
56
+ [Rakefile]
57
+ indent_style = space
58
+ indent_size = 2
59
+ trim_trailing_whitespace = true
60
+
61
+ [Gemfile]
62
+ indent_style = space
63
+ indent_size = 2
64
+ trim_trailing_whitespace = true
65
+
56
66
  [*.js]
57
67
  indent_style = tab
58
68
  indent_size = 4
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ Release 5.0.2
2
+ -------------
3
+
4
+ * Fixes a connection freeze that could occur when processing large responses. This would manifest itself under the error message "This website is under heavy load" or "Request queue is full, returning an error". Closes GH-1404.
5
+ * Debian and Ubuntu packages have been reintroduces.
6
+ * When `passenger-config restart-app` is run interactively, if Passenger is not serving any applications, then the command now prints an error message instead of showing a menu with only a "Cancel" option.
7
+ * Fixes a compilation problem on FreeBSD 10 (contributed by: clemensg). Closes GH-1401.
8
+ * [Standalone] Fixes a crash that would occur if you use the `--ctl` parameter.
9
+ * [Enterprise] The `--max-request-time` option has been added to Passenger Standalone.
10
+ * [Enterprise] The `max_request_time_reached` hook has been introduced. This hook allows you to run diagnostics on a process that that took too long to respond to a request.
11
+
12
+
1
13
  Release 5.0.1
2
14
  -------------
3
15
 
@@ -17,6 +17,7 @@
17
17
  * [Ruby coding style](#ruby_coding_style)
18
18
  * [Systems programming fundamentals](#systems_programming_fundamentals)
19
19
  * [Further reading](#further_reading)
20
+ * [Git structure] (#git_structure)
20
21
 
21
22
  Thank you for your interest in Phusion Passenger. Phusion Passenger is open source so your contributions are very welcome. Although we also provide a [commercial version](https://www.phusionpassenger.com/enterprise) and [commercial support](https://www.phusionpassenger.com/commercial_support), the core remains open source and we remain committed to keep it that way. This guide gives you an overview of the ways with which you can contribute, as well as contribution guidelines.
22
23
 
@@ -348,3 +349,12 @@ A good and comprehensive, but rather large source for learning POSIX is the [POS
348
349
  ### Further reading
349
350
 
350
351
  * [Coding Tips and Pitfalls](https://github.com/phusion/passenger/blob/master/doc/CodingTipsAndPitfalls.md)
352
+
353
+ <a name="git_structure"></a>
354
+ ### Git structure
355
+
356
+ The **master** branch is the main development branch, containing the latest and greatest code that was tested and accepted for inclusion into passenger (usually merged in from loose development branches that are deleted afterwards). This branch may not be stable enough yet for production.
357
+
358
+ Branches like **stable-4.0**, **stable-5.0** are production quality branches (split off from master) for major versions. Each production branch has tags for minor versions, whereby **tag x.0.1** represents the first production-ready version on a branch (there may be some release candidates before that). For example: branch stable-5.0, tagged 5.0.1 is the first release of the 5.0 line that is ready for production.
359
+
360
+ In general we apply fixes to the respective stable branch and merge these into the master, so it is easiest if you submit pull requests to the stable branches (unless of course you are working with the unstable master). Conversely, new features always go to the master and are then cherrypicked from one or more branches.
@@ -12,6 +12,7 @@ Bernd Ahlers
12
12
  Chad Fowler
13
13
  Chris Walquist
14
14
  Christoffer Sawicki
15
+ Clemens Gruber
15
16
  Damien Le Berrigaud
16
17
  Dan Peterson
17
18
  Danial Pearce
data/Gemfile CHANGED
@@ -1,17 +1,17 @@
1
1
  source 'https://rubygems.org/'
2
2
 
3
3
  group :base do
4
- gem 'mime-types', '1.25'
5
- gem 'rspec', '2.14.1'
6
- gem 'rake'
7
- gem 'drake'
8
- gem 'json'
9
- gem 'rack'
4
+ gem 'mime-types', '1.25'
5
+ gem 'rspec', '2.14.1'
6
+ gem 'rake'
7
+ gem 'drake'
8
+ gem 'json'
9
+ gem 'rack'
10
10
  end
11
11
 
12
12
  group :doc do
13
- # Last version that worked on Ruby 1.8
14
- gem 'nokogiri', '1.5.9'
15
- gem 'mizuho'
16
- gem 'bluecloth'
13
+ # Last version that worked on Ruby 1.8
14
+ gem 'nokogiri', '1.5.9'
15
+ gem 'mizuho'
16
+ gem 'bluecloth'
17
17
  end
@@ -146,15 +146,19 @@ USE_DMALLOC = boolean_option('USE_DMALLOC')
146
146
  USE_EFENCE = boolean_option('USE_EFENCE')
147
147
  USE_ASAN = boolean_option('USE_ASAN')
148
148
  OPTIMIZE = boolean_option('OPTIMIZE')
149
+ LTO = OPTIMIZE && boolean_option('LTO')
149
150
 
150
151
  # Agent-specific compiler flags.
151
152
  AGENT_CFLAGS = ""
152
- AGENT_CFLAGS << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN
153
153
  AGENT_CFLAGS << " -O" if OPTIMIZE
154
+ AGENT_CFLAGS << " -flto" if LTO
155
+ AGENT_CFLAGS << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN
154
156
  AGENT_CFLAGS.strip!
155
157
 
156
158
  # Agent-specific linker flags.
157
159
  AGENT_LDFLAGS = ""
160
+ AGENT_LDFLAGS << " -O" if OPTIMIZE
161
+ AGENT_LDFLAGS << " -flto" if LTO
158
162
  AGENT_LDFLAGS << " #{PlatformInfo.dmalloc_ldflags}" if USE_DMALLOC
159
163
  AGENT_LDFLAGS << " #{PlatformInfo.electric_fence_ldflags}" if USE_EFENCE
160
164
  AGENT_LDFLAGS << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN
@@ -36,66 +36,44 @@ def define_libboost_oxt_task(namespace, output_dir, extra_compiler_flags = nil)
36
36
  output_file = "#{output_dir}.a"
37
37
  flags = "-Iext #{extra_compiler_flags} #{EXTRA_CXXFLAGS}"
38
38
 
39
- if false && boolean_option('RELEASE')
40
- # Disable RELEASE support. Passenger Standalone wants to link to the
41
- # common library but does not know whether it was compiled with RELEASE
42
- # or not. See http://code.google.com/p/phusion-passenger/issues/detail?id=808
43
- sources = Dir['ext/boost/libs/**/*.cpp'] + Dir['ext/oxt/*.cpp']
44
- sources.sort!
45
-
46
- aggregate_source = "#{output_dir}/aggregate.cpp"
47
- aggregate_object = "#{output_dir}/aggregate.o"
48
- object_files = [aggregate_object]
49
-
50
- file(aggregate_object => sources) do
51
- sh "mkdir -p #{output_dir}" if !File.directory?(output_dir)
52
- aggregate_content = %Q{
53
- #ifndef _GNU_SOURCE
54
- #define _GNU_SOURCE
55
- #endif
56
- }
57
- sources.each do |source_file|
58
- name = source_file.sub(/^ext\//, '')
59
- aggregate_content << "#include \"#{name}\"\n"
60
- end
61
- File.open(aggregate_source, 'w') do |f|
62
- f.write(aggregate_content)
63
- end
64
- compile_cxx(aggregate_source, "#{flags} -o #{aggregate_object}")
65
- end
66
- else
67
- # Define compilation targets for .cpp files in ext/boost/src/pthread.
68
- boost_object_files = []
69
- Dir['ext/boost/libs/**/*.cpp'].each do |source_file|
70
- object_name = File.basename(source_file.sub(/\.cpp$/, '.o'))
71
- boost_output_dir = "#{output_dir}/boost"
72
- object_file = "#{boost_output_dir}/#{object_name}"
73
- boost_object_files << object_file
74
-
75
- file object_file => source_file do
76
- sh "mkdir -p #{boost_output_dir}" if !File.directory?(boost_output_dir)
77
- compile_cxx(source_file, "#{flags} -o #{object_file}")
78
- end
39
+ if OPTIMIZE
40
+ optimize = "-O2"
41
+ if LTO
42
+ optimize << " -flto"
79
43
  end
44
+ end
80
45
 
81
- # Define compilation targets for .cpp files in ext/oxt.
82
- oxt_object_files = []
83
- oxt_dependency_files = Dir["ext/oxt/*.hpp"] + Dir["ext/oxt/detail/*.hpp"]
84
- Dir['ext/oxt/*.cpp'].each do |source_file|
85
- object_name = File.basename(source_file.sub(/\.cpp$/, '.o'))
86
- oxt_output_dir = "#{output_dir}/oxt"
87
- object_file = "#{oxt_output_dir}/#{object_name}"
88
- oxt_object_files << object_file
89
-
90
- file object_file => [source_file, *oxt_dependency_files] do
91
- sh "mkdir -p #{oxt_output_dir}" if !File.directory?(oxt_output_dir)
92
- compile_cxx(source_file, "-O2 #{flags} -o #{object_file}".strip)
93
- end
46
+ # Define compilation targets for .cpp files in ext/boost/src/pthread.
47
+ boost_object_files = []
48
+ Dir['ext/boost/libs/**/*.cpp'].each do |source_file|
49
+ object_name = File.basename(source_file.sub(/\.cpp$/, '.o'))
50
+ boost_output_dir = "#{output_dir}/boost"
51
+ object_file = "#{boost_output_dir}/#{object_name}"
52
+ boost_object_files << object_file
53
+
54
+ file object_file => source_file do
55
+ sh "mkdir -p #{boost_output_dir}" if !File.directory?(boost_output_dir)
56
+ compile_cxx(source_file, "#{optimize} #{flags} -o #{object_file}")
94
57
  end
58
+ end
95
59
 
96
- object_files = boost_object_files + oxt_object_files
60
+ # Define compilation targets for .cpp files in ext/oxt.
61
+ oxt_object_files = []
62
+ oxt_dependency_files = Dir["ext/oxt/*.hpp"] + Dir["ext/oxt/detail/*.hpp"]
63
+ Dir['ext/oxt/*.cpp'].each do |source_file|
64
+ object_name = File.basename(source_file.sub(/\.cpp$/, '.o'))
65
+ oxt_output_dir = "#{output_dir}/oxt"
66
+ object_file = "#{oxt_output_dir}/#{object_name}"
67
+ oxt_object_files << object_file
68
+
69
+ file object_file => [source_file, *oxt_dependency_files] do
70
+ sh "mkdir -p #{oxt_output_dir}" if !File.directory?(oxt_output_dir)
71
+ compile_cxx(source_file, "#{optimize} #{flags} -o #{object_file}".strip)
72
+ end
97
73
  end
98
74
 
75
+ object_files = boost_object_files + oxt_object_files
76
+
99
77
  file(output_file => object_files) do
100
78
  sh "mkdir -p #{output_dir}"
101
79
  create_static_library(output_file, object_files.join(' '))
@@ -238,5 +216,5 @@ libboost_oxt_cflags = ""
238
216
  libboost_oxt_cflags << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN
239
217
  libboost_oxt_cflags.strip!
240
218
  LIBBOOST_OXT = define_libboost_oxt_task("common", COMMON_OUTPUT_DIR + "libboost_oxt", libboost_oxt_cflags)
241
- COMMON_LIBRARY.enable_optimizations! if OPTIMIZE
219
+ COMMON_LIBRARY.enable_optimizations!(LTO) if OPTIMIZE
242
220
  COMMON_LIBRARY.define_tasks(libboost_oxt_cflags)
@@ -102,8 +102,7 @@ task 'debian:dev' do
102
102
  sh "cd #{PKG_DIR}/#{distribution} && dpkg-checkbuilddeps"
103
103
  end
104
104
  distributions.each do |distribution|
105
- sh "cd #{PKG_DIR}/#{distribution} && env DEBUILD_DPKG_BUILDPACKAGE_OPTS=-F " +
106
- "debuild -e CCACHE_* -us -uc"
105
+ sh "cd #{PKG_DIR}/#{distribution} && debuild -e CCACHE_* -us -uc -F"
107
106
  end
108
107
  end
109
108
 
@@ -147,8 +146,7 @@ task 'debian:source_packages' => 'debian:orig_tarball' do
147
146
  create_debian_package_dir(distribution, pkg_dir)
148
147
  end
149
148
  ALL_DISTRIBUTIONS.each do |distribution|
150
- sh "cd #{pkg_dir}/#{distribution} && env DEBUILD_DPKG_BUILDPACKAGE_OPTS=-S " +
151
- "debuild -us -uc"
149
+ sh "cd #{pkg_dir}/#{distribution} && debuild -us -uc -S"
152
150
  end
153
151
  end
154
152
 
@@ -1,5 +1,5 @@
1
1
  # Phusion Passenger - https://www.phusionpassenger.com/
2
- # Copyright (c) 2010-2014 Phusion
2
+ # Copyright (c) 2010-2015 Phusion
3
3
  #
4
4
  # "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
5
5
  #
@@ -34,8 +34,8 @@ def recursive_copy_files(files, destination_dir, preprocess = false, variables =
34
34
  FileUtils.mkdir_p("#{destination_dir}/#{dir}")
35
35
  end
36
36
  if !File.directory?(filename)
37
- if preprocess && filename =~ /\.template$/
38
- real_filename = filename.sub(/\.template$/, '')
37
+ if preprocess && filename =~ /\.erb$/
38
+ real_filename = filename.sub(/\.erb$/, '')
39
39
  FileUtils.install(filename, "#{destination_dir}/#{real_filename}", :preserve => true)
40
40
  Preprocessor.new.start(filename, "#{destination_dir}/#{real_filename}",
41
41
  variables)
@@ -22,43 +22,9 @@
22
22
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  # THE SOFTWARE.
24
24
 
25
- # Implements a simple preprocessor language which combines elements in the C
26
- # preprocessor with ERB:
27
- #
28
- # Today
29
- # #if @today == :fine
30
- # is a fine day.
31
- # #elif @today == :good
32
- # is a good day.
33
- # #else
34
- # is a sad day.
35
- # #endif
36
- # Let's go walking.
37
- # Today is <%= Time.now %>.
38
- #
39
- # When run with...
40
- #
41
- # Preprocessor.new.start('input.txt', 'output.txt', :today => :fine)
42
- #
43
- # ...will produce:
44
- #
45
- # Today
46
- # is a fine day.
47
- # Let's go walking.
48
- # Today is 2013-08-11 22:37:06 +0200.
49
- #
50
- # Highlights:
51
- #
52
- # * #if blocks can be nested.
53
- # * Expressions are Ruby expressions, evaluated within the binding of a
54
- # Preprocessor::Evaluator object.
55
- # * Text inside #if/#elif/#else are automatically unindented.
56
- # * ERB compatible.
57
25
  class Preprocessor
58
26
  def initialize
59
27
  require 'erb' if !defined?(ERB)
60
- @indentation_size = 4
61
- @debug = boolean_option('DEBUG')
62
28
  end
63
29
 
64
30
  def start(filename, output_filename, variables = {})
@@ -69,99 +35,10 @@ class Preprocessor
69
35
  output = STDOUT
70
36
  end
71
37
  the_binding = create_binding(variables)
72
- context = []
73
- @filename = filename
74
- @lineno = 1
75
- @indentation = 0
76
-
77
- each_line(filename, the_binding) do |line|
78
- debug("context=#{context.inspect}, line=#{line.inspect}")
79
-
80
- name, args_string, cmd_indentation = recognize_command(line)
81
- case name
82
- when "if"
83
- case context.last
84
- when nil, :if_true, :else_true
85
- check_indentation(cmd_indentation)
86
- result = the_binding.eval(args_string, filename, @lineno)
87
- context.push(result ? :if_true : :if_false)
88
- inc_indentation
89
- when :if_false, :else_false, :if_ignore
90
- check_indentation(cmd_indentation)
91
- inc_indentation
92
- context.push(:if_ignore)
93
- else
94
- terminate "#if is not allowed in this context"
95
- end
96
- when "elif"
97
- case context.last
98
- when :if_true
99
- dec_indentation
100
- check_indentation(cmd_indentation)
101
- inc_indentation
102
- context[-1] = :if_false
103
- when :if_false
104
- dec_indentation
105
- check_indentation(cmd_indentation)
106
- inc_indentation
107
- result = the_binding.eval(args_string, filename, @lineno)
108
- context[-1] = result ? :if_true : :if_false
109
- when :else_true, :else_false
110
- terminate "#elif is not allowed after #else"
111
- when :if_ignore
112
- dec_indentation
113
- check_indentation(cmd_indentation)
114
- inc_indentation
115
- else
116
- terminate "#elif is not allowed outside #if block"
117
- end
118
- when "else"
119
- case context.last
120
- when :if_true
121
- dec_indentation
122
- check_indentation(cmd_indentation)
123
- inc_indentation
124
- context[-1] = :else_false
125
- when :if_false
126
- dec_indentation
127
- check_indentation(cmd_indentation)
128
- inc_indentation
129
- context[-1] = :else_true
130
- when :else_true, :else_false
131
- terminate "it is not allowed to have multiple #else clauses in one #if block"
132
- when :if_ignore
133
- dec_indentation
134
- check_indentation(cmd_indentation)
135
- inc_indentation
136
- else
137
- terminate "#else is not allowed outside #if block"
138
- end
139
- when "endif"
140
- case context.last
141
- when :if_true, :if_false, :else_true, :else_false, :if_ignore
142
- dec_indentation
143
- check_indentation(cmd_indentation)
144
- context.pop
145
- else
146
- terminate "#endif is not allowed outside #if block"
147
- end
148
- when "DEBHELPER"
149
- output.puts(line)
150
- when "", nil
151
- # Either a comment or not a preprocessor command.
152
- case context.last
153
- when nil, :if_true, :else_true
154
- output.puts(unindent(line))
155
- else
156
- # Check indentation but do not output.
157
- unindent(line)
158
- end
159
- else
160
- terminate "Unrecognized preprocessor command ##{name.inspect}"
161
- end
162
38
 
163
- @lineno += 1
164
- end
39
+ erb = ERB.new(File.read(filename), nil, "-")
40
+ erb.filename = filename
41
+ output.write(erb.result(the_binding))
165
42
  ensure
166
43
  if output_filename && output
167
44
  output.close
@@ -251,37 +128,6 @@ private
251
128
  end
252
129
  end
253
130
 
254
- def each_line(filename, the_binding)
255
- data = File.open(filename, 'r') do |f|
256
- erb = ERB.new(f.read, nil, "-")
257
- erb.filename = filename
258
- erb.result(the_binding)
259
- end
260
- data.each_line do |line|
261
- yield line.chomp
262
- end
263
- end
264
-
265
- def recognize_command(line)
266
- if line =~ /^([\s\t]*)#(.+)/
267
- indentation_str = $1
268
- command = $2
269
-
270
- # Declare tabs as equivalent to 4 spaces. This is necessary for
271
- # Makefiles in which the use of tabs is required.
272
- indentation_str.gsub!("\t", " ")
273
-
274
- name = command.scan(/^\w+/).first
275
- # Ignore shebangs and comments.
276
- return if name.nil?
277
-
278
- args_string = command.sub(/^#{Regexp.escape(name)}[\s\t]*/, '')
279
- return [name, args_string, indentation_str.to_s.size]
280
- else
281
- return nil
282
- end
283
- end
284
-
285
131
  def create_binding(variables)
286
132
  object = Evaluator.new
287
133
  variables.each_pair do |key, val|
@@ -291,51 +137,4 @@ private
291
137
  binding
292
138
  end
293
139
  end
294
-
295
- def inc_indentation
296
- @indentation += @indentation_size
297
- end
298
-
299
- def dec_indentation
300
- @indentation -= @indentation_size
301
- end
302
-
303
- def check_indentation(expected)
304
- if expected != @indentation
305
- terminate "wrong indentation: found #{expected} characters, should be #{@indentation}"
306
- end
307
- end
308
-
309
- def unindent(line)
310
- line =~ /^([\s\t]*)/
311
- # Declare tabs as equivalent to 4 spaces. This is necessary for
312
- # Makefiles in which the use of tabs is required.
313
- found = $1.to_s.gsub("\t", " ").size
314
-
315
- if found >= @indentation
316
- # Tab-friendly way to remove indentation.
317
- remaining = @indentation
318
- line = line.dup
319
- while remaining > 0
320
- if line[0..0] == " "
321
- remaining -= 1
322
- else
323
- # This is a tab.
324
- remaining -= 4
325
- end
326
- line.slice!(0, 1)
327
- end
328
- return line
329
- else
330
- terminate "wrong indentation: found #{found} characters, should be at least #{@indentation}"
331
- end
332
- end
333
-
334
- def debug(message)
335
- puts "DEBUG:#{@lineno}: #{message}" if @debug
336
- end
337
-
338
- def terminate(message)
339
- abort "*** ERROR: #{@filename} line #{@lineno}: #{message}"
340
- end
341
140
  end