lib/nanoc/base/services/compiler.rb in nanoc-4.4.6 vs lib/nanoc/base/services/compiler.rb in nanoc-4.4.7
- old
+ new
@@ -71,133 +71,277 @@
def compiled_content_cache
@compiled_content_cache
end
end
- # Provides functionality for (re)calculating the content of an item rep, without caching or
- # outdatedness checking.
- class RecalculatePhase
- include Nanoc::Int::ContractsSupport
+ # All phases for the compilation of a single item rep. Phases will be repeated for every rep.
+ module Phases
+ # Provides functionality for (re)calculating the content of an item rep, without caching or
+ # outdatedness checking.
+ class Recalculate
+ include Nanoc::Int::ContractsSupport
- def initialize(action_provider:, dependency_store:, compilation_context:)
- @action_provider = action_provider
- @dependency_store = dependency_store
- @compilation_context = compilation_context
+ def initialize(action_provider:, dependency_store:, compilation_context:)
+ @action_provider = action_provider
+ @dependency_store = dependency_store
+ @compilation_context = compilation_context
+ end
+
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
+ def run(rep, is_outdated:) # rubocop:disable Lint/UnusedMethodArgument
+ dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store)
+ dependency_tracker.enter(rep.item)
+
+ executor = Nanoc::Int::Executor.new(rep, @compilation_context, dependency_tracker)
+
+ @action_provider.memory_for(rep).each do |action|
+ case action
+ when Nanoc::Int::ProcessingActions::Filter
+ executor.filter(action.filter_name, action.params)
+ when Nanoc::Int::ProcessingActions::Layout
+ executor.layout(action.layout_identifier, action.params)
+ when Nanoc::Int::ProcessingActions::Snapshot
+ executor.snapshot(action.snapshot_name)
+ else
+ raise Nanoc::Int::Errors::InternalInconsistency, "unknown action #{action.inspect}"
+ end
+ end
+ ensure
+ dependency_tracker.exit
+ end
end
- contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
- def run(rep, is_outdated:) # rubocop:disable Lint/UnusedMethodArgument
- dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store)
- dependency_tracker.enter(rep.item)
+ # Provides functionality for (re)calculating the content of an item rep, with caching or
+ # outdatedness checking. Delegates to s::Recalculate if outdated or no cache available.
+ class Cache
+ include Nanoc::Int::ContractsSupport
- executor = Nanoc::Int::Executor.new(rep, @compilation_context, dependency_tracker)
+ def initialize(compiled_content_cache:, wrapped:)
+ @compiled_content_cache = compiled_content_cache
+ @wrapped = wrapped
+ end
- @action_provider.memory_for(rep).each do |action|
- case action
- when Nanoc::Int::ProcessingActions::Filter
- executor.filter(action.filter_name, action.params)
- when Nanoc::Int::ProcessingActions::Layout
- executor.layout(action.layout_identifier, action.params)
- when Nanoc::Int::ProcessingActions::Snapshot
- executor.snapshot(action.snapshot_name)
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
+ def run(rep, is_outdated:)
+ if can_reuse_content_for_rep?(rep, is_outdated: is_outdated)
+ Nanoc::Int::NotificationCenter.post(:cached_content_used, rep)
+ rep.snapshot_contents = @compiled_content_cache[rep]
else
- raise Nanoc::Int::Errors::InternalInconsistency, "unknown action #{action.inspect}"
+ @wrapped.run(rep, is_outdated: is_outdated)
end
+
+ rep.compiled = true
+ @compiled_content_cache[rep] = rep.snapshot_contents
end
- ensure
- dependency_tracker.exit
+
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Bool
+ def can_reuse_content_for_rep?(rep, is_outdated:)
+ !is_outdated && !@compiled_content_cache[rep].nil?
+ end
end
- end
- # Provides functionality for (re)calculating the content of an item rep, with caching or
- # outdatedness checking. Delegates to RecalculatePhase if outdated or no cache available.
- class CachePhase
- include Nanoc::Int::ContractsSupport
+ # Provides functionality for suspending and resuming item rep compilation (using fibers).
+ class Resume
+ include Nanoc::Int::ContractsSupport
- def initialize(compiled_content_cache:, wrapped:)
- @compiled_content_cache = compiled_content_cache
- @wrapped = wrapped
+ def initialize(wrapped:)
+ @wrapped = wrapped
+ end
+
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
+ def run(rep, is_outdated:)
+ fiber = fiber_for(rep, is_outdated: is_outdated)
+ while fiber.alive?
+ Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
+ res = fiber.resume
+
+ case res
+ when Nanoc::Int::Errors::UnmetDependency
+ Nanoc::Int::NotificationCenter.post(:compilation_suspended, rep, res)
+ raise(res)
+ when Proc
+ fiber.resume(res.call)
+ else
+ # TODO: raise
+ end
+ end
+
+ Nanoc::Int::NotificationCenter.post(:compilation_ended, rep)
+ end
+
+ private
+
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => Fiber
+ def fiber_for(rep, is_outdated:)
+ @fibers ||= {}
+
+ @fibers[rep] ||=
+ Fiber.new do
+ @wrapped.run(rep, is_outdated: is_outdated)
+ @fibers.delete(rep)
+ end
+
+ @fibers[rep]
+ end
end
- contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
- def run(rep, is_outdated:)
- if can_reuse_content_for_rep?(rep, is_outdated: is_outdated)
- Nanoc::Int::NotificationCenter.post(:cached_content_used, rep)
- rep.snapshot_contents = @compiled_content_cache[rep]
- else
- @wrapped.run(rep, is_outdated: is_outdated)
+ class Write
+ include Nanoc::Int::ContractsSupport
+
+ def initialize(wrapped:)
+ @wrapped = wrapped
end
- rep.compiled = true
- @compiled_content_cache[rep] = rep.snapshot_contents
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
+ def run(rep, is_outdated:)
+ @wrapped.run(rep, is_outdated: is_outdated)
+
+ rep.snapshot_defs.each do |sdef|
+ ItemRepWriter.new.write(rep, sdef.name)
+ end
+ end
end
- contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Bool
- def can_reuse_content_for_rep?(rep, is_outdated:)
- !is_outdated && !@compiled_content_cache[rep].nil?
+ class MarkDone
+ include Nanoc::Int::ContractsSupport
+
+ def initialize(wrapped:, outdatedness_store:)
+ @wrapped = wrapped
+ @outdatedness_store = outdatedness_store
+ end
+
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
+ def run(rep, is_outdated:)
+ @wrapped.run(rep, is_outdated: is_outdated)
+ @outdatedness_store.remove(rep)
+ end
end
end
- # Provides functionality for suspending and resuming item rep compilation (using fibers).
- class ResumePhase
- include Nanoc::Int::ContractsSupport
+ module Stages
+ class Preprocess
+ def initialize(action_provider:, site:, dependency_store:, checksum_store:)
+ @action_provider = action_provider
+ @site = site
+ @dependency_store = dependency_store
+ @checksum_store = checksum_store
+ end
- def initialize(wrapped:)
- @wrapped = wrapped
+ def run
+ @action_provider.preprocess(@site)
+
+ @dependency_store.objects = @site.items.to_a + @site.layouts.to_a
+ @checksum_store.objects = @site.items.to_a + @site.layouts.to_a + @site.code_snippets + [@site.config]
+
+ @site.freeze
+ end
end
- contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
- def run(rep, is_outdated:)
- fiber = fiber_for(rep, is_outdated: is_outdated)
- while fiber.alive?
- Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
- res = fiber.resume
+ class Prune
+ def initialize(config:, reps:)
+ @config = config
+ @reps = reps
+ end
- case res
- when Nanoc::Int::Errors::UnmetDependency
- Nanoc::Int::NotificationCenter.post(:compilation_suspended, rep, res)
- raise(res)
- when Proc
- fiber.resume(res.call)
- else
- # TODO: raise
+ def run
+ if @config[:prune][:auto_prune]
+ Nanoc::Pruner.new(@config, @reps, exclude: prune_config_exclude).run
end
end
- Nanoc::Int::NotificationCenter.post(:compilation_ended, rep)
- end
+ private
- private
+ def prune_config
+ @config[:prune] || {}
+ end
- contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => Fiber
- def fiber_for(rep, is_outdated:)
- @fibers ||= {}
+ def prune_config_exclude
+ prune_config[:exclude] || {}
+ end
+ end
- @fibers[rep] ||=
- Fiber.new do
- @wrapped.run(rep, is_outdated: is_outdated)
- @fibers.delete(rep)
+ class DetermineOutdatedness
+ def initialize(reps:, outdatedness_checker:, outdatedness_store:)
+ @reps = reps
+ @outdatedness_checker = outdatedness_checker
+ @outdatedness_store = outdatedness_store
+ end
+
+ def run
+ outdated_reps_tmp = @reps.select do |r|
+ @outdatedness_store.include?(r) || @outdatedness_checker.outdated?(r)
end
- @fibers[rep]
- end
- end
+ outdated_items = outdated_reps_tmp.map(&:item).uniq
+ outdated_reps = Set.new(outdated_items.flat_map { |i| @reps[i] })
- class WritePhase
- include Nanoc::Int::ContractsSupport
+ outdated_reps.each { |r| @outdatedness_store.add(r) }
- def initialize(wrapped:)
- @wrapped = wrapped
+ yield(outdated_items)
+ end
end
- contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
- def run(rep, is_outdated:)
- @wrapped.run(rep, is_outdated: is_outdated)
+ class CompileReps
+ def initialize(outdatedness_store:, dependency_store:, action_provider:, compilation_context:, compiled_content_cache:)
+ @outdatedness_store = outdatedness_store
+ @dependency_store = dependency_store
+ @action_provider = action_provider
+ @compilation_context = compilation_context
+ @compiled_content_cache = compiled_content_cache
+ end
- rep.snapshot_defs.each do |sdef|
- ItemRepWriter.new.write(rep, sdef.name)
+ def run
+ selector = Nanoc::Int::ItemRepSelector.new(@outdatedness_store.to_a)
+ selector.each do |rep|
+ handle_errors_while(rep) { compile_rep(rep, is_outdated: @outdatedness_store.include?(rep)) }
+ end
+ ensure
+ @outdatedness_store.store
+ @compiled_content_cache.store
end
+
+ private
+
+ def handle_errors_while(item_rep)
+ yield
+ rescue => e
+ raise Nanoc::Int::Errors::CompilationError.new(e, item_rep)
+ end
+
+ def compile_rep(rep, is_outdated:)
+ item_rep_compiler.run(rep, is_outdated: is_outdated)
+ end
+
+ def item_rep_compiler
+ @_item_rep_compiler ||= begin
+ recalculate_phase = Phases::Recalculate.new(
+ action_provider: @action_provider,
+ dependency_store: @dependency_store,
+ compilation_context: @compilation_context,
+ )
+
+ cache_phase = Phases::Cache.new(
+ compiled_content_cache: @compiled_content_cache,
+ wrapped: recalculate_phase,
+ )
+
+ resume_phase = Phases::Resume.new(
+ wrapped: cache_phase,
+ )
+
+ write_phase = Phases::Write.new(
+ wrapped: resume_phase,
+ )
+
+ mark_done_phase = Phases::MarkDone.new(
+ wrapped: write_phase,
+ outdatedness_store: @outdatedness_store,
+ )
+
+ mark_done_phase
+ end
+ end
end
end
include Nanoc::Int::ContractsSupport
@@ -223,57 +367,51 @@
attr_reader :outdatedness_checker
# @api private
attr_reader :reps
- def initialize(site, compiled_content_cache:, checksum_store:, rule_memory_store:, action_provider:, dependency_store:, outdatedness_checker:, reps:)
+ # @api private
+ attr_reader :outdatedness_store
+
+ def initialize(site, compiled_content_cache:, checksum_store:, rule_memory_store:, action_provider:, dependency_store:, outdatedness_checker:, reps:, outdatedness_store:)
@site = site
@compiled_content_cache = compiled_content_cache
@checksum_store = checksum_store
@rule_memory_store = rule_memory_store
@dependency_store = dependency_store
@outdatedness_checker = outdatedness_checker
@reps = reps
@action_provider = action_provider
+ @outdatedness_store = outdatedness_store
end
def run_all
- @action_provider.preprocess(@site)
+ preprocess_stage.run
build_reps
- prune
- run
- @action_provider.postprocess(@site, @reps)
- end
-
- def run
+ prune_stage.run
load_stores
- @site.freeze
-
- compile_reps
+ determine_outdatedness
+ forget_dependencies_if_needed
store
+ compile_reps
+ store_output_state
+ @action_provider.postprocess(@site, @reps)
ensure
Nanoc::Int::TempFilenameFactory.instance.cleanup(
Nanoc::Filter::TMP_BINARY_ITEMS_DIR,
)
Nanoc::Int::TempFilenameFactory.instance.cleanup(
Nanoc::Int::ItemRepWriter::TMP_TEXT_ITEMS_DIR,
)
end
def load_stores
- # FIXME: icky hack to update the dependency/checksum store’s list of objects
- # (does not include preprocessed objects otherwise)
- dependency_store.objects = site.items.to_a + site.layouts.to_a
- checksum_store.objects = site.items.to_a + site.layouts.to_a + site.code_snippets + [site.config]
-
stores.each(&:load)
end
- # Store the modified helper data used for compiling the site.
- #
- # @return [void]
+ # TODO: rename to store_preprocessed_state
def store
# Calculate rule memory
(@reps.to_a + @site.layouts.to_a).each do |obj|
rule_memory_store[obj] = action_provider.memory_for(obj).serialize
end
@@ -282,13 +420,18 @@
objects_to_checksum =
site.items.to_a + site.layouts.to_a + site.code_snippets + [site.config]
objects_to_checksum.each { |obj| checksum_store.add(obj) }
# Store
- stores.each(&:store)
+ checksum_store.store
+ rule_memory_store.store
end
+ def store_output_state
+ @dependency_store.store
+ end
+
def build_reps
builder = Nanoc::Int::ItemRepBuilder.new(
site, action_provider, @reps
)
builder.run
@@ -303,75 +446,66 @@
)
end
private
- def prune
- if site.config[:prune][:auto_prune]
- Nanoc::Pruner.new(site.config, reps, exclude: prune_config_exclude).run
- end
+ def preprocess_stage
+ @_preprocess_stage ||= Stages::Preprocess.new(
+ action_provider: action_provider,
+ site: site,
+ dependency_store: dependency_store,
+ checksum_store: checksum_store,
+ )
end
- def prune_config
- site.config[:prune] || {}
+ def prune_stage
+ @_prune_stage ||= Stages::Prune.new(
+ config: site.config,
+ reps: reps,
+ )
end
- def prune_config_exclude
- prune_config[:exclude] || {}
+ def determine_outdatedness_stage
+ @_determine_outdatedness_stage ||= Stages::DetermineOutdatedness.new(
+ reps: reps,
+ outdatedness_checker: outdatedness_checker,
+ outdatedness_store: outdatedness_store,
+ )
end
- def compile_reps
- outdated_items = @reps.select { |r| outdatedness_checker.outdated?(r) }.map(&:item).uniq
- outdated_items.each { |i| @dependency_store.forget_dependencies_for(i) }
+ def compile_reps_stage
+ @_compile_reps_stage ||= Stages::CompileReps.new(
+ outdatedness_store: @outdatedness_store,
+ dependency_store: @dependency_store,
+ action_provider: action_provider,
+ compilation_context: compilation_context,
+ compiled_content_cache: compiled_content_cache,
+ )
+ end
- reps_to_recompile = Set.new(outdated_items.flat_map { |i| @reps[i] })
- selector = Nanoc::Int::ItemRepSelector.new(reps_to_recompile)
- selector.each do |rep|
- handle_errors_while(rep) { compile_rep(rep, is_outdated: reps_to_recompile.include?(rep)) }
+ def determine_outdatedness
+ determine_outdatedness_stage.run do |outdated_items|
+ @outdated_items = outdated_items
end
end
- def handle_errors_while(item_rep)
- yield
- rescue => e
- raise Nanoc::Int::Errors::CompilationError.new(e, item_rep)
+ def forget_dependencies_if_needed
+ @outdated_items.each { |i| @dependency_store.forget_dependencies_for(i) }
end
- def compile_rep(rep, is_outdated:)
- item_rep_compiler.run(rep, is_outdated: is_outdated)
+ def compile_reps
+ compile_reps_stage.run
end
- def item_rep_compiler
- @_item_rep_compiler ||= begin
- recalculate_phase = RecalculatePhase.new(
- action_provider: action_provider,
- dependency_store: @dependency_store,
- compilation_context: compilation_context,
- )
-
- cache_phase = CachePhase.new(
- compiled_content_cache: compiled_content_cache,
- wrapped: recalculate_phase,
- )
-
- resume_phase = ResumePhase.new(
- wrapped: cache_phase,
- )
-
- WritePhase.new(
- wrapped: resume_phase,
- )
- end
- end
-
# Returns all stores that can load/store data that can be used for
# compilation.
def stores
[
checksum_store,
compiled_content_cache,
@dependency_store,
rule_memory_store,
+ @outdatedness_store,
]
end
end
end