Incremental regeneration

This commit is contained in:
Alfred Xing 2014-11-15 22:47:22 -08:00
parent cb8a4b4d62
commit 11917645f2
10 changed files with 139 additions and 6 deletions

View File

@ -48,6 +48,7 @@ module Jekyll
autoload :Layout, 'jekyll/layout' autoload :Layout, 'jekyll/layout'
autoload :LayoutReader, 'jekyll/layout_reader' autoload :LayoutReader, 'jekyll/layout_reader'
autoload :LogAdapter, 'jekyll/log_adapter' autoload :LogAdapter, 'jekyll/log_adapter'
autoload :Metadata, 'jekyll/metadata'
autoload :Page, 'jekyll/page' autoload :Page, 'jekyll/page'
autoload :PluginManager, 'jekyll/plugin_manager' autoload :PluginManager, 'jekyll/plugin_manager'
autoload :Post, 'jekyll/post' autoload :Post, 'jekyll/post'

View File

@ -21,7 +21,14 @@ module Jekyll
# #
# Returns an Array of the file and directory paths # Returns an Array of the file and directory paths
def obsolete_files def obsolete_files
(existing_files - new_files - new_dirs + replaced_files).to_a (existing_files - new_files - new_dirs + replaced_files + metadata_file).to_a
end
# Private: The metadata file storing dependency tree and build history
#
# Returns an Array with the metdata file as the only item
def metadata_file
[site.metadata.metadata_file]
end end
# Private: The list of existing files, apart from those included in keep_files and hidden files. # Private: The list of existing files, apart from those included in keep_files and hidden files.

View File

@ -58,6 +58,7 @@ module Jekyll
c.option 'unpublished', '--unpublished', 'Render posts that were marked as unpublished' c.option 'unpublished', '--unpublished', 'Render posts that were marked as unpublished'
c.option 'quiet', '-q', '--quiet', 'Silence output.' c.option 'quiet', '-q', '--quiet', 'Silence output.'
c.option 'verbose', '-V', '--verbose', 'Print verbose output.' c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
c.option 'clean', '-c', '--clean', 'Clean the site before rebuilding.'
end end
end end

View File

@ -207,6 +207,12 @@ module Jekyll
info, info,
File.join(site.config['layouts'], layout.name)) File.join(site.config['layouts'], layout.name))
# Add layout to dependency tree
site.metadata.add_dependency(
Jekyll.sanitized_path(site.source, path),
Jekyll.sanitized_path(site.source, layout.path)
)
if layout = layouts[layout.data["layout"]] if layout = layouts[layout.data["layout"]]
if used.include?(layout) if used.include?(layout)
layout = nil # avoid recursive chain layout = nil # avoid recursive chain

View File

@ -8,6 +8,9 @@ module Jekyll
# Gets the name of this layout. # Gets the name of this layout.
attr_reader :name attr_reader :name
# Gets the path to this layout.
attr_reader :path
# Gets/Sets the extension of this layout. # Gets/Sets the extension of this layout.
attr_accessor :ext attr_accessor :ext
@ -26,6 +29,7 @@ module Jekyll
@site = site @site = site
@base = base @base = base
@name = name @name = name
@path = Jekyll.sanitized_path(site.source, File.join(base, name))
self.data = {} self.data = {}

87
lib/jekyll/metadata.rb Normal file
View File

@ -0,0 +1,87 @@
require 'set'
module Jekyll
class Metadata
attr_reader :site, :metadata
def initialize(site)
@site = site
# Initialize metadata store by reading YAML file,
# or an empty hash if file does not exist
@metadata = (File.file?(metadata_file) && !(site.config['clean'])) ? SafeYAML.load(File.read(metadata_file)) : {}
# Initialize cache to an empty hash
@cache = {}
end
# Add a path to the metadata
#
# Returns true.
def add(path)
@metadata[path] = {
"mtime" => File.mtime(path),
"deps" => []
}
@cache[path] = true
end
# Force a path to regenerate
#
# Returns true.
def force(path)
@cache[path] = true
end
# Checks if a path should be regenerated
#
# Returns a boolean.
def regenerate?(path)
# Check for path in cache
if @cache.has_key? path
return @cache[path]
end
# Check path that exists in metadata
if (data = @metadata[path])
data["deps"].each do |dependency|
if regenerate?(dependency)
return @cache[dependency] = @cache[path] = true
end
end
if data["mtime"] == File.mtime(path)
return @cache[path] = false
else
return add(path)
end
end
# Path does not exist in metadata, add it
return add(path)
end
# Add a dependency of a path
#
# Returns true.
def add_dependency(path, dependency)
@metadata[path]["deps"] << dependency unless @metadata[path]["deps"].include? dependency
add(dependency)
end
# Write the metadata to disk
#
# Returns nothing.
def write
File.open(metadata_file, 'w') do |f|
f.write(@metadata.to_yaml)
end
end
# Produce the absolute path of the metadata file
#
# Returns the String path of the file.
def metadata_file
Jekyll.sanitized_path(site.source, '.jekyll-metadata')
end
end
end

View File

@ -138,6 +138,12 @@ module Jekyll
File.join(site.config['layouts'], layout.name) File.join(site.config['layouts'], layout.name)
) )
# Add layout to dependency tree
site.metadata.add_dependency(
Jekyll.sanitized_path(site.source, document.path),
Jekyll.sanitized_path(site.source, layout.path)
)
if layout = site.layouts[layout.data["layout"]] if layout = site.layouts[layout.data["layout"]]
if used.include?(layout) if used.include?(layout)
layout = nil # avoid recursive chain layout = nil # avoid recursive chain

View File

@ -11,6 +11,7 @@ module Jekyll
:gems, :plugin_manager :gems, :plugin_manager
attr_accessor :converters, :generators attr_accessor :converters, :generators
attr_accessor :metadata
# Public: Initialize a new Site. # Public: Initialize a new Site.
# #
@ -27,6 +28,9 @@ module Jekyll
@source = File.expand_path(config['source']).freeze @source = File.expand_path(config['source']).freeze
@dest = File.expand_path(config['destination']).freeze @dest = File.expand_path(config['destination']).freeze
# Build metadata
@metadata = Metadata.new(self)
self.plugin_manager = Jekyll::PluginManager.new(self) self.plugin_manager = Jekyll::PluginManager.new(self)
self.plugins = plugin_manager.plugins_path self.plugins = plugin_manager.plugins_path
@ -49,7 +53,7 @@ module Jekyll
read read
generate generate
render render
cleanup cleanup if config['clean']
write write
end end
@ -289,13 +293,17 @@ module Jekyll
collections.each do |label, collection| collections.each do |label, collection|
collection.docs.each do |document| collection.docs.each do |document|
document.output = Jekyll::Renderer.new(self, document).run if @metadata.regenerate?(document.path)
document.output = Jekyll::Renderer.new(self, document).run
end
end end
end end
payload = site_payload payload = site_payload
[posts, pages].flatten.each do |page_or_post| [posts, pages].flatten.each do |page_or_post|
page_or_post.render(layouts, payload) if @metadata.regenerate?(Jekyll.sanitized_path(source, page_or_post.relative_path))
page_or_post.render(layouts, payload)
end
end end
rescue Errno::ENOENT => e rescue Errno::ENOENT => e
# ignore missing layout dir # ignore missing layout dir
@ -312,7 +320,12 @@ module Jekyll
# #
# Returns nothing. # Returns nothing.
def write def write
each_site_file { |item| item.write(dest) } each_site_file { |item|
if @metadata.regenerate? Jekyll.sanitized_path(source, item.path)
item.write(dest)
end
}
@metadata.write
end end
# Construct a Hash of Posts indexed by the specified Post attribute. # Construct a Hash of Posts indexed by the specified Post attribute.

View File

@ -105,13 +105,20 @@ eos
end end
def render(context) def render(context)
site = context.registers[:site]
dir = resolved_includes_dir(context) dir = resolved_includes_dir(context)
file = render_variable(context) || @file file = render_variable(context) || @file
validate_file_name(file) validate_file_name(file)
path = File.join(dir, file) path = File.join(dir, file)
validate_path(path, dir, context.registers[:site].safe) validate_path(path, dir, site.safe)
# Add include to dependency tree
site.metadata.add_dependency(
Jekyll.sanitized_path(site.source, context.registers[:page]["path"]),
path
)
begin begin
partial = Liquid::Template.parse(source(path, context)) partial = Liquid::Template.parse(source(path, context))

1
site/.gitignore vendored
View File

@ -2,3 +2,4 @@ _site/
*.swp *.swp
pkg/ pkg/
test/ test/
.jekyll-metadata