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