lib/nanoc/cli/commands/compile.rb in nanoc-4.2.3 vs lib/nanoc/cli/commands/compile.rb in nanoc-4.2.4
- old
+ new
@@ -12,10 +12,11 @@
IDENTICAL - The item was deemed outdated and has been recompiled, but the compiled version turned out to be identical to the already existing version
SKIP - The item was deemed not outdated and was therefore not recompiled
EOS
+flag nil, :profile, 'profile compilation' if Nanoc::Feature.enabled?('PROFILER')
module Nanoc::CLI::Commands
class Compile < ::Nanoc::CLI::CommandRunner
# Listens to compilation events and reacts to them. This abstract class
# does not have a real implementation; subclasses should override {#start}
@@ -47,10 +48,22 @@
# Stops the listener. The default implementation removes self from all notification center observers.
#
# @return [void]
def stop
end
+
+ # @api private
+ def start_safely
+ start
+ @_started = true
+ end
+
+ # @api private
+ def stop_safely
+ stop if @_started
+ @_started = false
+ end
end
# Generates diffs for every output file written
class DiffGenerator < Listener
# @see Listener#enable_for?
@@ -61,14 +74,14 @@
# @see Listener#start
def start
require 'tempfile'
setup_diffs
old_contents = {}
- Nanoc::Int::NotificationCenter.on(:will_write_rep) do |rep, path|
+ Nanoc::Int::NotificationCenter.on(:will_write_rep, self) do |rep, path|
old_contents[rep] = File.file?(path) ? File.read(path) : nil
end
- Nanoc::Int::NotificationCenter.on(:rep_written) do |rep, path, _is_created, _is_modified|
+ Nanoc::Int::NotificationCenter.on(:rep_written, self) do |rep, path, _is_created, _is_modified|
unless rep.binary?
new_contents = File.file?(path) ? File.read(path) : nil
if old_contents[rep] && new_contents
generate_diff_for(path, old_contents[rep], new_contents)
end
@@ -78,10 +91,14 @@
end
# @see Listener#stop
def stop
super
+
+ Nanoc::Int::NotificationCenter.remove(:will_write_rep, self)
+ Nanoc::Int::NotificationCenter.remove(:rep_written, self)
+
teardown_diffs
end
protected
@@ -348,10 +365,32 @@
def log(level, action, path, duration)
Nanoc::CLI::Logger.instance.file(level, action, path, duration)
end
end
+ # Records a profile using StackProf
+ class StackProfProfiler < Listener
+ PROFILE_FILE = 'tmp/stackprof_profile'.freeze
+
+ # @see Listener#enable_for?
+ def self.enable_for?(command_runner)
+ command_runner.options.fetch(:profile, false)
+ end
+
+ # @see Listener#start
+ def start
+ require 'stackprof'
+ StackProf.start(mode: :cpu)
+ end
+
+ # @see Listener#stop
+ def stop
+ StackProf.stop
+ StackProf.results(PROFILE_FILE)
+ end
+ end
+
attr_accessor :listener_classes
def initialize(options, arguments, command)
super
@listener_classes = default_listener_classes
@@ -386,20 +425,21 @@
Nanoc::CLI::Commands::Compile::DiffGenerator,
Nanoc::CLI::Commands::Compile::DebugPrinter,
Nanoc::CLI::Commands::Compile::TimingRecorder,
Nanoc::CLI::Commands::Compile::GCController,
Nanoc::CLI::Commands::Compile::FileActionPrinter,
+ Nanoc::CLI::Commands::Compile::StackProfProfiler,
]
end
def setup_listeners
@listeners =
@listener_classes
.select { |klass| klass.enable_for?(self) }
.map { |klass| klass.new(reps: reps) }
- @listeners.each(&:start)
+ @listeners.each(&:start_safely)
end
def listeners
@listeners
end
@@ -410,10 +450,10 @@
ensure
teardown_listeners
end
def teardown_listeners
- @listeners.each(&:stop)
+ @listeners.each(&:stop_safely)
end
def reps
site.compiler.reps
end