Ruby Skript asm.rb
Einerseits möchte man in Ruby wiederverwendbaren Code schreiben, lagert also Klassen in extra Dateien aus, die man dann als Bibliothek nachlädt. Andererseits ist es oft nervig und hinderlich, wenn man mehrere Dateien installieren muss, um ein kleines Skript auf einem Rechner verfügbar zu machen.
Ich hab mir deshalb ein Skript geschrieben, welches alle Quellcode-Dateien zu einer Programmdatei zusammenfügt, die man dann ideal verteilen kann.
Das Konzept: Bei der Entwicklung und beim Test werden wie bisher alle selbstgeschriebenen Klassenbibliotheken mit require in das Anwendungs-Skript geholt. Jedoch werden alle require-Zeilen, die später in eine Datei zusammengefasst werden sollen, mit einem #include markiert.
require 'csv' require 'myclasslib/dbg' #include require 'myclasslib/string_ext' #include # Skriptcode...
Wenn das Skript nun freigegeben ist und verteilt werden soll, assembliert man alle Teile.
ruby asm.rb MeinSkript.rb > MeinSkript
Das asm.rb durchläuft MeinSkript.rb und ersetzt alle require Zeilen, die mit #include markiert sind, durch den Inhalt der Bibliotheks-Dateien. Heraus kommt eine Datei, die den ganzen Code enthält. Es werden sozusagen alle Einzelteile zusammengebaut, ähnlich wie beim statischen Linken bei C-Programmen.
Quellcode asm.rb
Der Quellcode wurde getestet unter Linux und Windows 2000 mit Ruby 1.6.8 und Ruby 1.8.2. Neben der Benutzung also unabhängiges Werkzeug, kann es auch als Bibliothek in ein eigenes Skript eingebunden werden.
Changelog
- 2005/07/28 Shebang Ersetzung
- 2005/02/26 Ersetzung __FILE__ durch Library-Filenamen, abschneiden alles hinter __END__, Code aufgeräumt
- 2005/02/25 Initial Release
Patch: 2005/07/28 Shebang Ersetzung
Das bisherige Skript setzt automatisch eine Default-Shebang-Zeile ein (#!/usr/bin/env ruby). Eine kleine Erweiterung sorgt dafür, dass die Shebang Zeile des Originalskripts genommen wird, falls vorhanden. Die Methode self.insert_file muss wie folgt gepatcht werden:
def self.insert_file( file, root )
error( "File #{file} not found." ) if !File.file?(file)
# --- BEGIN Patch
# First line Shebang? -> replace default-Shebang
if root
File.foreach(file) do |firstline|
if firstline =~ /^\#\!/
@@out[0] = firstline.chomp
end
break
end
end
# --- END Patch
File.foreach(file) do |line|
...
Version: 2005/02/26
#!/usr/bin/env ruby
# Winfried Mueller, www.reintechnisch.de, Copyright, 2004, GPL
# Release 2005/02/26
#
# asm.rb - assemble the main file and all #include requires to one file
# Use: ruby asm.rb infile > out
class Asm
@@included = Hash.new
@@out = Array.new
@@header = Array.new
def self.clear
@@out = Array.new
@@included = Hash.new
@@header = Array.new
end
def self.out
return @@out
end
def self.to_s
@@out.join( "\n" )
end
# insert all required files with #include directive
# e.g. require 'mylib/lib1' #include
def self.insert_file( file, root )
error( "File #{file} not found." ) if !File.file?(file)
File.foreach(file) do |line|
# include all lines with '#include' at end of line
rxp_req_include = /^\s*require\s*["'](.*?)["']\s*#\s*include/
if line =~ rxp_req_include
libfile = $1
# file is already included
if @@included[libfile] == true
next
end
# include file
@@included[libfile] = true
out_begin_require_header( libfile )
libfile_full = find_lib_file( libfile ) ||
error( "File #{libfile} not found." )
insert_file( libfile_full, false )
out_end_require_header( libfile )
else
line.chomp!
# replace __FILE__ in included libs
# cut all after __END__ in included libs
if !root
line.gsub!( /__FILE__/, "'" << File.basename( file ) << "'" )
if line =~ /^__END__/
return
end
end
@@out << line
end
end
end
# add a line to global header
def self.add_header( str )
@@header << str.chomp
end
# generate global header buffered in @@out
def self.out_header
@@out << "#!/usr/bin/env ruby"
@@out << "#"
@@out << "# This file is automatically generated. DO NOT MODIFY!"
@@out << "#"
@@out << ""
@@header.each do |line|
@@out << line
end
end
def self.out_begin_require_header( libfile )
@@out << ""
@@out << "# ========== begin include #{libfile} =========="
@@out << ""
end
private_class_method :out_begin_require_header
def self.out_end_require_header( libfile )
@@out << "# ========== end include #{libfile} =========="
@@out << ""
end
private_class_method :out_end_require_header
# search in all libdirs for libfile
def self.find_lib_file( libfile )
filename_full = nil
$:.each do |libdir|
f = File.join(libdir, libfile + ".rb")
if File.file?( f )
filename_full = f
break
end
end
return filename_full
end
private_class_method :find_lib_file
def self.error( msg )
$stderr.puts "Error: " << msg
raise msg
end
private_class_method :error
end
if __FILE__ == $0
main_file = $*[0]
Asm::out_header
Asm::insert_file( main_file, true )
puts Asm::to_s
exit (0)
end
Version: 2005/02/25
#!/usr/bin/env ruby
# Winfried Mueller, www.reintechnisch.de, Copyright, 2004, GPL
# Release 2005/02/25
#
# asm.rb - assemble the main file and all #include requires to one file
# Use: ruby asm.rb infile > out
class Asm
@@included = Hash.new
@@out = Array.new
@@header = Array.new
def self.clear
@@out = Array.new
@@included = Hash.new
@@header = Array.new
end
def self.out
return @@out
end
def self.to_s
@@out.join( "\n" )
end
def self.insert_file( file )
File.foreach(file) do |line|
file = nil
# include all lines with '#include' at end of line
if line =~ /^\s*require\s*["'](.*?)["']\s*#\s*include/
file = $1
if @@included[file] == true
next
end
@@included[file] = true
@@out << ""
@@out << "# ========== begin include #{$1} =========="
@@out << ""
included = false
$:.each do |libdir|
ffile = File.join(libdir, file + ".rb")
if File.file?( ffile )
self.insert_file( ffile )
included = true
break
end
end
if !included
$stderr.puts "File #{file} not found."
raise
end
@@out << "# ========== end include #{file} =========="
@@out << ""
else
@@out << line.chomp
end
end
end
def self.add_header( str )
@@header << str.chomp
end
def self.out_header
@@out << "#!/usr/bin/env ruby"
@@out << "#"
@@out << "# This file is automatically generated. DO NOT MODIFY!"
@@out << "#"
@@out << ""
@@header.each do |line|
@@out << line
end
end
end
if __FILE__ == $0
main_file = $*[0]
if !File.file?(main_file)
$stderr.puts "Error: Can't find file #{main_file}"
exit (-1)
end
Asm::out_header
Asm::insert_file( main_file )
puts Asm::to_s
exit (0)
end