RCtool is a small tool which makes/updates rcfiles automatically.
Latest Version 1.1.1
`set_filenames' bugfix on Windows.
New methodology to incorporate RCtool.
RCtool is a small tool which makes/updates rcfiles automatically.
There is a rcfile in software each. For example, it is .zshrc in zsh, .emacs in Emacs and so on.
Please think about the scene which introduces a new EmacsLisp program into Emacs. You copy a .el file in the directory which is in load-path. You have to write setting in .emacs last.
I think that update of .emacs is easy if you introduce it once. However, please imagine a scene upgrading an EmacsLisp program. When the EmacsLisp code is largely modified, and setting did not have compatibility either, a user must modify .emacs by hand one by one. Changing .emacs by hand is boring and error-prone; changing .emacs automatically without any notification is extremely rude.
RCtool takes over this troublesome work. RCtool does the following work separately.
A user understands which part changed clearly.
A developer modifies the definition script and can offer appropriate rcfiles to a user.
RCtool needs Ruby. RCtool works in Debian GNU/Linux and WINE. I think RCtool works in any Unices and Windows.
It is very easy to use RCtool as a user. A necessary tool is only Ruby.
It is assumed that a software RCtool is incorporated is installed and there is a definition script `foo-rctool' in that.
foo-rctool -p
foo-rctool -d
foo-rctool -i
foo-rctool -u
foo-rctool
That's all!
You MUST NOT MODIFY the block(a region between `Beginning of the foo block:' and `End of the foo block.') including the comment. A change result is lost if you have modified the block when you invoke RCtool again. When you want to change an initial value, please overwrite with a value below the block.
But you can MOVE A BLOCK ENTIRELY in an arbitrary place. When you do not like a place of a block inserted in RCtool, please move to an appropriate part. When you invoke RCtool next time, RCtool finds the block and modify it adequately.
If you develop a program with rcfiles, let's incorporate RCtool.
Please execute the following commands.
ruby -ropen-uri -e 'URI("http://www.rubyist.net/~rubikitch/archive/rctool-1.1.1.tar.gz").read.display' > rctool-1.1.1.tar.gz
tar xzvf rctool-1.1.1.tar.gz
When you failed, please download it from the next link.
Execute setup.rb to install RCtool.
ruby setup.rb
Since version 1.1.0, RCtool uses new methodology to incorporate into your system.
It is assumed the following situation.
mkrctool generates an integrated script.
mkrctool foo-rctool.rb > bin/foo-rctool
Of course if you modify `foo-rctool.rb', you must update `bin/foo-rctool'. Make or Rake takes over such a cumbersome task.
In explanation of RCtool, I define the following concepts.
Here, I decide to point at one part of contents of rcfiles.
I call a code piece added/updated by invoking RCtool a block. When a name of a block is foo, a block begins from
Beginning of the foo block:
and ends after
End of the foo block.
When there is not an rcfile, RCtool creates an rcfile. It is a template that define contents of front and back of a block. A block is expressed with "%s" in the template inside. (IO#printf is called)
A part under a block is a user-setting area. It is used to override the setting defined by a block.
RCtool Patch is an information that contains an rcfile name and a block and a template. RCtool Patch is unrelated to a diff/patch command. Please think that RCtool patches an rcfile by an original method.
Next, write a RCtool "definition script" if you installed RCtool. A definition script is a pure Ruby script.
It is 2 of next to do in a definition script.
RCtool.new(params)This method makes an RCtool object.
params is a Hash.
A key of params and correspondence of a value are as follows.
By normal use: you are enough if you define even :name.
RCtool#define_patch(file, block, comment_start, template="%s", where=:append)This method defines an RCtool patch to an rcfile file.
comment_start is a comment start character string.
A block is put at the end of a file if where == :append.
A block is put at the top of a file if where == :prepend.
The private methods that RCtool defines are only these. It is work of a definition script that computes a default value of setting (in other words, contents of a block).
About concrete how to use RCtool, it is early to watch an example of a program introducing RCtool into. El4r modifies ~/.emacs, ~/.el4rrc.rb, ~/.el4r/init.rb with RCtool.
Here is the definition script, el4r-rctool. It is long, but script in itself is simple because most are here document string.
# (shell-command "rm ~/.el4rrc.rb")
# (progn (find-sh "rake rctool; el4r-rctool -p ; el4r-rctool -d; el4r-rctool -i") (find-filez "~/.el4rrc.rb ~/.emacs ~/.el4r/init.rb"))
# (find-sh "ruby ~/.el4rrc.rb")
require 'tmpdir'
require 'rbconfig'
include Config
home_dir_expr = %q!ENV['EL4R_HOME'] || File.expand_path("~/.el4r")!
home_dir = eval home_dir_expr
bindir = CONFIG["bindir"]
datadir = CONFIG["datadir"]
sitelibdir = CONFIG["sitelibdir"]
emacsrubydir = File.join(sitelibdir, "el4r/emacsruby")
el_program = File.expand_path('emacs/site-lisp/el4r.el', datadir)
el_dir = File.dirname el_program
rc = RCtool.new(:name=>"el4r")
rc.define_patch(".el4rrc.rb", <<END_OF_BLOCK, "#", <<END_OF_TEMPLATE, :prepend)
### Internal variables
@stdlib_dir = #{emacsrubydir.dump}
@autoload_dir = #{File.join(emacsrubydir, "autoload").dump}
@el_program_relative = "data/emacs/site-lisp/el4r.el"
@instance_program_relative = "bin/el4r-instance"
@el_program = #{el_program.dump}
@instance_program = #{File.expand_path('el4r-instance', bindir).dump}
@lisp_object_gc_trigger_count = 100
@lisp_object_gc_trigger_increment = 100
@ruby_gc_trigger_count = 100
@ruby_gc_trigger_increment = 100
@log_buffer = "*el4r:log*"
@output_buffer = "*el4r:output*"
@unittest_lisp_object_gc_trigger_count = 5000
@unittest_lisp_object_gc_trigger_increment = 5000
@unittest_ruby_gc_trigger_count = 5000
@unittest_ruby_gc_trigger_increment = 5000
@temp_file = "#{Dir.tmpdir}/el4r-\#{ENV['USER'] || ENV['USERNAME'] || 'me'}.tmp"
### El4r bootstrap code
def __conf__
if ENV['EL4R_ROOT']
$: << File.join(ENV['EL4R_ROOT'], "lib")
end
require 'el4r/el4r-sub'
ConfigScript.new(__FILE__)
end
def __elisp_init__
$> << "(setq \\n"
instance_variables.map{|iv| [iv[1..-1], instance_variable_get(iv)]}.each {|k,v| $> << "el4r-\#{k.gsub(/_/,'-')} \#{v.inspect}\\n" if Numeric === v or String === v}
$> << ')' << "\n"
end
at_exit { __elisp_init__ if __FILE__==$0 }
### Customizable variables
### You can override these variables in User-setting area.
# directory containing EmacsRuby scripts
@home_dir = #{home_dir_expr}
# directory containing other package's EmacsRuby scripts
@site_dir = "\#{@home_dir}/site"
# startup EmacsRuby script
@init_script = "init.rb"
# EmacsRuby search path
@el4r_load_path = [ @home_dir, @site_dir, @stdlib_dir, "." ]
END_OF_BLOCK
%s
# Ruby interpreter name used by el4r
@ruby_program = "ruby"
# Emacs program name used by el4r / el4r-runtest.rb
@emacs_program = "emacs"
END_OF_TEMPLATE
rc.define_patch(".emacs", <<END_OF_BLOCK, ";;", "%s", :append)
(add-to-list 'load-path #{el_dir.dump})
(require 'el4r)
(el4r-boot)
END_OF_BLOCK
rc.define_patch("#{home_dir}/init.rb", <<END_OF_BLOCK, "#", "%s", :prepend)
# This is the el4r initialization file.
END_OF_BLOCK
At first rc is an RCtool object.
rc = RCtool.new(:name=>"el4r")
The next line defines an RCtool patch of .el4rrc.rb.
rc.define_patch(".el4rrc.rb", <<END_OF_BLOCK, "#", <<END_OF_TEMPLATE, :prepend)
Because :home_dir is not specified, .el4rrc.rb is a relative path from a home directory.
Because el4rrc.rb is a Ruby script, comment character string is #.
A block ends at END_OF_BLOCK.
This is a part updated for el4r-rctool run time.
In the block, because it contains the line
root_dir = #{Dir.pwd.dump}
contents of a block vary with current directory.
A template begins from the next line of END_OF_BLOCK.
It is over before END_OF_TEMPLATE.
*1
When ~/.el4rrc.rb does not exist, the template is used. Because it is different by an individual, a name of a Ruby interpreter and a name of Emacs define it as a template. A user may modify these values freely.
@ruby_program = "ruby" @emacs_program = "emacs"
When ~/.el4rrc.rb exists because :prepend is specified, a block comes to the top of a file.
There is a definition of a patch of ~/.emacs next.
rc.define_patch(".emacs", <<END_OF_BLOCK, ";;", "%s", :append)
Because :append is specified, a block is added to the end of ~/.emacs.
Notice that the comment start character string is ";;", and the template is "%s".
There is a definition of a patch of an EmacsRuby script executed at the startup of el4r.
rc.define_patch("#{home_dir}/init.rb", <<END_OF_BLOCK, "#", "%s", :prepend)
A part of a file name becomes complicated because it is changed in environment variable EL4R_HOME.
This example shows that the patch file name can be specified by an absolute path.
The actual installation of el4r shows how el4r-rctool modifies rcfiles.
*1In Ruby, You can define a here document in succession.