class Epuber::Compiler

Constants

EPUB_CONTENT_FOLDER

Attributes

compilation_context[R]

@return [CompilationContext]

file_resolver[R]

@return [Epuber::Compiler::FileResolver]

Public Class Methods

globals_tracker() click to toggle source

@return [Bade::Runtime::GlobalsTracker]

# File lib/epuber/compiler.rb, line 29
def self.globals_tracker
  @globals_tracker ||=
    Bade::Runtime::GlobalsTracker.new(constants_location_prefixes: [Config.instance.project_path])
end
new(book, target) click to toggle source

@param [Epuber::Book::Book] book @param [Epuber::Book::Target] target

# File lib/epuber/compiler.rb, line 45
def initialize(book, target)
  @book = book
  @target = target
  @compilation_context = CompilationContext.new(book, target)
end

Public Instance Methods

archive(path = nil, configuration_suffix: nil) click to toggle source

Archives current target files to epub

@param [String] path path to created archive

@return [String] path

# File lib/epuber/compiler.rb, line 122
def archive(path = nil, configuration_suffix: nil)
  path ||= epub_name(configuration_suffix)

  epub_path = File.expand_path(path)

  Dir.chdir(@file_resolver.destination_path) do
    new_paths = @file_resolver.package_files.map(&:pkg_destination_path)

    if ::File.exist?(epub_path)
      Zip::File.open(epub_path, true) do |zip_file|
        old_paths = zip_file.instance_eval { @entry_set.entries.map(&:name) }
        diff = old_paths - new_paths
        diff.each do |file_to_remove|
          UI.debug "removing file from result EPUB: #{file_to_remove}"
          zip_file.remove(file_to_remove)
        end
      end
    end

    run_command(%(zip -q0X "#{epub_path}" mimetype))
    run_command(%(zip -qXr9D "#{epub_path}" "#{new_paths.join('" "')}" --exclude \\*.DS_Store))
  end

  path
end
compile(build_folder, check: false, write: false, release: false, verbose: false, use_cache: true) click to toggle source

Compile target to build folder

@param [String] build_folder path to folder, where will be stored all compiled files @param [Bool] check should run non-release checkers @param [Bool] write should perform transformations of source files and write them back @param [Bool] release this is release build

@return [void]

# File lib/epuber/compiler.rb, line 60
    def compile(build_folder, check: false, write: false, release: false, verbose: false, use_cache: true)
      @file_resolver = FileResolver.new(Config.instance.project_path, build_folder)
      compilation_context.file_resolver = @file_resolver
      compilation_context.should_check = check
      compilation_context.should_write = write
      compilation_context.release_build = release
      compilation_context.verbose = verbose
      compilation_context.use_cache = use_cache

      self.class.globals_tracker.catch do
        @build_folder = build_folder

        FileUtils.mkdir_p(build_folder)

        UI.info "  #{<<~MSG}"
          building target #{@target.name.inspect} (build dir: #{Config.instance.pretty_path_from_project(build_folder)})
        MSG

        file_resolver.add_file(FileTypes::SourceFile.new(Config.instance.pretty_path_from_project(@book.file_path)))
        compilation_context.plugins

        # validate bookspec
        compilation_context.perform_plugin_things(Checker, :bookspec) do |checker|
          checker.call(@book, compilation_context)
        end

        parse_toc_item(@target.root_toc)
        parse_target_file_requests

        process_all_target_files
        generate_other_files

        # run :after_all_text_files transformers
        compilation_context.perform_plugin_things(Transformer, :after_all_text_files) do |transformer|
          transformer.call(@book, compilation_context)
        end

        process_global_ids

        # build folder cleanup
        remove_unnecessary_files
        remove_empty_folders

        source_paths = file_resolver.files.select { |a| a.is_a?(FileTypes::SourceFile) }.map(&:source_path)
        compilation_context.source_file_database.cleanup(source_paths)
        compilation_context.source_file_database.update_all_metadata
        compilation_context.source_file_database.save_to_file

        compilation_context.target_file_database.cleanup(source_paths)
        compilation_context.target_file_database.update_all_metadata
        compilation_context.target_file_database.save_to_file
      end
    ensure
      self.class.globals_tracker.clear_all
    end
epub_name(configuration_suffix = nil) click to toggle source

Creates name of epub file for current book and current target

@return [String] name of result epub file

# File lib/epuber/compiler.rb, line 152
def epub_name(configuration_suffix = nil)
  epub_name = if !@book.output_base_name.nil?
                @book.output_base_name
              elsif @book.from_file?
                ::File.basename(@book.file_path, ::File.extname(@book.file_path))
              else
                @book.title
              end

  epub_name += @book.build_version.to_s unless @book.build_version.nil?
  epub_name += "-#{@target.name}" if @target != @book.default_target
  epub_name += "-#{configuration_suffix}" unless configuration_suffix.nil?
  "#{epub_name}.epub"
end

Private Instance Methods

generate_other_files() click to toggle source

@return nil

# File lib/epuber/compiler.rb, line 201
def generate_other_files
  # generate nav file (nav.xhtml or nav.ncx)
  nav_file = FileTypes::NavFile.new(@target.epub_version)
  @file_resolver.add_file(nav_file)
  process_file(nav_file)

  # generate .opf file
  opf_file = FileTypes::OPFFile.new
  @file_resolver.add_file(opf_file)
  process_file(opf_file)

  # generate mimetype file
  mimetype_file = FileTypes::MimeTypeFile.new
  @file_resolver.add_file(mimetype_file)
  process_file(mimetype_file)

  # generate META-INF files
  container_xml = FileTypes::ContainerXMLFile.new
  @file_resolver.add_file(container_xml)
  process_file(container_xml)

  return unless @target.epub_version >= 2.0 && @target.epub_version < 3.0

  ibooks_options = FileTypes::IBooksDisplayOptionsFile.new
  @file_resolver.add_file(ibooks_options)
  process_file(ibooks_options)
end
parse_target_file_requests() click to toggle source

@return nil

# File lib/epuber/compiler.rb, line 231
def parse_target_file_requests
  @target.files.each do |file_request|
    @file_resolver.add_file_from_request(file_request)
  end
end
parse_toc_item(toc_item) click to toggle source

@param [Epuber::Book::TocItem] toc_item

# File lib/epuber/compiler.rb, line 296
def parse_toc_item(toc_item)
  unless toc_item.file_request.nil?
    file = @file_resolver.add_file_from_request(toc_item.file_request, :spine)
    file.toc_item = toc_item if file.toc_item.nil?
  end

  # process recursively other files
  toc_item.sub_items.each do |child|
    parse_toc_item(child)
  end
end
process_all_target_files() click to toggle source

@return nil

# File lib/epuber/compiler.rb, line 285
def process_all_target_files
  @file_resolver.manifest_files.each_with_index do |file, idx|
    UI.start_processing_file(file, idx, @file_resolver.manifest_files.count)
    process_file(file)
  end

  UI.end_processing
end
process_file(file) click to toggle source

@param [Epuber::Compiler::FileTypes::AbstractFile] file

# File lib/epuber/compiler.rb, line 239
def process_file(file)
  file.compilation_context = compilation_context

  resolve_dependencies(file) if file.is_a?(FileTypes::SourceFile)
  file.process(compilation_context)

  file.compilation_context = nil
end
process_global_ids() click to toggle source
# File lib/epuber/compiler.rb, line 308
def process_global_ids
  UI.print_step_processing_time('Processing global ids') do
    xhtml_files = @file_resolver.files.select { |file| file.is_a?(FileTypes::XHTMLFile) }
    global_ids = validate_global_ids(xhtml_files)

    xhtml_files.each do |file|
      file.process_global_ids(compilation_context, global_ids)
    end
  end
end
remove_empty_folders() click to toggle source

@return nil

# File lib/epuber/compiler.rb, line 172
def remove_empty_folders
  Dir.chdir(@file_resolver.destination_path) do
    Dir.glob('**/*')
       .select { |d| File.directory?(d) }
       .select { |d| (Dir.entries(d) - %w[. ..]).empty? }
       .each do |d|
         UI.debug "removing empty folder `#{d}`"
         Dir.rmdir(d)
       end
  end
end
remove_unnecessary_files() click to toggle source

@return nil

# File lib/epuber/compiler.rb, line 186
def remove_unnecessary_files
  unnecessary_paths = @file_resolver.unneeded_files_in_destination.map do |path|
    File.join(@file_resolver.destination_path, path)
  end
  unnecessary_paths.each do |path|
    if compilation_context.verbose?
      UI.debug "removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`"
    end

    File.delete(path)
  end
end
resolve_dependencies(file) click to toggle source

@param [FileTypes::SourceFile] file

# File lib/epuber/compiler.rb, line 250
def resolve_dependencies(file)
  deps = file.find_dependencies

  # compute better paths for FileDatabase
  dirname = File.dirname(file.source_path)
  paths = deps.map { |relative| Config.instance.pretty_path_from_project(File.expand_path(relative, dirname)) }.uniq

  # add missing files to file_resolver
  paths.each do |path|
    next if file_resolver.file_with_source_path(path)

    file_resolver.add_file(FileTypes::SourceFile.new(path))
  end

  # add .bookspec file
  paths += [Config.instance.pretty_path_from_project(@book.file_path).to_s]

  # add all activated plugin files
  paths += compilation_context.plugins.map do |plugin|
    plugin.files.map(&:source_path)
  end.flatten

  # add dependencies to databases
  source_db = compilation_context.source_file_database
  source_db.update_metadata(file.source_path) unless source_db.file_stat_for(file.source_path)
  source_db.add_dependency(paths, to: file.source_path)

  # add dependencies to databases
  target_db = compilation_context.target_file_database
  target_db.update_metadata(file.source_path) unless target_db.file_stat_for(file.source_path)
  target_db.add_dependency(paths, to: file.source_path)
end
run_command(cmd) click to toggle source

@param [String] cmd

@return [void]

@raise if the return value is not 0

# File lib/epuber/compiler.rb, line 350
def run_command(cmd)
  system(cmd)

  $stdout.flush
  $stderr.flush

  code = $CHILD_STATUS
  raise 'wrong return value' if code != 0
end
validate_global_ids(xhtml_files) click to toggle source

Validates duplicity of global ids in all files + returns map of global ids to files

@param [Array<FileTypes::XHTMLFile>] xhtml_files @return [Hash<String, FileTypes::XHTMLFile>]

# File lib/epuber/compiler.rb, line 324
def validate_global_ids(xhtml_files)
  map = {}
  xhtml_files.each do |file|
    file.global_ids.each do |id|
      if map.key?(id)
        message = "Duplicate global id `#{id}` in file `#{file.source_path}`."
        if compilation_context.release_build?
          UI.error!(message)
        else
          UI.warning("#{message} Will fail during release build.")
        end
      end

      map[id] = file
    end
  end

  map
end