rant-0.5.8/0000755000175000017500000000000010527254520012050 5ustar xavierxavierrant-0.5.8/bin/0000755000175000017500000000000010527254520012620 5ustar xavierxavierrant-0.5.8/bin/rant0000644000175000017500000000044210527253231013505 0ustar xavierxavier#! /usr/bin/env ruby # rant -- Ruby's ant # # Copyright (C) 2005 Stefan Lang # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. require 'rant/rantlib' exit Rant.run rant-0.5.8/bin/rant-import0000644000175000017500000000051210527253231015013 0ustar xavierxavier#! /usr/bin/env ruby # rant-import -- Create a monolithic rant script. # # Copyright (C) 2005 Stefan Lang # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. require 'rant/import' exit Rant::RantImport.run rant-0.5.8/doc/0000755000175000017500000000000010527254520012615 5ustar xavierxavierrant-0.5.8/doc/examples/0000755000175000017500000000000010527254520014433 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp/0000755000175000017500000000000010527254520015517 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp/c/0000755000175000017500000000000010527254520015741 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp/c/problem_1_1/0000755000175000017500000000000010527254520020041 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp/c/problem_1_1/another_test.c0000644000175000017500000000006410527253223022703 0ustar xavierxavier #include "another_test.h" void another_test() { } rant-0.5.8/doc/examples/c_cpp/c/problem_1_1/another_test.h0000644000175000017500000000012110527253223022702 0ustar xavierxavier #ifndef ANOTHER_TEST_H #define ANOTHER_TEST_H void another_test(void); #endif rant-0.5.8/doc/examples/c_cpp/c/problem_1_1/main.c0000644000175000017500000000026310527253223021131 0ustar xavierxavier #include #include "test.h" #include "another_test.h" int main(char argc, char** argv) { test(); another_test(); printf("Hello, world!\n"); return 0; } rant-0.5.8/doc/examples/c_cpp/c/problem_1_1/test.c0000644000175000017500000000004410527253223021161 0ustar xavierxavier #include "test.h" void test() { } rant-0.5.8/doc/examples/c_cpp/c/problem_1_1/test.h0000644000175000017500000000007110527253223021166 0ustar xavierxavier #ifndef TEST_H #define TEST_H void test(void); #endif rant-0.5.8/doc/examples/c_cpp/c/template.rf0000644000175000017500000000053510527253223020107 0ustar xavierxavier# Autogenerated. Do not modify! # Create additional tasks and make other modifications in custom.rf. task :run => "test" do sys "./test" end gen C::Dependencies gen Action do source "c_dependencies" end file "test" => sys["*.c"].sub_ext("o") do |t| sys "gcc -o #{t.name} #{t.prerequisites}" end source "custom.rf" if File.exist? "custom.rf" rant-0.5.8/doc/examples/c_cpp/c++/0000755000175000017500000000000010527254520016067 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp/c++/problem_1_1/0000755000175000017500000000000010527254520020167 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp/c++/problem_1_1/another_test.cpp0000644000175000017500000000006410527253223023371 0ustar xavierxavier #include "another_test.h" void another_test() { } rant-0.5.8/doc/examples/c_cpp/c++/problem_1_1/another_test.h0000644000175000017500000000006710527253223023041 0ustar xavierxavier #ifndef ANOTHER_TEST_H #define ANOTHER_TEST_H #endif rant-0.5.8/doc/examples/c_cpp/c++/problem_1_1/main.cpp0000644000175000017500000000026010527253223021614 0ustar xavierxavier #include #include "test.h" #include "another_test.h" using namespace std; int main(char argc, char** argv) { cout << "Hello, world!" << endl; return 0; } rant-0.5.8/doc/examples/c_cpp/c++/problem_1_1/test.cpp0000644000175000017500000000004410527253223021647 0ustar xavierxavier #include "test.h" void test() { } rant-0.5.8/doc/examples/c_cpp/c++/problem_1_1/test.h0000644000175000017500000000004710527253223021317 0ustar xavierxavier #ifndef TEST_H #define TEST_H #endif rant-0.5.8/doc/examples/c_cpp/c++/template.rf0000644000175000017500000000053710527253223020237 0ustar xavierxavier# Autogenerated. Do not modify! # Create additional tasks and make other modifications in custom.rf. task :run => "test" do sys "./test" end gen C::Dependencies gen Action do source "c_dependencies" end file "test" => sys["*.cpp"].sub_ext("o") do |t| sys "g++ -o #{t.name} #{t.prerequisites}" end source "custom.rf" if File.exist? "custom.rf" rant-0.5.8/doc/examples/c_cpp/root.rant0000644000175000017500000000177410527253223017400 0ustar xavierxavier import "md5" import "autoclean", "package/tgz", "c/dependencies" cpp_dirs = sys["c++/problem_*"] c_dirs = sys["c/problem_*"] all_dirs = cpp_dirs + c_dirs cpp_rf_template = "c++/template.rf" c_rf_template = "c/template.rf" desc "Run all C and C++ tests." task :run => all_dirs.map{ |f| "#{f}/run" } desc "Build all." task :build => all_dirs.map{ |f| "#{f}/test" } desc "Remove all autogenerated files." gen AutoClean gen Rule, '.o' => '.cpp' do |t| sys "g++ -c -o #{t.name} #{t.source}" end gen Rule, '.o' => '.c' do |t| sys "gcc -c -o #{t.name} #{t.source}" end desc "Create source package." gen Package::Tgz, "pkg/c_cpp", :files => sys["root.rant", "**/*.{c,cpp,h,rf}"] gen Action do cpp_dirs.each { |dir| make "#{dir}/sub.rant" => cpp_rf_template do |t| sys.cp t.source, t.name end } c_dirs.each { |dir| make "#{dir}/sub.rant" => c_rf_template do |t| sys.cp t.source, t.name end } subdirs cpp_dirs, c_dirs end # vim:ft=ruby rant-0.5.8/doc/examples/c_cpp_examples/0000755000175000017500000000000010527254520017415 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp_examples/c/0000755000175000017500000000000010527254520017637 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp_examples/c/problem_1_1/0000755000175000017500000000000010527254520021737 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp_examples/c/problem_1_1/another_test.c0000644000175000017500000000006410527253223024601 0ustar xavierxavier #include "another_test.h" void another_test() { } rant-0.5.8/doc/examples/c_cpp_examples/c/problem_1_1/another_test.h0000644000175000017500000000012110527253223024600 0ustar xavierxavier #ifndef ANOTHER_TEST_H #define ANOTHER_TEST_H void another_test(void); #endif rant-0.5.8/doc/examples/c_cpp_examples/c/problem_1_1/main.c0000644000175000017500000000026310527253223023027 0ustar xavierxavier #include #include "test.h" #include "another_test.h" int main(char argc, char** argv) { test(); another_test(); printf("Hello, world!\n"); return 0; } rant-0.5.8/doc/examples/c_cpp_examples/c/problem_1_1/test.c0000644000175000017500000000004410527253223023057 0ustar xavierxavier #include "test.h" void test() { } rant-0.5.8/doc/examples/c_cpp_examples/c/problem_1_1/test.h0000644000175000017500000000007110527253223023064 0ustar xavierxavier #ifndef TEST_H #define TEST_H void test(void); #endif rant-0.5.8/doc/examples/c_cpp_examples/c/template.rf0000644000175000017500000000065210527253223022005 0ustar xavierxavier# Autogenerated. Do not modify! # Create additional tasks and make other modifications in custom.rf. import "c/dependencies", "autoclean" task :run => "test" do sys "./test" end gen C::Dependencies gen Action do source "c_dependencies" end file "test" => sys["*.c"].sub_ext("o") do |t| sys "gcc -o #{t.name} #{t.prerequisites}" end gen AutoClean source "../../rule.rf" source "custom.rf" if File.exist? "custom.rf" rant-0.5.8/doc/examples/c_cpp_examples/c++/0000755000175000017500000000000010527254520017765 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp_examples/c++/problem_1_1/0000755000175000017500000000000010527254520022065 5ustar xavierxavierrant-0.5.8/doc/examples/c_cpp_examples/c++/problem_1_1/another_test.cpp0000644000175000017500000000006410527253223025267 0ustar xavierxavier #include "another_test.h" void another_test() { } rant-0.5.8/doc/examples/c_cpp_examples/c++/problem_1_1/another_test.h0000644000175000017500000000006710527253223024737 0ustar xavierxavier #ifndef ANOTHER_TEST_H #define ANOTHER_TEST_H #endif rant-0.5.8/doc/examples/c_cpp_examples/c++/problem_1_1/main.cpp0000644000175000017500000000026010527253223023512 0ustar xavierxavier #include #include "test.h" #include "another_test.h" using namespace std; int main(char argc, char** argv) { cout << "Hello, world!" << endl; return 0; } rant-0.5.8/doc/examples/c_cpp_examples/c++/problem_1_1/test.cpp0000644000175000017500000000004410527253223023545 0ustar xavierxavier #include "test.h" void test() { } rant-0.5.8/doc/examples/c_cpp_examples/c++/problem_1_1/test.h0000644000175000017500000000004710527253223023215 0ustar xavierxavier #ifndef TEST_H #define TEST_H #endif rant-0.5.8/doc/examples/c_cpp_examples/c++/template.rf0000644000175000017500000000065410527253223022135 0ustar xavierxavier# Autogenerated. Do not modify! # Create additional tasks and make other modifications in custom.rf. import "c/dependencies", "autoclean" task :run => "test" do sys "./test" end gen C::Dependencies gen Action do source "c_dependencies" end file "test" => sys["*.cpp"].sub_ext("o") do |t| sys "g++ -o #{t.name} #{t.prerequisites}" end gen AutoClean source "../../rule.rf" source "custom.rf" if File.exist? "custom.rf" rant-0.5.8/doc/examples/c_cpp_examples/Rantfile0000644000175000017500000000152110527253223021102 0ustar xavierxavier import "autoclean", "package/tgz" cpp_dirs = sys["c++/problem_*"] c_dirs = sys["c/problem_*"] all_dirs = cpp_dirs + c_dirs cpp_rf_template = "c++/template.rf" c_rf_template = "c/template.rf" desc "Run all C and C++ tests." task :run => all_dirs.map{ |f| "#{f}/run" } desc "Build all." task :build => all_dirs.map{ |f| "#{f}/test" } desc "Remove all autogenerated files." gen AutoClean source "rule.rf" desc "Create source package." gen Package::Tgz, "pkg/c_cpp_exercises", :files => sys["Rantfile", "**/*.{c,cpp,h,rf}"] gen Action do cpp_dirs.each { |dir| make "#{dir}/Rantfile" => cpp_rf_template do |t| sys.cp t.source, t.name end } c_dirs.each { |dir| make "#{dir}/Rantfile" => c_rf_template do |t| sys.cp t.source, t.name end } subdirs cpp_dirs, c_dirs end rant-0.5.8/doc/examples/c_cpp_examples/rule.rf0000644000175000017500000000022710527253223020715 0ustar xavierxaviergen Rule, '.o' => '.cpp' do |t| sys "g++ -c -o #{t.name} #{t.source}" end gen Rule, '.o' => '.c' do |t| sys "gcc -c -o #{t.name} #{t.source}" end rant-0.5.8/doc/examples/c_dependencies/0000755000175000017500000000000010527254520017363 5ustar xavierxavierrant-0.5.8/doc/examples/c_dependencies/include/0000755000175000017500000000000010527254520021006 5ustar xavierxavierrant-0.5.8/doc/examples/c_dependencies/include/hello.h0000644000175000017500000000010310527253223022253 0ustar xavierxavier #ifndef HELLO_H #define HELLO_H #define RECEIVER "world" #endif rant-0.5.8/doc/examples/c_dependencies/include/util.h0000644000175000017500000000010110527253223022123 0ustar xavierxavier #ifndef UTIL_H #define UTIL_H void println(char* msg); #endif rant-0.5.8/doc/examples/c_dependencies/src/0000755000175000017500000000000010527254520020152 5ustar xavierxavierrant-0.5.8/doc/examples/c_dependencies/src/main.c0000644000175000017500000000017610527253223021245 0ustar xavierxavier #include "util.h" #include "hello.h" int main(int argc, char** argv) { println("Hello, " RECEIVER "!"); return 0; } rant-0.5.8/doc/examples/c_dependencies/src/util.c0000644000175000017500000000014610527253223021273 0ustar xavierxavier #include "util.h" #include void println(char* msg) { printf(msg); printf("\n"); } rant-0.5.8/doc/examples/c_dependencies/Rantfile0000644000175000017500000000143410527253223021053 0ustar xavierxavier import %w(c/dependencies clean autoclean) desc "Compile hello world program." file "hello" => %w(src/main.o src/util.o) do |t| sys "cc -Wall -o #{t.name} #{sys.sp t.prerequisites}" end gen Rule, :o => :c do |t| sys "cc -Wall -c -Iinclude -o #{t.name} #{t.source}" end # Similar to the "makedepend" program: # Create dependencies between C/C++ source/header files by parsing them # for #include statements. The dependencies will be written to a file # called "c_dependencies". gen C::Dependencies, :search => "include" # Automatically make and load the dependency file before invoking any # task. gen Action do source "c_dependencies" end desc "Remove C compiler products." gen Clean var[:clean].include "**/*.o", "hello" desc "Remove all generated files." gen AutoClean, :distclean rant-0.5.8/doc/examples/directedrule/0000755000175000017500000000000010527254520017106 5ustar xavierxavierrant-0.5.8/doc/examples/directedrule/src_a/0000755000175000017500000000000010527254520020175 5ustar xavierxavierrant-0.5.8/doc/examples/directedrule/src_a/a_1.c0000644000175000017500000000000010527253223020766 0ustar xavierxavierrant-0.5.8/doc/examples/directedrule/src_a/a_2.c0000644000175000017500000000003610527253223021000 0ustar xavierxavier int main() { return 0; } rant-0.5.8/doc/examples/directedrule/src_b/0000755000175000017500000000000010527254520020176 5ustar xavierxavierrant-0.5.8/doc/examples/directedrule/src_b/b_1.c0000644000175000017500000000000010527253223020770 0ustar xavierxavierrant-0.5.8/doc/examples/directedrule/Rantfile0000644000175000017500000000065410527253223020601 0ustar xavierxavier import %w(directedrule autoclean) desc "Build foo." file :foo => "obj/libfoo.a" do |t| sys "cc -o #{t.name} #{t.source}" end gen Directory, "obj" ro = gen DirectedRule, "obj" => sys["src_*"], :o => :c do |t| sys "cc -c -o #{t.name} #{t.source}" end file "obj/libfoo.a" => ro.candidates do |t| sys "ar cr #{t.name} #{sys.sp t.prerequisites}" sys "ranlib #{t.name}" end gen AutoClean, :clean # vim: ft=ruby rant-0.5.8/doc/examples/myprog/0000755000175000017500000000000010527254520015750 5ustar xavierxavierrant-0.5.8/doc/examples/myprog/src/0000755000175000017500000000000010527254520016537 5ustar xavierxavierrant-0.5.8/doc/examples/myprog/src/Rantfile0000644000175000017500000000042010527253223020221 0ustar xavierxavierfile "lib.o" => %w(lib.c lib.h) do sys "cc -c -o lib.o lib.c" end file "main.o" => "main.c" do sys "cc -c -o main.o main.c" end file "myprog" => %w(lib.o main.o) do sys "cc -o myprog main.o lib.o" end task :clean do sys.rm_f Dir["*.o"] + %w(myprog) end rant-0.5.8/doc/examples/myprog/src/lib.c0000644000175000017500000000006610527253223017452 0ustar xavierxavier#include "lib.h" int nothing(void) { return 0; } rant-0.5.8/doc/examples/myprog/src/lib.h0000644000175000017500000000006610527253223017457 0ustar xavierxavier#ifndef LIB_H #define LIB_H int nothing(void); #endif rant-0.5.8/doc/examples/myprog/src/main.c0000644000175000017500000000012310527253223017622 0ustar xavierxavier#include "lib.h" int main(int argc, char** argv) { nothing(); return 0; } rant-0.5.8/doc/examples/myprog/README0000644000175000017500000000006110527253223016624 0ustar xavierxavier This is the README file for the myprog program. rant-0.5.8/doc/examples/myprog/Rantfile0000644000175000017500000000043010527253223017433 0ustar xavierxavierdesc "Build myprog." file "myprog" => "src/myprog" do sys.cp "src/myprog", "myprog" end desc "Remove compiler products." task :clean => "src/clean" do sys.rm_f "myprog" end # Tell Rant to look in src for an Rantfile, # we could list more directories here. subdirs "src" rant-0.5.8/doc/homepage/0000755000175000017500000000000010527254520014402 5ustar xavierxavierrant-0.5.8/doc/homepage/index.html0000644000175000017500000001323710527253222016403 0ustar xavierxavier Rant

Rant - Flexible, Ruby based make

Similar to make, the rant command line tool reads a file called Rantfile, which contains task definitions. Unlike make, however, a Rantfile is just a valid script written in the Ruby programming language. Since Ruby comes with many useful libraries and is portable across many different operating systems, it's very easy to write portable Rantfiles.

Striking features:

  • Defining custom tasks.
  • Automated packaging, testing and RDoc generation for Ruby applications and libraries.
  • Creation of a monolithic script tailored to the needs of a specific project which can be used instead of an Rant installation - users don't need to install Rant.
  • Creating gzipped tar and zip archives -- without installing additional software.
  • Optional recognition of file changes based on MD5 checksums instead of file modification times.
  • Support for compiling C# sources and resources
  • Dependency checking for C/C++ source files (integrated makedepend replacement).

Getting started

1. Install Ruby
Issue the command: % ruby --version
which prints the Ruby version number you've installed. If it isn't equal or higher to 1.8.0, get a newer version from the Ruby homepage or the One-Click Installer for Windows.
2. Install Rant
Issue the command: % gem install --remote rant
which should download and install Rant. If you don't have the gem command, download the package from RubyForge, unpack it and run % ruby install.rb.
3. Read the documentation
If you want to start with the basics, continue here.
If you want to get an overview, continue here.

Valid XHTML 1.0!

rant-0.5.8/doc/homepage/rant_home.css0000644000175000017500000000507510527253222017075 0ustar xavierxavierbody { color:#333; background-color:white; /* background-color:gray; */ margin:20px; padding:0px; font:12px verdana, arial, helvetica, sans-serif; } h1 { margin:0px 0px 15px 0px; padding:0px; font-size:28px; font-weight:900; color:#ccc; } h2 { font:bold 14px/15px verdana, arial, helvetica, sans-serif; margin:0px 0px 5px 0px; padding:0px; } h3 { font:bold 12px/14px verdana, arial, helvetica, sans-serif; margin:0px 0px 5px 0px; padding:0px; } p { font:12px/20px verdana, arial, helvetica, sans-serif; margin:0px 0px 16px 0px; padding:0px; } .Content>p {margin:0px;} .Content>p+p {text-indent:30px;} a { color:#09c; font-size:12px; font-family:verdana, arial, helvetica, sans-serif; font-weight:600; text-decoration:none; } a:link {color:#09c;} a:visited {color:#07a;} a:hover {background-color:#eee;} /* All the content boxes belong to the content class. */ .content { position:relative; /* Position is declared "relative" to gain control of stacking order (z-index). */ width:auto; min-width:120px; margin:0px 210px 20px 170px; border:1px solid black; background-color:white; padding:10px; z-index:3; /* This allows the content to overlap the right menu in narrow windows in good browsers. */ } #navAlpha { position:absolute; /* width:150px; */ top:20px; left:20px; border:1px dashed black; background-color:#eee; padding:10px; z-index:2; /* Here is the ugly brilliant hack that protects IE5/Win from its own stupidity. Thanks to Tantek Celik for the hack and to Eric Costello for publicizing it. IE5/Win incorrectly parses the "\"}"" value, prematurely closing the style declaration. The incorrect IE5/Win value is above, while the correct value is below. See http://glish.com/css/hacks.asp for details. */ voice-family: "\"}\""; voice-family:inherit; /* width:128px; */ } /* I've heard this called the "be nice to Opera 5" rule. Basically, it feeds correct length values to user agents that exhibit the parsing error exploited above yet get the CSS box model right and understand the CSS2 parent-child selector. ALWAYS include a "be nice to Opera 5" rule every time you use the Tantek Celik hack (above). */ body>#navAlpha {width:128px;} #navBeta { position:absolute; width:190px; top:20px; right:20px; border:1px dashed black; background-color:#eee; padding:10px; z-index:1; /* Again, the ugly brilliant hack. */ voice-family: "\"}\""; voice-family:inherit; width:168px; } /* Again, "be nice to Opera 5". */ body>#navBeta {width:168px;} rant-0.5.8/doc/advanced.rdoc0000644000175000017500000002345610527253223015244 0ustar xavierxavier == Advanced Rantfiles === Sharing variables The +var+ command allows you to share variables between Rantfiles and to set variables from the commandline. Just use +var+ like a hash to set/get variables: var[:manifest] = %w(README Rantfile myprog.rb lib) In this example, :manifest is the variable name, which has to be a string or symbol. Symbols as variable names are converted to strings. Now you can access the "manifest" variable in every Rantfile of your project: file "MANIFEST" do |t| open(t.name, "w") { |f| var[:manifest].each { |str| f.puts(str) } } end Arguments of the form VAR=VAL to the rant command are also available through +var+: % cat Rantfile task :show_test do puts var[:test] end % rant nil % rant test=hello hello For some variables it is necessary to make them available for subprocesses, like CC or CFLAGS: var.env %w(CC CFLAGS) The variables CC and CFLAGS are available through +var+ as always and are mapped to environment variables. === Cleaning up generated files Use the +Clean+ generator in your Rantfiles: import "clean" file "junk" do # create junk end # create a task called clean desc "cleanup generated files" gen Clean # var[:clean] is a filelist object now var[:clean] << "junk" var[:clean].include "**/*.bak", "**/*.obj" === Let Rant cleanup for you Use the +AutoClean+ generator which will remove all files generated by any filetask (including those created by rules): import "autoclean" file "junk" do # create junk end gen Rule, :o => :c do |t| sys "cc -c -o #{t.name} #{t.source}" end desc "Cleanup generated files." gen AutoClean, :clean # The clean task automatically detects which files where created # by our rule and the junk task. # Additionally we can add files to remove to the variable with the # same name as the AutoClean taskname (here: clean): var[:clean].include "**/*.bak" TAKE CARE:: AutoClean will recursively remove directories for which a task exists. Meaning: gen Directory, "doc/html" AutoClean will recursively remove the doc directory! The Directory generator takes an optional base directory as first argument. Example: gen Directory, "doc", "html" Now Rant assumes that the "doc" directory already exists and creates only a task for the "doc/html" directory. If you run AutoClean now, it will only remove the "html" directory. The same goes for the SubFile generator: gen SubFile, "doc/html/index.html" do |t| # do something end This creates (amongst the other two tasks) a task for the "doc" directory, thus AutoClean will recursively unlink the "doc" directory. gen SubFile, "doc", "html/index.html" do |t| # do something end This doesn't create a task for the "doc" directory, thus AutoClean will only unlink the "doc/html" directory. === The DirectedRule generator A directed rule is some sort of special rule. It searches for source files in one or more given directories and produces file in one output directory. import "directedrule" ro = gen DirectedRule, "obj" => sys["src_*"], :o => :c do |t| sys "cc -c -o #{t.name} #{t.source}" end This rule produces a file task for targets in the obj/ directory ending in `.o'. It looks for a source file in all directories starting with `src_' and files ending in `.c'. Practically, this means that it compiles the C files in src_x/, src_y, ... to object files which are placed in the obj/ directory. Look in the doc/examples/directedrule directory of the Rant distribution for a small example project. === The SubFile generator A _SubFile_ is just a shortcut for the combination of a _Directory_ and a _file_ task. The following example without the SubFile generator: gen Directory, "backup" file "backup/data" => %w(data backup) do |t| sys.cp t.source, t.name end can be directly translated to: gen SubFile, "backup/data" => "data" do |t| sys.cp t.source, t.name end The SubFile generator automatically creates all necessary directory tasks and adds them as prerequisites to the final file task. === Constraining variables Rant allows you to constrain variables which are managed by the +var+ command (and thus can be set from the commandline): import "var/numbers" var :count, 0..10 This initializes the variable +count+ to 0 and restricts it to the integer range 0 to 10. Create a task to test it: task :show_count do puts var[:count] end And now try to set the count variable from the commandline: % rant 0 % rant count=5 5 % rant count=-1 rant: [ERROR] in file `/home/stefan/tmp/Rantfile', line 2: "-1" doesn't match constraint: integer 0..10 rant aborted! % rant count=100 rant: [ERROR] in file `/home/stefan/tmp/Rantfile', line 2: "100" doesn't match constraint: integer 0..10 rant aborted! Other available constraints: import "var/strings" # variable str is ensured to be a string var :str, :String import "var/booleans" # variable b is a bool (always true or false) # can be set to "yes", "no", "1", "0", "true", "false" var :b, :Bool === The Action generator Consider a C project. In some C source file, let's say config.h you define the project version, e.g.: #define VERSION 2.3 Many of your tools use this version number, so you have decided to duplicate it in a file called +version+, which contains just a line with the program version. One solution to automate the +version+ file creation would be to write a file task: file "version" => "config.h" do |t| puts "updating version file" open("w", t.name) { |f| f.puts(extract_config_version()) } end and make all other tasks that need this version dependent on it. But this can get very tedious if you have many tasks that need this version file. Another solution is to just run the task every time the Rantfile is sourced. This can be achieved by replacing the +file+ command with the +make+ command: make "version" => "config.h" do |t| puts "updating version file" open("w", t.name) { |f| f.puts(extract_config_version()) } end This creates a file task like before and tells rant to immediately invoke all tasks that are required to build the version file. But imagine your users just want to see the list of available tasks: % rant --tasks updating version file rant foo # build foo program rant lib # build libfoo.so rant clean # remove generated files Hmm, we really didn't need the version to show our users the available tasks. To avoid this, wrap such code in an Action: gen Action do make "version" => "config.h" do |t| puts "updating version file" open("w", t.name) { |f| f.puts(extract_config_version()) } end end And now on a clean source base: % rant --tasks rant foo # build foo program rant lib # build libfoo.so rant clean # remove generated files OK. Didn't confuse our users! Run any task: % rant foo updating version file cc -o foo foo.c This means, Action blocks are executed whenever we actually want to build something, not just extract information from our Rantfile. It is recommended to wrap any code that has effects on the environment (mainly the file system) inside an Action block instead of embedding it plain in the Rantfile. An Action block also won't be run when our Rantfile is read by rant-import. === More selective actions If a regular expression is given as argument to the Action generator, the block will be executed the first time Rant searches for a task matching this regular expression. An artifical example: import "sys/more" gen Action, /\.t$/ do puts "executing action for files/tasks ending in `.t'" source "t.rant" end file "t.rant" do |t| sys.write_to_file t.name, <<-EOF task "a.t" do puts "making a.t" end task "b.t" do puts "making b.t" end EOF end task :a do puts "making a" end task :default => ["a", "a.t", "b.t"] Running rant: % rant making a executing action for files/tasks ending in `.t' writing 128 bytes to file `t.rant' making a.t making b.t rant performed the following steps: 1. Invoke task +default+ 2. Invoke first prerequisite of +default+, which happens to be +a+. 3. Execute action block of task +a+. Output: "making a" 4. Search for a task for the second prerequisite of +default+, which happens to be a.t. 5. Since no task with the name a.t exists, it checks for a matching action/rule. The only defined action matches, so the action block is executed. Output: "executing action for files/tasks ending in `.t'" 6. The action block contains a +source+ statement, which will first cause a build of t.rant. Output: "writing 128 bytes to file `t.rant'" After the build, t.rant will be read as Rantfile. 7. The action block is done, the action is "deleted". 8. Now, Rant finds a task with the name a.t, invokes it and executes the associated ruby block. Output: "making a.t" 9. Invoke last prerequisite of +default+, which happens to be b.t and execute the associated ruby block. Output: "making b.t" Generally, an action is usable to autogenerate a (bigger) set of tasks that are needed by a subset of other tasks. == See also Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Support for C/C++:: doc/c.rdoc[link:files/doc/c_rdoc.html] Packaging:: doc/package.rdoc[link:files/doc/package_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/c.rdoc0000644000175000017500000000666210527253223013721 0ustar xavierxavier == Support for C/C++ Rant can automatically determine the dependencies between C/C++ source files. Use the C::Dependencies generator: import "c/dependencies" gen C::Dependencies This generates a task called "c_dependencies" which will scan all C/C++ source files in the current directory and all subdirectories for #include statements and write the dependencies to the file "c_dependencies". It searches for include files in the current directory. If you want to specify which directories it should search for include files, give the +search+ option: gen C::Dependencies, :search => %w(. include) Rant will search for include files in the current and in the "include" directory now. If you want to create the dependencies only for a specific set of source files or for source files with non-standard filename extensions, give a list with the +sources+ option: gen C::Dependencies, :sources => sys["**/*.cxx"] This task will create the dependencies for all files ending in ".cxx". Of course you can combine this options and you can give another task/filename as first argument: gen C::Dependencies, "depend.rf" :search => "include", :sources => sys["**/*.cxx"] This creates a file task with the name "depend.rf". Note that all our previous examples only created a filetask with the dependencies, to use them you have to load them in your Rantfile. So you probably want to call the +source+ command: gen C::Dependencies # invoke task "c_dependencies" and load the created file source "c_dependencies" And a good habit would be to wrap the +source+ expression in an +Action+ block: gen C::Dependencies # Do dependency checking only if at least one task will be # invoked. gen Action do source "c_dependencies" end or more selective: gen C::Dependencies # Do dependency checking when Rant looks at the first file # ending in ".h" or ".c" gen Action, /\.(h|c)$/ do source "c_dependencies" end For a little example project using the C::Dependency generator look into the doc/examples/c_dependencies[link:../examples/c_dependencies] directory of the Rant distribution. === Issues on case-insensitive file systems If you are building on a case insensitive file system, your Compiler will usually ignore the case of filenames in #include statements. Thus if you have a header file called myutils.h and the following preprocessor statement main.c: #include "MyUtils.h" The preprocessor will include the contents of myutils.h. The problem is that Rant's node names are case sensitive. In the above example Rant wouldn't track changes in files included by myutils.h when compiling main.c. To overcome this problem, set the :correct_case option to true, e.g.: gen C::Dependencies, :correct_case => true Note that this option makes only sense on case-insensitive file systems. It can also significantly slow down dependency checking. Currently, the implementation of this option has one limit. In the following statement #include "someDir/MyUtils.h" the case of someDir must be identical to the actual directory name on disk! == See also Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Tasks with command change recognition:: doc/command.rdoc[link:files/doc/command_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/command.rdoc0000644000175000017500000001462410527253223015112 0ustar xavierxavier == Command change recognition A +Command+ task is similar to a +file+ task: It is intended to create on file which may depend on other files. But instead of creating the file by executing a block of Ruby code, the +Command+ task creates the target file by executing a shell command. A +file+ task rebuilds the target file if at least one of the following two conditions is met: 1. The target file doesn't exist. 2. One of the prerequisites changed since the last build. A +Command+ task rebuilds the target file if at least one of the following three conditions is met: 1. The target file doesn't exist. 2. One of the prerequisites changed since the last build. 3. The command to create the target file changed since the last build. === General usage Consider the following Rantfile for rant 0.4.6: var :CFLAGS => "-g -O2" # can be overriden from commandline file "foo" => ["foo.o", "util.o"] do |t| sys "cc -o #{t.name} #{var :CFLAGS} #{t.prerequisites.join(' ')}" end gen Rule, ".o" => ".c" do |t| sys "cc -c -o #{t.name} #{var :CFLAGS} #{t.source}" end The problem with this buildfile is, that it won't recognize a change of CFLAGS, i.e. foo.o, util.o and foo should get rebuilt whenever CFLAGS changes. Since Rant 0.4.8, it is possible to do the following: import "command" var :CFLAGS => "-g -O2" # can be overriden from commandline gen Command, "foo" => ["foo.o", "util.o"] do |t| # notice: we're not calling the sys method, we # are returning a string from the block "cc -o #{t.name} #{var :CFLAGS} #{t.prerequisites.join(' ')}" end # if the block to Rule takes two arguments, # it is expected to return a task gen Rule, ".o" => ".c" do |target, sources| gen Command, target => sources do |t| "cc -c -o #{t.name} #{var :CFLAGS} #{t.source}" end end Now, whenever the command to build foo or a *.o file changes, it will be rebuilt. There is also a more concise syntax: import "command" var :CFLAGS => "-g -O2" # first argument (string) is the task/file name, second # argument (string, array or filelist) is a list of # prerequisites (notice: no `target => prereqs' syntax!) # third argument is a command string, which will be # executed by a subshell. gen Command, "foo", ["foo.o", "util.o"], 'cc -o $(>) $[CFLAGS] $(<)' gen Rule, ".o" => ".c" do |target, sources| gen Command, target, sources, 'cc -c -o $(>) $[CFLAGS] $(-)' end For the last syntax: === Interpolation of variables into command strings: ==== Which variables are interpolated? 1. Instance variables (mostly for internal usage), e.g.: @cc = "cc" 2. "var" variables (can be set from commandline, easy synchronization with environment variables), e.g.: var :cc => "cc" 3. Special, task specific variables "name" (symbol equivalent ">"):: task name "prerequisites" (symbol equivalent "<"):: all prerequisites seperated by spaces "source" (symbol equivalent "-"):: first prerequisite more task specific variables might get added later ==== Syntax of variable interpolation Variable names must consist only of "word characters", i.e. matching \w in Ruby regexes. 1. Plain interpolation. Example command: "echo $[ARGS]" The contents of variable ARGS (either @ARGS or var[:ARGS]) are converted to a string and interpolated. If the variable contains an array, ARGS.join(' ') is interpolated. 2. Escaped interpolation. Example command: "echo ${ARGS}" Like plain interpolation, but spaces will be escaped (system dependent). Consider this Rantfile: import "command" @args = ["a b", "c d", "ef"] @sh_puts = "ruby -e \"puts ARGV\"" gen Command, "foo", '$[sh_puts] ${args} > $(>)' Running rant will give on Windows: ruby -e "puts ARGV" "a b" "c d" ef > foo and on other systems: ruby -e "puts ARGV" a\ b c\ d ef > foo 3. Path interpolation. Example command: "echo $(ARGS)" Like escaped interpolation, but additionally, forward slashes (as used for filenames in Rantfiles) will be replaced with backslashes on Windows. ==== More on semantics of variable interpolation Interpolation is recursive, except for special target variables. There is a small semantic difference between the verbose special target variables (+name+ +prerequisites+ +source+) and the symbolic ones (< > -): The symbolic ones are interpolated *after* checking if the command has changed since the last build, the verbose forms are interpolated *before*. Consider this (artifical, using the Unix tool "cat") example: Rantfile (symbolic special target vars): import "command" @src = ["src1", "src2"] @src = var[:SRC].split if var[:SRC] gen Command, "foo", @src, 'cat $(<) > $(>)' % echo a > src1 % echo b > src2 % echo b > src3 % rant cat src1 src2 > foo "foo" didn't exist, so it was built anyway. Now let us change the prerequisite list: % rant "SRC=src1 src3" won't cause a rebuild of foo. Dependencies of foo changed from ["src1", "src2"] to ["src1", "src3"] but since src2 and src3 have the same content and $(<) isn't expanded for command change recognition, rant considers foo up to date. Now change Rantfile to (verbose special target vars): import "command" @src = ["src1", "src2"] @src = var[:SRC].split if var[:SRC] gen Command, "foo", @src, 'cat $(prerequisites) > $(name)' Starting from scratch: % echo a > src1 % echo b > src2 % echo b > src3 % rant cat src1 src2 > foo % rant "SRC=src1 src3" cat src1 src3 > foo This time, Rant expanded $(prerequisites) for command change recognition, and since the prerequsite list changed, it caused a rebuild. == See also If you want more details, look in the test/import/command directory of the Rant distribution. Using MD5 checksums instead of file modification times:: doc/md5.rdoc[link:files/doc/md5_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Support for C/C++:: doc/c.rdoc[link:files/doc/c_rdoc.html] Packaging:: doc/package.rdoc[link:files/doc/package_rdoc.html] Ruby project howto:: doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/configure.rdoc0000644000175000017500000000143010527253223015444 0ustar xavierxavier == The Configure plugin The Configure plugin lets you define a list of checks and generates tasks to run them. I'll show an Rantfile using the Configure plugin and explain it afterwards. conf = plugin :Configure do |conf| conf.task # define a task named :configure conf.check "a" do |c| c.default "value_a" c.guess { "value_a_guess" } c.react { |val| p val } end conf.check "b" do |c| c.default "value_b" end conf.check "c" do |c| end conf.check "d" do |c| c.react { } end conf.check "e" do |c| c.guess { false } end end file conf["a"] do |t| sys.touch t.name end == See also Rant Overview:: README[link:files/README.html] Writing an Rantfile:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] rant-0.5.8/doc/csharp.rdoc0000644000175000017500000000560510527253223014753 0ustar xavierxavier == C# Compiling Compiling a basic C# application is as simple as passing the desired filename and a list of source files to the +CSharp+ generator (assuming a C# compiler is in your path). import 'csharp' gen CSharp, "example.dll", :sources => sys["**/*.cs"] This creates a file task called "example.dll". Source files can be specified as a +FileList+, array, or string. The created task is dependent on all source files, so if any of them change the dll will be recompiled. You can specify library and resource files in the same manner gen CSharp, "example.dll", :sources => sys["**/*.cs"], :libs => ["mylib.dll", "mylib2.dll"] :res => "myres.resource" The generator guesses what type of output you want by inspecting the file extension of the out file ("example.dll"). +exe+ will be compiled to +winexe+, +netmodule+ to +module+, and all others to +dll+. You can override this by providing a :target key in the parameter hash. Any other parameters you specify are passed through to the C# compiler. gen CSharp, "example.exe", :sources => sys["**/*.cs"], :target => "exe", :warnaserror => true == Alternate Compilers The +CSharp+ generator searches your path for one of 3 known C# compilers - +csc+, +mcs+, and +gmcs+. You can specify a different compiler by providing a compiler adapter through the :compiler key. The existing compiler adapters provide a one parameter constructor that allows you to specify the path to that compiler. Or you can of course provide your own implementation. csc11 = CscCompiler.new("/path/to/csc1.1") gen CSharp, "example.dll", :sources => sys["**/*.cs"], :compiler => csc11 == Resource Generation A generator is provided to create a rule for compiling resource files with resgen. This is quite powerful as it allows you to simply reference the compiled resource files in you CSharp generator, and they will automatically be built as required. import "resgen" gen Resgen, :build_dir => "build/", :namespace => "Example" # properties/resource.resx => build/Example.Properties.Resource.resources # MyRes.resx => build/Example.MyRes.resources == Running NUnit You can easily run an NUnit test suite using the +NUnitTest+ generator import "nunittest" gen NUnitTest, "test", :dlls => ["mytest1.dll", "mytest2.dll"] By default, nunit-console is used, and it must exist in your path. You can specify a different test runner using the :bin key. Any other arguments are passed through to the test runner gen NUnitTest, "test", :dlls => sys["test/*.dll"], :bin => "my-test-runner", :xml => "results.xml" rant-0.5.8/doc/filelist.rdoc0000644000175000017500000007270510527253223015313 0ustar xavierxavier == Rant::FileList A +FileList+ object is in essence a list of path names. It behaves similar to an array of strings, but provides useful methods to include files based on so called "glob" patterns, i.e. strings containing wildcards and similar special characters. The examples in this document show how to use the +FileList+ class as library from other ruby applications/libraries. To use the Rant::FileList class, you must require "rant/filelist" first. This will define the following constants: Rant:: Module, used as namespace for all Rant code. Rant::VERSION:: The Rant version in use, e.g. "0.5.2". Rant::FileList:: The filelist class. Rant::Sys:: With method +regular_filename+. It is recommended to *not* include Rant at the toplevel for use in libraries or bigger applications. A better technique is to assign often used constants to shorter names, e.g.: FileList = Rant::FileList Some method documentations contain an Implementation Note. These notes are provided for better understanding. The actual implementation might change. === Creating a Rant::FileList There a five ways to obtain a +FileList+ object: * Rant::FileList.new Creates an empty filelist. Examples: require 'rant/filelist' fl = Rant::FileList.new fl.entries # => [] fl.include("*README*") fl.entries # => ["README", ".README.swp"] * Rant::FileList[*patterns] Create a filelist which contains all file names matching one of +patterns+. Each of +patterns+ is a glob pattern as described under Glob pattern syntax. Per default, all files/directories starting with a dot are ignored, unless a pattern explicitely matches names starting with a dot. Examples: require 'rant/filelist' # Create a filelist containing all file names ending in ".c" from # the current directory fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] # Create a filelist containing the "README" file from the current # directory (if it exists) and all files ending in ".rdoc" under # the "doc" directory and its subdirectories. fl = Rant::FileList["README", "doc/**/*.rdoc"] fl.entries # => ["README", "doc/foo.rdoc", "doc/html/emit.rdoc", "doc/tutorials/html/creating.rdoc"] # Create a filelist containing all files starting with a dot and # ending in ".rdoc" under the "doc" directory. fl = Rant::FileList["doc/.*.rdoc"] fl.entries # => ["doc/.bar.rdoc"] Note:: The order of the file names in the filelist and in the array returned by the +entries+ method is unspecified. Implementation Note:: Equivalent to Rant::FileList.new.hide_dotfiles.include(*patterns) * Rant::FileList.glob(*patterns) { |filelist| ... } The same as Rant::FileList[*patterns] but yields the new filelist to the block if a block is given. Additionally, it won't include the "." (current) and ".." (parent) directory entries. If a block is given, returns the return value of the block, otherwise the new filelist. Examples: require 'rant/filelist' Rant::FileList.glob("*.*) do |fl| fl.exclude "*.bak", "*~" fl.entries end # => ["README.txt", "setup.rb"] fl = Rant::FileList.glob(".*") fl.entries # => [".svn", ".README.txt.swp"] # compare the last example with the result of using # Rant::FileList[]: fl = Rant::FileList[".*"] fl.entries # => [".", "..", ".svn", ".README.txt.swp"] Implementation Note:: Before the given block is called, the filelist is created and initialized with Rant::FileList.new.hide_dotfiles.ignore(".", "..").include(*patterns) * Rant::FileList.glob_all(*patterns) { |filelist| ... } The same as Rant::FileList.glob(*patterns) but also includes files starting with a dot. Examples: require 'rant/filelist' Rant::FileList.glob_all("*.*) do |fl| fl.exclude "*.bak", "*~" fl.entries end # => ["README.txt", "setup.rb", ".README.txt.swp"] Implementation Note:: Before the given block is called, the filelist is created and initialized with Rant::FileList.new.ignore(".", "..").include(*patterns) * Rant::FileList(arg) Tries to convert +arg+ to an Rant::FileList object. If +arg+ responds to +to_rant_filelist+, the result of arg.to_rant_filelist is returned. If +arg+ responds to +to_ary+, a new filelist object containing the entries of arg.to_ary is returned. Note that changes to the returned filelist might cause changes to +arg+. Examples: require 'rant/filelist' fl = Rant::FileList.new Rant::FileList(fl) # => fl # convert array to filelist a = ["foo", "bar"] fl = Rant::FileList(a) # => new Rant::FileList fl.entries # => ["foo", "bar"] # obj doesn't respond to one of to_rant_filelist, to_ary obj = Object.new fl = Rant::FileList(obj) # => raises TypeError === Rant::FileList instance methods The Rant::FileList class includes the +Enumerable+ module. Thus you can call all methods provided by the +Enumerable+ module on +FileList+ instances. You can read the +ri+ documentation for those methods: % ri Enumerable Note that the methods +map+ (alias +collect+) and +select+ (alias +find_all+) have slightly different semantics as documented by +Enumerable+. Read below for documentation. Most Rant::FileList instance methods are _lazy_. This means that the actual work (e.g. glob pattern expansion) isn't done unless the filelists entries are being read. Following is a list of Rant::FileList instance methods. All lazy methods are marked with -lazy-. All methods that force evaluation of previously specified lazy operations, are marked with -eager-. Note that not all methods marked with -eager- are guaranteed to hold this predicate in future versions. The -eager- marker is just intended as additional information. To force execution of all previously lazy operations, call the +resolve+ method. rb_names = Rant::FileList["**/*.rb"].map { |fn| File.basename(fn) } # force expansion of the glob pattern "**/*.rb" and execution of # the map operation rb_names.resolve Note that you need to call +resolve+ only if the actual point in time of execution is important, e.g. this could be if a map operation has side effects like printing to standard output. * glob_unix(*patterns) -lazy- Include the file names specified by the glob patterns +patterns+. For exact glob pattern syntax read the section Glob pattern syntax below. Filenames starting with a dot ignored, unless explicitely matched by a pattern (e.g. ".*"). Returns +self+. Examples: require 'rant/filelist' fl = Rant::FileList.new fl.include "**/*.{c,h}", "main.cpp" fl.entries # => ["lib/util.c", "include/util.h", "config.h", "main.cpp"] Note:: No specific order of entries included with this method is guaranteed. * glob_all(*patterns) -lazy- Same as glob_unix(*patterns) but no special handling of filenames starting with a dot. Examples: require 'rant/filelist' fl = Rant::FileList.new fl.include "**/*.{c,h}", "main.cpp" fl.entries # => ["lib/.util.c", "lib/util.c", "include/util.h", "config.h", "main.cpp"] * include(*patterns) -lazy- glob(*patterns) Include the file names specified by the glob patterns +patterns+. For exact glob pattern syntax read the section Glob pattern syntax below. Each filelist has a flag that indicates wheter a glob operation should hide dotfiles or not. It can be read with the method glob_dotfiles?, and set with the method glob_dotfiles= to either +true+ or +false+. For filelists created with Rant::FileList.new or Rant::FileList.glob_all() this flag is +true+ per default. For filelists created with Rant::FileList[] or Rant::FileList.glob() this flag is +false+ per default. If this flag is true, the +glob+ (alias +include+) method calls glob_all(*patterns), otherwise it calls glob_unix(*patterns). Returns +self+. Examples: require 'rant/filelist' fl = Rant::FileList.new fl.include "**/*.{c,h}", "main.cpp" fl.entries # => ["lib/.util.c", "lib/util.c", "include/util.h", "config.h", "main.cpp"] fl = Rant::FileList.new fl.glob_dotfiles = false fl.include "**/*.{c,h}", "main.cpp" fl.entries # => ["lib/util.c", "include/util.h", "config.h", "main.cpp"] Note:: No specific order of entries included with this method is guaranteed. * exclude(*patterns) -lazy- Remove all entries matching one of +patterns+. Each of +patterns+ is either a regular expression or a glob pattern as described under the section Glob pattern syntax, *except* that *currently* the characters { and } (curly braces) are not treated special. A call to +exclude+ does not effect entries added later to the filelist. Returns +self+. Examples: require 'rant/filelist' fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "main.c~"] fl.exclude("*~") fl.entries # => ["main.c"] fl.include("*.h") fl.entries # => ["main.c", "main.h", "main.h~"] fl.exclude(/~$/) fl.entries # => ["main.c", "main.h"] * exclude_path(*patterns) -lazy- Like exclude(*patterns), but doesn't accept regular expressions and the metacharacters in the patterns are treated more restrictive. Wildcards ("*") and "?" won't match filename separators. Returns +self+. Examples: # exclude vs. exclude_path fl1 = Rant::FileList["**/*.rb"] fl1.entries # => ["setup.rb", "lib/foo.rb"] fl2 = fl1.dup fl2.entries # => ["setup.rb", "lib/foo.rb"] fl1.exclude "*.rb" fl2.exclude_path "*.rb" fl1.entries # => [] fl2.entries # => ["lib/foo.rb"] # another one fl = Rant::FileList(["a.rb", "lib/b.rb", "lib/foo/c.rb"]) fl.exclude_path "lib/*.rb" fl.entries # => ["a.rb", "lib/foo/c.rb"] Note:: The method +exclude_path+ provides an abstraction over the File::FNM_PATHNAME flag for the File.fnmatch method. * exclude_name(*names) -lazy- shun(*names) Remove all entries whose base name or one of its parent directory names is in +names+. A call to +exclude_name+ (or +shun+) does not effect entries added later to the filelist. Returns +self+. Examples: require 'rant/filelist' fl = Rant::FileList["**/*"] fl.entries # => ["CVS", "README", "README.CVS", "sub/CVS", "sub/README", "sub/CVS/foo", "sub/foo/bar"] fl.exclude_name("CVS") fl.entries # => ["README", "README.CVS", "sub/README", "sub/foo/bar"] * ignore(*patterns) -lazy- This filelist will never contain an entry matching one of +patterns+. Each element of +patterns+ is either a regular expression or a string. If the pattern is a string it matches all entries which have the string as base name or parent directory name (like +exclude_name+). This method applies to all previously added entries and to all entries that will be added in the future. Returns +self+. Examples: fl = Rant::FileList["**/*"] fl.entries # => ["CVS", "README", "README.CVS", "sub/CVS", "sub/README", "sub/CVS/foo", "sub/foo/bar"] fl.ignore("CVS") fl.entries # => ["README", "README.CVS", "sub/README", "sub/foo/bar"] fl.concat("dir/CVS", "dir") # note that fl doesn't contain "dir/CVS" fl.entries # => ["README", "README.CVS", "sub/README", "sub/foo/bar", "dir"] * files -lazy- Get a new filelist containing only the existing files from +self+. Examples: fl = Rant::FileList["**/*"] fl.concat ["does_not_exist"] fl.entries # => ["README", "sub", "sub/README", "does_not_exist"] files = fl.files files.entries # => ["README", "sub/README"] Rantfile Note:: In Rantfiles, this method is not loaded per default. You have to import "filelist/std" first. * dirs -lazy- Get a new filelist containing only the existing directories from +self+. Examples: fl = Rant::FileList["**/*"] fl.concat ["does_not_exist"] fl.entries # => ["README", "sub", "sub/README", "does_not_exist"] dirs = fl.dirs dirs.entries # => ["sub"] Rantfile Note:: In Rantfiles, this method is not loaded per default. You have to import "filelist/std" first. * no_dir -lazy- Remove all existing directories. A call to +no_dir+ does not effect entries added later to the filelist. Returns +self+. Examples: require 'rant/filelist' # create a filelist including all files in the current directory # and its subdirectories (recursive). fl = Rant::FileList["**/*"] fl.entries => ["README", "lib", "bin", "bin/rant", "lib/rant.rb"] fl.no_dir # now fl doesn't contain directory entries fl.entries => ["README", "bin/rant", "lib/rant.rb"] Rantfile Note:: In Rantfiles, this method is not loaded per default. You have to import "filelist/std" first. * no_dir(dir) -lazy- Remove all entries with a parent directory with the name +dir+ and all directories with a base name of +dir+. A call to this method does not effect entries added later to the filelist. Returns +self+. Examples: fl = Rant::FileList["**/*"] fl.entries # => ["README", "coverage", "coverage/index.html", "bin/coverage", "test/coverage", "test/coverage/index.html", "test/test_foo.rb"] fl.no_dir("coverage") # assumin "bin/coverage" is not a directory fl.entries # => ["README", "bin/coverage", "test/test_foo.rb"] Rantfile Note:: In Rantfiles, this method is not loaded per default. You have to import "filelist/std" first. * select { |fn| ... } -lazy- find_all { |fn| ... } Returns a copy of this filelist which contains only the elements for which the given block returns true. The calling filelist object isn't modified. Examples: # create a list of all files in the current directory and its # subdirectories all_files = Rant::FileList["**/*"].no_dir # create a list which contains the file names of all empty files empty_files = all_files.select { |fn| File.size(fn) == 0 } puts "The following files are empty:" # print the file names, one per line puts empty_files * map { |fn| ... } -lazy- collect { |fn| ... } Each entry of this filelist is passed in turn to the given block. The method returns a copy of this filelist with all entries replaced by the return value of block execution. The calling filelist object is not modified. Examples: names = Rant::FileList["**/*"] # create a filelist containing only absolute pathes abs_pathes = names.map { |fn| File.expand_path(fn) } * ext(ext_str) -lazy- Returns a new filelist containing the same entries as this list, but all entries have the extension +ext_str+. The calling filelist object is not modified. Examples: c_files = Rant::FileList["*.c"] c_files.entries # => ["a.c", "b.c"] obj_files = c_files.ext("o") obj_files.entries # => ["a.o", "b.o"] files = Rant::FileList(["a.c", "b", "c.en.doc", "d.txt"]) txt_files = file.ext("txt") txt_files.entries # => ["a.txt", "b.txt", "c.en.txt", "d.txt"] * uniq! -lazy- Removes duplicate entries. Returns +self+. Examples: files = Rant::FileList(["a", "b", "a", "a"]) files.uniq! files.entries # => ["a", "b"] * sort! -lazy- Sort the entries in this list in alphabetical order. Returns +self+. Examples: fl = Rant::FileList(["b", "a", "c"]) fl.sort! fl.entries # => ["a", "b", "c"] * map! { |fn| ... } -lazy- Pass each entry to the given block and replace it by the return value of the block. Returns +self+. Examples: # get a list of directories containing C source files src_dirs = Rant::FileList.glob "**/*.{c,h}" do |fl| fl.map! { |fn| File.dirname(fn) } fl.uniq! end * reject! { |fn| ... } -lazy- Pass each entry to the given block and remove those entries for which the block returns a true value. Returns +self+. Examples: non_empty_files = Rant::FileList["**/*"].reject! { |fn| stat = File.stat(fn) !stat.file? or stat.zero? } * resolve -eager- Execute all lazy operations. Returns +self+. * to_s -eager- Joins all entries with a single space as separator. Spaces in entries are escaped for the shell used on the current platform. Examples: txt_files = Rant::FileList["*.txt"] txt_files.entries # => ["User Manual.txt", "README.txt"] txt_files.to_s # on windows: '"User Manual.txt" README.txt' # unix/linux: 'User\ Manual.txt README.txt' # start the vim editor to edit all text files system "vim #{txt_files}" Note:: Might escape more special shell characters in the future. * entries -eager- to_a to_ary Convert this filelist to an array. Examples: fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] Especially the definition of the +to_ary+ method has much impact on how a filelist object is treated by Ruby libraries. Per convention, an object that responds to +to_ary+ can be passed to most methods that expect an actual array. So, in most cases you can use a filelist object where an array is expected. Examples: a = ["foo", "bar"] a.concat Rant::FileList["*.c"] a # => ["foo", "bar", "main.c", "util.c"] # remove all object files require 'fileutils' FileUtils.rm_f Rant::FileList["**/*.o"] # flattening an array (a somewhat artifical example ;) a = ["foo"] a << Rant::FileList["*.c"].ext("o") a.flatten # => ["foo", "main.o", "util.o"] It is not guaranteed that this method always returns the same object. It is guaranteed that the returned object is an instance of the +Array+ class. Note that changes to the returned array might affect the filelist and cause an undefined filelist state. * +(other) -lazy- Returns a new filelist containing the entries of +self+ and +other+. +other+ has to respond to +to_rant_filelist+ or to +to_ary+, otherwise a +TypeError+ is risen. Examples: c_files = Rant::FileList["*.c"] h_files = Rant::FileList["*.h"] src_files = c_files + h_files c_files.entries # => ["main.c", "util.c"] h_files.entries # => ["main.h", "util.h"] src_files.entries # => ["main.c", "util.c", "main.h", "util.h"] * size -eager- Returns the number of entries in this filelist. Examples: fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] fl.size # => 2 * empty? -eager- Returns true if this filelist doesn't contain any entry. Examples: fl = Rant::FileList([]) fl.empty? # => true fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] fl.empty? # => false * join(sep = ' ') -eager- Join the entries together to form a single string. Entries are seperated with the given separator string +sep+ or a single space if +sep+ is not given. Examples: fl = Rant::FileList(["a", "b"]) fl.join # => "a b" fl.join("\n") # => "a\nb" * pop -eager- Removes the last element and returns it, or +nil+ if this filelist is empty. Examples: fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] fl.pop # => "util.c" fl.entries # => ["main.c"] * push(entry) -eager- Append +entry+ to this filelist. Returns +self+. Examples: fl = Rant::FileList["*.c"] fl.push("foo") fl.entries # => ["main.c", "util.c", "foo"] * shift -eager- Removes the first entry and returns it. Returns +nil+ if the filelist is empty. Examples: fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] fl.shift # => "main.c" fl.entries # => ["util.c"] * unshift(entry) -eager- Insert +entry+ at the first position. Returns +self+. Examples: fl = Rant::FileList["*.c"] fl.entries # => ["main.c", "util.c"] fl.unshift("foo") fl.entries # => ["foo", "main.c", "util.c"] * keep(entry) Add +entry+ to this filelist. +entry+ will stay in this list, even if it matches a pattern given to +exclude+ or +ignore+. The position of +entry+ in this list is unspecified. Examples: fl = Rant::FileList.new fl.include "README.txt", "README.txt~" fl.keep "NEWS.txt" fl.keep "NEWS.txt~" fl.exclude "*~" fl.uniq! # Note that "README.txt~" was excluded, but "NEWS.txt~" not. fl.entries # => ["README.txt", "NEWS.txt", "NEWS.txt~"] * concat(ary) +ary+ has to respond to +to_ary+ (+ary+ could be an Array or other filelist). Appends the entries of +ary+ to this filelist. Returns +self+. Examples: fl = Rant::FileList(["a"]) fl.concat ["b", "c"] fl.entries # => ["a", "b", "c"] fl = Rant::FileList.new fl.include "*.c" fl.concat ["foo", "bar"] fl.entries # => ["main.c", "util.c", "foo", "bar"] * each { |entry| ... } Iterate over the filelist entries. Examples: # cat.rb - A simple cat program using Ruby glob patterns. require 'rant/filelist' Rant::FileList[*ARGV].no_dir.each { |file| print File.read(file) } # example usage: print contents of all Ruby files under lib % cat.rb "lib/**/*.rb" * to_rant_filelist Returns +self+. * dup Returns a copy of +self+. Thus after calling this method, there are two equal filelist objects. It is guaranteed that modifications of one filelist do not affect the other filelist, as long as the entry strings aren't modified. Examples: a = Rant::FileList["*.c"] a.entries # => ["main.c", "util.c"] b = a.dup b.entries # => ["main.c", "util.c"] a.include("*.h") a.entries # => ["main.c", "util.c", "main.h", "util.h"] # b not affected b.entries # => ["main.c", "util.c"] # Note: the original entry strings aren't modified. # a not affected b.map! { |entry| entry.capitalize } b.entries # => ["Main.c", "Util.c"] a.entries # => ["main.c", "util.c", "main.h", "util.h"] c = a.dup # DON'T DO THAT. Look at the previous example on how to accomplish # the same. c.each { |entry| entry.capitalize! } c.entries # => ["Main.c", "Util.c", "Main.h", "Util.h"] # Now the state of a is unspecified: The individual entries may be # capitalized or not! * copy Returns a deep copy of +self+. Thus after calling this method, there are two equal filelist objects. It is guaranteed that modification of one filelist (or its entries) has no impact on the other filelist. Examples: a = Rant::FileList["*.c"] a.entries # => ["main.c", "util.c"] b = a.copy b.entries # => ["main.c", "util.c"] b.each { |entry| entry.capitalize! } b.entries # => ["Main.c", "Util.c"] a.entries # => ["main.c", "util.c"] * glob_dotfiles Convinience method for glob_dotfiles = true. The following calls to +glob+ (alias +include+) won't treat filenames starting with a dot special. Returns +self+. * hide_dotfiles Convinience method for glob_dotfiles = false. The following calls to +glob+ (alias +include+) won't include filenames starting with a dot. Returns +self+. * inspect Overrides Object#inspect. Defined mainly for use in +irb+. The +inspect+ method as defined by the +Object+ class is still available under the name +object_inspect+. Examples: # irb session # Remember that irb uses the inspect method to show results (lines # starting with `=>'). % irb irb(main):001:0> require 'rant/filelist' => true irb(main):002:0> fl = Rant::FileList["*.rb"] => # irb(main):003:0> fl.resolve => # irb(main):004:0> Note:: Don't rely on the exact format of the string produced by +inspect+. Rantfile Note:: To get this +inspect+ method in Rantfiles, you have to import "filelist/std" first. * object_inspect Ruby's default +inspect+ method. === Glob pattern syntax The syntax used for filelist glob patterns is the same as for the Dir.glob Ruby core method. Basically, a glob pattern is a string where a few characters are treated special. Unless otherwise mentioned, a pattern is matched against the file/directory entries in the current directory. The following is a list of characters (the so called "metacharacters") that get special treatment: (Parts of this documentation are taken from the output of % ri Dir.glob, Ruby 1.8.4). *:: Match any file/directory. Can be restricted, e.g. "*.txt" matches all files/directories ending in ".txt". The pattern "\*a\*" matches any entry containing the character "a". The pattern "bar*" matches any entry starting with "bar". The pattern "lib/*.rb" matches any entry under the "lib" directory that ends in ".rb". **:: Matches directories recursively. E.g. the pattern "**/*.rb" matches all entries in the current directory and all its subdirectories (recursively) that end in ".rb". ?:: Matches any one character. [set]:: Matches any one character in +set+. E.g. the pattern "ca[rt]s" matches the entries "cars" and "cats". {p,q}:: Matches either literal +p+ or literal +q+. Matching literals may be more than one character in length. More than two literals may be specified. \\:: Escapes the following metacharacter. E.g. while pattern "a*b" would match any entry starting with "a" and ending with "b", the pattern "a\\*b" literally matches the entry "a*b". Some Ruby programmers use the File.join(dir, filename) method to construct patterns for Dir.glob (or Dir[]). The Rant::FileList class guarantees support for either directly using a slash ("/") as filename separator, which is recommended, or using File.join. Examples: # Using a slash as filename separator, supported and preferred fl = Rant::FileList["lib/*.c"] # Using File.join, supported pattern = File.join("lib", "*.c") fl = Rant::FileList[pattern] Note that the Rant::FileList class only supports a slash as filename separator. To convert a filename into Rant's internal filename format, use the Rant::Sys.regular_filename method. Example script, filename.rb: require 'rant/filelist' print "Rant internal filename: " puts Rant::Sys.regular_filename(ARGV[0]) Executing this on windows: % ruby filename.rb foo\bar Rant internal filename: foo/bar == See also Rant Overview:: README[link:files/README.html] Using filelists in Rantfiles:: doc/sys_filelist.rdoc[link:files/doc/sys_filelist_rdoc.html] Rant libraries:: doc/rubylib.rdoc[link:files/doc/rubylib_rdoc.html] Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] rant-0.5.8/doc/jamis.rb0000644000175000017500000002611610527253223014252 0ustar xavierxaviermodule RDoc module Page FONTS = "\"Bitstream Vera Sans\", Verdana, Arial, Helvetica, sans-serif" STYLE = < pre { padding: 0.5em; border: 1px dotted black; background: #FFE; } CSS XHTML_PREAMBLE = %{ } HEADER = XHTML_PREAMBLE + < %title% ENDHEADER FILE_PAGE = <
File
%short_name%
Path: %full_path% IF:cvsurl  (CVS) ENDIF:cvsurl
Modified: %dtm_modified%

HTML ################################################################### CLASS_PAGE = < %classmod%
%full_name% IF:parent ENDIF:parent
In: START:infiles HREF:full_path_url:full_path: IF:cvsurl  (CVS) ENDIF:cvsurl END:infiles
Parent: IF:par_url ENDIF:par_url %parent% IF:par_url ENDIF:par_url
HTML ################################################################### METHOD_LIST = < IF:diagram
%diagram%
ENDIF:diagram IF:description
%description%
ENDIF:description IF:requires
Required Files
    START:requires
  • HREF:aref:name:
  • END:requires
ENDIF:requires IF:toc
Contents
ENDIF:toc IF:methods
Methods
    START:methods
  • HREF:aref:name:
  • END:methods
ENDIF:methods IF:includes
Included Modules
    START:includes
  • HREF:aref:name:
  • END:includes
ENDIF:includes START:sections IF:sectitle IF:seccomment
%seccomment%
ENDIF:seccomment ENDIF:sectitle IF:classlist
Classes and Modules
%classlist% ENDIF:classlist IF:constants
Constants
START:constants IF:desc ENDIF:desc END:constants
%name% = %value%
  %desc%
ENDIF:constants IF:attributes
Attributes
START:attributes END:attributes
IF:rw [%rw%] ENDIF:rw %name% %a_desc%
ENDIF:attributes IF:method_list START:method_list IF:methods
%type% %category% methods
START:methods
IF:callseq %callseq% ENDIF:callseq IFNOT:callseq %name%%params% ENDIF:callseq IF:codeurl [ source ] ENDIF:codeurl
IF:m_desc
%m_desc%
ENDIF:m_desc IF:aka
This method is also aliased as START:aka %name% END:aka
ENDIF:aka IF:sourcecode
%sourcecode%
ENDIF:sourcecode
END:methods ENDIF:methods END:method_list ENDIF:method_list END:sections HTML FOOTER = < ENDFOOTER BODY = HEADER + <
#{METHOD_LIST}
#{FOOTER} ENDBODY ########################## Source code ########################## SRC_PAGE = XHTML_PREAMBLE + < %title%
%code%
HTML ########################## Index ################################ FR_INDEX_BODY = <
START:entries %name%
END:entries
HTML CLASS_INDEX = FILE_INDEX METHOD_INDEX = FILE_INDEX INDEX = XHTML_PREAMBLE + < %title% IF:inline_source ENDIF:inline_source IFNOT:inline_source ENDIF:inline_source <body bgcolor="white"> Click <a href="html/index.html">here</a> for a non-frames version of this page. </body> HTML end end rant-0.5.8/doc/md5.rdoc0000644000175000017500000000357410527253223014163 0ustar xavierxavier == Using MD5 checksums to detect file changes Most build tools rely on the modification time of a file which is updated usually when a program writes to the file. But this is not always as accurate as one would like, e.g. when you edit a C source file to simple change/add a comment, the compiler will probably produce the same object file as before. The build tool recognizes that the file modification time of the object file is newer than that of the target program and rebuilds the target program. With MD5 checksums instead of file modification times, the build tool recognizes that the object file didn't change and thus the target program doesn't need to be rebuilt. Even worse is the case where the file modification time isn't updated or is corrupted and the build tool thinks a source file hasn't changed were in fact it has! In other words, with MD5 checksums, the build tool recognizes when the *contents* of a file changes. To enable this nice feature for your project, put this single line at the top (before other +import+, +task+, +file+ or +gen+ statements) of the Rantfile: import "md5" Note that Rant saves the checksums after a build in a file called .rant.meta. If you remove this file, Rant looses all information about the last builds, and so will rebuild all targets on invocation. If you want to switch back to modification time based builds, simply remove the import "md5" statement and remove the file .rant.meta. == See also Writing an Rantfile:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Support for C/C++:: doc/c.rdoc[link:files/doc/c_rdoc.html] Packaging:: doc/package.rdoc[link:files/doc/package_rdoc.html] Ruby project howto:: doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/package.rdoc0000644000175000017500000002276410527253223015073 0ustar xavierxavier == Packaging Usually you want to create some archive file(s) to distribute your software. Rant supports creation of zip and gzipped tar archives. The good news is that you don't have to install any extra software because Rant integrates parts of Mauricio Julio Fernandez Pradier and Austin Ziegler's archive-tar-minitar and Thomas Sondergaard's rubyzip. Rant provides two techniques, which will be described in the following sections. === The Package::* generators The Package namespace provides two generators, namely Package::Tgz and Package::Zip. Let's look at some examples to create gzipped tar archives: Put this into your Rantfile: import "package/tgz" gen Package::Tgz, "foo", :files => sys["{bin,lib,test}/**/*"] This creates a file task called foo.tgz. If you run it with % rant foo.tgz rant will create a directory called foo, link all selected files (in this case all files under bin/, lib/, test/ and all their subdirectories, recursively) to foo/ and then create the archive foo.tgz from the foo directory. This means that the archive contains exactly one toplevel directory (foo) which in turn contains the selected files. Most Open Source software is packaged up this way. Also usual is to add a version number to the package name: import "package/tgz" gen Package::Tgz, "foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] Now the created file task (and thus the archive file) is called foo-1.0.1.tgz, and the toplevel directory in the archive is called foo-1.0.1. TAKE CARE:: If the directory foo or whatever the name of the package is, exists, it will be removed if you invoke the package task! To avoid such clashes, you can tell Rant to place the package in a subdirectory, e.g. pkg: import "package/tgz" gen Package::Tgz, "pkg/foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] Now the task is called pkg/foo-1.0.1.tgz. Rant automatically creates the pkg/ directory when required. Behind the scenes, Rant defines a Directory task for the pkg/ directory. === Cleaning up with AutoClean The AutoClean generator knows which files/directories have been created by our packaging tasks. Just put this lines into the Rantfile: import "package/tgz", "autoclean" gen Package::Tgz, "pkg/foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] gen AutoClean If you invoke rant with autoclean as argument now: % rant autoclean it will recursively remove the +pkg+ and the foo-1.0.1 directories. This could be a problem if you have source files (or other precious files) in the pkg/ directory. In this case change the syntax to: import "package/tgz", "autoclean" gen Package::Tgz, "pkg", "foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] gen AutoClean The file name for the package is still pkg/foo-1.0.1.tgz like in the previous example, but now rant assumes that the +pkg+ directory belongs to your source base and doesn't create a Directory task for it. If you invoke the +autoclean+ task now, it won't remove the +pkg+ directory, just the pkg/foo-1.0.1.tgz archive and the pkg/foo-1.0.1 directory. If you intend to use AutoClean, you should definitely read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]. === Writing a MANIFEST Rant can automatically write a +MANIFEST+ file where it lists all files which come into the package. Just give the :manifest flag: import "package/tgz" gen Package::Tgz, "pkg/foo", :manifest, :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] Now Rant will write/update a file called +MANIFEST+ whenever the package task (pkg/foo-1.0.1.tgz) is invoked. +MANIFEST+ will then contain a list of all files (each path on a separate line, as usual). Of course the +MANIFEST+ file will also be in the archive. You can use another name instead of +MANIFEST+. In the following example we use +contents+: import "package/tgz" gen Package::Tgz, "pkg/foo", :manifest => "contents", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] === Reading a MANIFEST If you manually maintain a MANIFEST file with all files which belong to your software package, you can tell Rant to read it: import "package/tgz" gen Package::Tgz, "pkg/foo", :manifest, :version => "1.0.1" Because we didn't specify the list of files to package with the :files option, but gave the :manifest flag, Rant will read the list of files to package from a file called +MANIFEST+. === Giving a non-standard file extension If you want another file extension than the default .tgz or .zip you can specify it with the :extension option: import "package/tgz" gen Package::Tgz, "pkg/foo", :manifest, :version => "1.0.1", :extension => ".tar.gz" === Creating zip packages To create a zip package, use exactly the same syntax and options as for tgz packages. Just import "package/zip" and use the Pakage::Zip generator: import "package/zip" gen Package::Zip, "foo", :files => sys["{bin,lib,test}/**/*"] This has the same effect as our first example, with the only difference that a foo.zip archive will be created. As already mentioned, you can give the same options and flags as to the Package::Tgz generator and of course you can use both in the same Rantfile: import "package/zip", "package/tgz" gen Package::Zip, "foo", :files => sys["{bin,lib,test}/**/*"] gen Package::Tgz, "foo", :files => sys["{bin,lib,test}/**/*"] === The Archive::* generators Like the Package:: namespace, the Archive:: namespace also provides two generators, namely Archive::Tgz and Archive::Zip. They require the same syntax and recognize the same options. So what's the difference? The Archive::* generators don't move the selected files into a toplevel directory. Let's look at an example: import "archive/tgz", "package/tgz" gen Package::Tgz, "foo", :files => sys["{bin,lib,test}/**/*"] gen Archive::Tgz, "bar", :files => sys["{bin,lib,test}/**/*"] This creates a file task foo.tgz and a file task bar.tgz. The difference is, that foo.tgz contains a toplevel directory +foo+ which in turn contains the +bin+, +lib+ and +test+ directories, but bar.tgz directly contains the +bin+, +lib+ and +test+ directories: Created with Package::Tgz: foo.tgz foo/ bin/ . . lib/ . . test/ . . Created with Archive::Tgz: bar.tgz bin/ . . lib/ . . test/ Of course the same difference is between the Package::Zip and Archive::Zip generators. === Creating a shortcut, dependencies Imagine you have a package task which creates a tgz package and a task which should upload the package to a server. This means that the upload task depends on the package task. You could do this like in the following Rantfile: import "package/tgz" gen Package::Tgz, "pkg", "foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] task :upload => "pkg/foo-1.0.1.tgz" do |t| sys "" end That's not very convinient and it's tedious to always update the dependency when you change the version number. But the package generator returns an object with information about the created package tasks: import "package/tgz" pkg = gen Package::Tgz, "pkg", "foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] task :upload => pkg.path do |t| sys "" end Here we assign this object to the +pkg+ variable. The pkg.path method returns the path to our tgz archive. Another handy use case is to create a shortcut: import "package/tgz" pkg = gen Package::Tgz, "pkg", "foo", :version => "1.0.1", :files => sys["{bin,lib,test}/**/*"] desc "Create package for distribution." task :package => pkg.path Now you can invoke the package task from the commandline: % rant -T rant package # Create package for distribution. % rant package === Some random notes * Symbolic links are always dereferenced, i.e. the file/directory the link points to will be in the archive, not the symbolic link. Rant will probably provide an option to store the symbolic links in the future. * Directories are not included recursively: gen Package::Tgz, "foo", :files => sys["lib"] Assuming +lib+ is a directory, foo.tgz will contain only the empty directory foo/lib, no matter how many files/subdirectories +lib+ contains. If you want to package +lib+ with all subdirectories and files it contains, use the following pattern: gen Package::Tgz, "foo", :files => sys["lib/**/*"] * Consider the directory where all files are linked to by the Package::* generators as byproducts and don't rely on their creation. == See also Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Support for C/C++:: doc/c.rdoc[link:files/doc/c_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/rant-import.rdoc0000644000175000017500000000220410527253223015737 0ustar xavierxavier == The rant-import command The rant-import command creates a monolithic rant script tailored to the needs of your project and thus removes the dependency on an Rant installation (but of course one person needs an Rant installation to run rant-import). Just run the command with the --help option to get a brief help message: % rant-import --help Probably the easiest way to create your monolithic rant script is with the --auto option: % rant-import --auto ant This will write a monolithic rant script to the file +ant+ in the current directory. To determine which plugins and imports your project is using, it performs step 2 of the rant command as described in doc/rant.rdoc[link:files/doc/rant_rdoc.html], which means that it loads the Rantfile in the current directory. That one command should be enough, try it out: % ruby ant This script has the same behaviour as the rant command. Distribute it with your project and nobody else but you needs an Rant installation. == See also Rant Overview:: README[link:files/README.html] Writing an Rantfile:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] rant-0.5.8/doc/rant.10000644000175000017500000000721610527253223013650 0ustar xavierxavier.\" rant.1 - rant manpage .\" Date of last change to this manpage. .TH RANT 1 "September 17, 2005" .SH NAME rant \- A flexible and portable build tool. .SH SYNOPSIS .B rant .RI [OPTION]\ ...\ [TARGET]\ ... .br .SH DESCRIPTION .B Striking features * Defining custom tasks. * Automated packaging, testing and RDoc generation for Ruby applications and libraries. * Creation of a monolithic script tailored to the needs of a specific project which can be used instead of an Rant installation - users don't need to install Rant. * Creating gzipped tar and zip archives -- without installing additional software. * Optional recognition of file changes based on MD5 checksums instead of file modification times. * Dependency checking for C/C++ source files (integrated makedepend replacement). * Primitive support for compiling C# sources with csc, cscc and mcs. .B Startup On startup, rant will look for a file called .IR Rantfile , .IR rantfile or .IR root.rant which contains the build specification (which in fact is valid Ruby code). After reading the Rantfile(s), rant will execute the following tasks: .B If at least one target was given on the commandline: rant will execute all tasks necessary to build all targets given on the commandline. .B else: rant tries to build the target called "default". If no task with this name exists, rant invokes the first defined task. If you want to know which task(s) will be invoked when no target is given on the commandline, run rant with the .IR -T option. Example: $ rant -T .br rant # => test .br rant package # Create packages for distribution. .br rant doc # Generate documentation. .br rant publish-docs # Publish html docs on make.rubyfore.org. .br # Note: scp will prompt for rubyforge password. .br rant test # Run basic tests. The .B first line always shows the target that will be built if no target argument is given. In this case, it would be the .RI test target. .PP .SH OPTIONS .TP --help -h Print a help message and exit. .TP --version -V Print version of Rant and exit. .TP --verbose -v Print more messages to stderr. .TP --quiet -q Don't print commands. .TP --err-commands Print failed commands and their exit status. .TP --directory DIRECTORY -C DIRECTORY Run rant in DIRECTORY. .TP --cd-parent -c With this option, Rant starts to search for an Rantfile in the current working directory as usual, but if it doesn't find one, it changes to the parent directory and looks there for an Rantfile. This is repeated until an Rantfile is found or the working directory is the root directory of the filesystem. .TP --look-up -u Look in parent directories for root Rantfile. .TP --rantfile RANTFILE -f RANTFILE Process RANTFILE instead of standard rantfiles. Multiple files may be specified by repeating this option. .TP --force-run TARGET -a TARGET Force rebuild of TARGET and all dependencies. .TP --dry-run -n Print the names of the tasks that would be executed instead of actually executing task actions. .TP --tasks -T Show a list of all described tasks and exit. .SH AUTHOR rant was developed by Stefan Lang .SH COPYRIGHT Copyright (C) 2005 Stefan Lang .SH SEE ALSO For extensive documentation visit the Rant homepage .IR http://make.ruby-co.de .\" vim:ft=nroff rant-0.5.8/doc/rant.rdoc0000644000175000017500000000527610527253223014443 0ustar xavierxavier == The *rant* program For a short usage message and a list of options invoke rant with the --help option: % rant --help Usually you'll run rant by giving it the name of the task(s) to be invoked as argument(s). To get an overview for the project in the current working directory, run rant with the --tasks (short form: -T) option: % rant -T rant # => test rant package # Create packages for distribution. rant doc # Generate documentation. rant test # Run unit tests. rant cov # Run all tests and generate coverage with rcov. rant clean # Remove autogenerated files. rant publish-docs # Publish html docs on RubyForge. # Note: scp will prompt for rubyforge password. This lists the "public" tasks for the project. The first line always tells you the task(s) that will be invoked when no argument is given to rant, in the above example, this would be the +test+ task. When you invoke rant on the commandline it performs the following steps (roughly): 1. Process commandline options and arguments. An option starts with two dashes or one for the single letter equivalent. Arguments of the form VAR=VAL set variables available in the Rantfile(s). All other arguments are names of tasks to be invoked. 2. Load Rantfile in working directory. Rantfiles with the following names are recognized: Rantfile rantfile root.rant 3. Calculate task dependencies and invoke required tasks. If no task was given on the commandline, a task called "default" will be invoked. If the "default" task doesn't exist, the first task will be invoked. === Dry-Run If you run rant in dry-run mode, it will print the actions it would execute instead of actually executing them. This can be useful in debugging your Rantfiles. To enable it, give the --dry-run, option or its short form, -n, on the commandline. Example Rantfile: import "command" task :install => "foo" do sys.install "foo", "/usr/local/bin", :mode => 0755 end gen Command, "foo", "foo.c", "cc -o $(>) $(<)" Running rant in dry-run mode: % rant -n Executing "foo" - SHELL cc -o foo foo.c Executing "install" - Ruby Proc at Rantfile:3 Running rant in "normal" mode: % rant cc -o foo foo.c install -c -m 0755 foo /usr/local/bin Running rant in dry-run mode again: % rant -n Executing "install" - Ruby Proc at Rantfile:3 == See also Rant Overview:: README[link:files/README.html] Independent from Rant? The rant-import command:: doc/rant-import.rdoc[link:files/doc/rant-import_rdoc.html] rant-0.5.8/doc/rant_vs_rake.rdoc0000644000175000017500000001027010527253223016143 0ustar xavierxavier == Rant vs. Rake Since many people (especially Ruby programmers) that know Rake ask for a Rake/Rant comparison, I'll spend a few paragraphs on this topic. This comparison is for Rant 0.4.8 and Rake 0.6.2. If not stated otherwise, this document assumes a standard Ruby 1.8/1.9 installation without additional libraries (except for Rant and Rake). === Feature comparison Generally speaking, Rant has all major features of Rake and more. Especially the following major Rant features aren't available for Rake: * Optional use of MD5 checksums instead of timestamps. To enable it, one import statement at the top of the Rantfile is enough: import "md5" * Easy and portable tgz/zip file creation on Linux/MacOS X/Windows (and probably most other platforms where ruby runs). No additional libraries necessary! * Create a script, tailored to the needs of a project, which can be used instead of an Rant installation => Distribute this script with your project and users and other developers don't need an Rant installation. Try the rant-import command: % rant-import --auto make.rb rant-import reads the Rantfile in the current directory, determines what code of the Rant distribution (and custom imports) is needed and writes a script that supports all required Rant features and depends only on a standard Ruby (1.8.0 or newer) installation to the file make.rb. Users and other developers don't need an Rant installation anymore. Instead of typing: % rant foo they can type: % ruby make.rb foo * It is possible to split up the build specification into multiple files in different directories. (=> no such thing as "recursive make" necessary). * Dependency checking for C/C++ source files (integrated makedepend replacement). * The --force-run (-a) option forces the rebuild of a target and all its dependencies. E.g.: % rant -a foo Let's say +foo+ depends on foo.o and util.o. Then the above command will cause a rebuild of foo.o, util.o and +foo+, no matter if Rant considers these files up to date or not. * Tasks with command change recognition. Example code: import "command" var :CFLAGS => "-g -O2" # can be overridden from commandline gen Command, "foo", ["foo.o", "util.o"], "cc $[CFLAGS] -o $(name) $(prerequisites)" The last two lines tell Rant, that the file +foo+ depends on the files foo.o and util.o and that +foo+ can be built by running the command in the last line ("cc ...") in a subshell. If at least one of the following three conditions is met, +foo+ will be rebuilt: 1. +foo+ doesn't exist. 2. foo.o or util.o changed since the last build of +foo+. 3. The command (most probably +CFLAGS+) changed since the last build of +foo+. * Dependency checking for C/C++ source files import "c/dependencies" # save dependencies between source/header files in the file # "cdeps"; search for include files in "." and "include" dirs gen C::Dependencies, "cdeps" :search => [".", "include"] # load dependency information when Rant looks at a C file gen Action, /\.(c|h)/ do source "cdeps" end Some other goodies: The +make+ method, the SubFile and AutoClean tasks, special variables and more. Most of this is documented in doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] === Internals * Rake defines many methods (task, file, desc, FileUtils methods, etc.) in the Object class (i.e. accessible from *each* line of Ruby code) which can get problematic at least if you want to use Rake as a library. Rant solves this problem by evaluating Rantfiles in a special context (with +instance_eval+). * Rake uses global variables and class/class instance variables to store state (tasks, etc.). The effect of this is, that you can have only one Rake application per Ruby interpreter (process) at a time. Rant stores application state in a class instance. It is possible to instantiate as many "Rant applications" at the same time as needed. On the other hand, there is currently no public and documented interface to do so (but it will come at least with Rant 1.0.0). rant-0.5.8/doc/rantfile.rdoc0000644000175000017500000003541710527253223015303 0ustar xavierxavier == How to write an *Rantfile* As already mentioned, an Rantfile is a Ruby script. Additionally, Rant provides methods and classes which aid you in automating your work. Centric to the processing done by Rant is the *task*. The Rantfile defines tasks and their dependencies, Rant invokes the required tasks. Also important is the Rant application. When the rant command starts, it instantiates one Rant application which will read one or more Rantfiles. The Rantfile communicates with the following list of methods with the Rant application: +task+:: Defines a task with prerequisites and an action. +file+:: A special form of task which creates one file. +desc+:: Describes the next defined task. A task with description is considered a "public" task. +gen+:: Takes a _generator_ object as first argument which usually creates one or more tasks. An example would be the +RubyPackage+ generator which produces tasks for packaging. +import+:: Imports additional code which usually provides additional generators. Example: to use the +RubyPackage+ generator you have to import "rubypackage" first. +sys+:: Run external commands or do usual file system operations (copy files, unlink files, install, ...). +source+:: Takes a filename as argument and causes Rant to read it in as Rantfile. +subdirs+:: Takes a list of subdirectories in which Rant should look for Rantfiles. +var+:: Provides access to variables accessible in Rantfiles and from the commandline. +make+:: Immediately build a target. === Defining a task Just call the +task+ function and give it a task name as argument. The task name may be a string or symbol: task :taskname That's it, your first task. Not very useful, because it doesn't do anything. To associate an action with the task, add a block: task :mytask do puts "Hello, mytask running." end Put these 3 lines of code into a file called +Rantfile+ and run rant: % rant mytask Hello, mytask running. Note: you could have omited the mytask argument to rant because it is the only task in the Rantfile. You can add a block parameter which will be a reference to the created task: task :mytask do |t| puts t.name end Running rant now: % rant mytask Add prerequisites to create relations between tasks: task :first => [:t1, :t2] do |t| puts t.name end task :t1 do |t| puts t.name end task :t2 do |t| puts t.name end In the definition of the "first" task we told Rant that it _depends_ on task "t1" and task "t2". "t1" and "t2" are called prerequisites for "first". Try it out: % rant first t1 t2 first % rant t1 t1 % rant t2 t2 === Defining a file task You will notice that rant will run the actions for a normal task always its name is given on the commandline or it is required by another task. Often you want to create a file, e.g. a program by invoking a compiler. In this case, the action needs only to be run if the source files (prerequisites) have changed. Use a file task instead of a normal task. In this example we use the sys.touch method to test our file task. (This method works the same as the Unix touch command: Update the modification time of a file or create an empty file): file "testfile" do |t| sys.touch t.name end Now run rant: % rant touch testfile This would have been the same with a normal task. But now run rant a second time: % rant This time rant doesn't run the action, because "testfile" is up to date. Of course you can add prerequisites the same way as for a normal task. Additionally you can add filenames as prerequisites. Assuming the files "a.o" and "b.o" are in the same directory as the Rantfile: file "myprog" => %w(a.o b.o) do |t| sys %w(cc -o), t.name, t.prerequisites end Running rant: % rant cc -o myprog a.o b.o Running a second time: % rant Does nothing, myprog is up to date. Don't be irritated by the %w() syntax. It creates a list of strings. The following expressions are equivalent: ["a", "b", "c"] %w(a b c) === Adding task descriptions The +desc+ function lets you describe your tasks. A small example Rantfile: # Generate C source file ls.c with the xgen command. file "ls.c" => %w(ls1.x ls2.x) do |t| sys %w(xgen -o), t.name, t.prerequisites end desc "Build ls program." file "ls" => "ls.c" do sys "cc -o ls ls.c" end desc "Remove autogenerated files." task :clean do sys.rm_f %w(ls.c ls) end (Note that xgen is a hypothetical command ;) The --tasks (or the short form, -T) option of rant shows this descriptions: % rant -T rant ls # Build ls program. rant clean # Remove autogenerated files. Only the tasks which have a description are listed. === The +sys+ method After using the +sys+ method quite often in the examples, I should explain it a little bit. It can be used in three ways: 1. File system operations The first form is with no arguments. It returns an object on which you can invoke methods for common file system operations: Examples are: sys.rm ["file1", "file2"] # remove files sys.cp "src", "dest" # copy from "src" do "dest" sys.mkdir "dir" # create directory "dir" For a list of all available methods read doc/sys.rdoc[link:files/doc/sys_rdoc.html]. 2. Running external commands Invoke the +sys+ method with a string as argument to run a shell: sys "echo *.c" will print a list of C files to stdout. When given multiple arguments, +sys+ invokes the program named with the first argument giving it the remaining arguments as arguments: sys "echo", "*.c" will print "*.c" to stdout. When the external program returns with an exit code other than 0, Rant will abort with an error message. Sometimes this is not desirable. E.g. the +diff+ program, which compares two files, returns 0 if the files are equal, 1 if they have differences and something else if an error occurs. Rant allows you to handle/ignore the exit code of a program yourself. Example: task :diff do |t| sys "diff -u a/util.c b/util.c > util.diff" do |ps| # ps is an instance of Process::Status case ps.exitstatus when 0: puts "a/util.c and b/util.c are equal" when 1: puts "a/util.c and b/util.c are not equal" else t.fail "diff error" end end end 3. Selecting files To select files with the help of glob patterns use +sys+ with the [] operator: file "program" => sys["*.o"] The task "program" depends on all files ending in ".o". For extensive documentation read doc/sys_filelist.rdoc[link:files/doc/sys_filelist_rdoc.html]. === Generators The *gen* function takes a generator which usually creates one or more tasks for you. The following list of generators is immediately available: +Directory+:: Create directories. +Task+:: Define custom task. +Rule+:: Define a rule (a rule produces tasks on the fly). +Action+:: Run a block of code immediately. The Action generator is discussed in doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]. === The +Directory+ generator An example usage of the +Directory+ generator would be the backup example shown in the README file: file "misc/backup/data" => %w(misc/backup data) do |t| sys.cp "data", t.name end gen Directory, "misc/backup" Now rant will create the directories "misc" and "backup" on demand. Assuming "misc/backup" doesn't exist: % rant mkdir misc mkdir misc/backup cp data misc/backup/data === The +Task+ generator The +Task+ generator allows you to determine by hand when your task action needs to be run: desc "Install with setup.rb" gen Task, :install do |t| t.needed { !File.exist? "InstalledFiles" } t.act do sys.ruby "setup.rb" end end The +act+ block of the "install" task will only be run if "InstalledFiles" doesn't exist. Of course you can add prerequisites like with any other task. === Rules A Rule allows you to tell Rant how it should build files matching a common pattern, e.g. how to build files ending in ".o". A standard rule usage is to create C object files: gen Rule, '.o' => '.c' do |t| sys "cc -c -o #{t.name} #{t.source}" end Assuming that we have the C source file util.c in the current directory: % rant util.o cc -c -o util.o util.c Because Rant didn't find a task for util.o, it looked for a matching rule and created a task for util.o. The first line above could also be written as: gen Rule, :o => :c do |t| The +source+ method of the task object gives us the first dependency. So the following line has the same effect: sys "cc -c -o #{t.name} #{t.prerequisites.first}" You can also refine the rule pattern by using a regular expression. To refine dependency selection give a block as source argument: src = lambda { |target| [target.sub_ext("c"), target.sub_ext("h")] } gen Rule, /^my_[^.]+\.o$/ => src do |t| sys "cc -c -o #{t.name} #{t.source}" end This rule generates a task for files beginning with "my_" and ending in ".o" (like "my_program.o"). The task has a file ending in ".c" and one ending in ".h" as dependencies (like "my_program.c" and "my_program.h") . Since t.source gives us the *first* dependency, the ".c" file will appear as argument to cc, but not the ".h" file. The +sub_ext+ method of the +String+ class replaces anything after the last dot with the given string. === Importing additional generators The +import+ function lets you import additional generators. Currently the following come with Rant: Clean:: Remove selected files. AutoClean:: Remove all files generated by any file task (including those generated by rules). DirectedRule:: A Rule which takes sources from one or more directories and puts generated files into a specified directory. SubFile:: Create file task and necessary directory tasks. Command:: Tasks with command change recognition. RubyTest:: Run Test::Unit tests for your Ruby code. RubyDoc:: Run RDoc. RubyPackage:: Generate tar, zip and gem packages of your Ruby application/library. Archive::Tgz:: Create gzipped tar archives. Archive::Zip:: Create zip archives. Package::Tgz:: Create gzipped tar packages. Package::Zip:: Create zip packages. C::Dependencies:: Determine dependencies between C/C++ source/header files caused by #include statements. Win32::RubyCmdWrapper:: Create .cmd wrapper scripts for installation of Ruby scripts on Windows. Read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] and doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html] for documentation. === The +subdirs+ command The +subdirs+ command allows you to give Rant a list of subdirectories (relative to the Rantfile) where it should look for additional Rantfiles. The tasks defined in those subdirectories can be referenced from your main Rantfile and vice versa. A small example: We are assuming the following files: myprog/ Rantfile # the main Rantfile README src/ Rantfile main.c lib.c lib.h Then the main Rantfile could look like: desc "Build myprog." file "myprog" => "src/myprog" do sys.cp "src/myprog", "myprog" end desc "Remove compiler products." task :clean => "src/clean" do sys.rm_f "myprog" end # Tell Rant to look in src for an Rantfile, # we could list more directories here. subdirs "src" And src/Rantfile: file "lib.o" => %w(lib.c lib.h) do sys "cc -c -o lib.o lib.c" end file "main.o" => "main.c" do sys "cc -c -o main.o main.c" end file "myprog" => %w(lib.o main.o) do sys "cc -o myprog main.o lib.o" end task :clean do sys.rm_f Dir["*.o"] + %w(myprog) end Note that we refer to the task in subdirectory simply by prepending the directory name and a slash to the task name. file "myprog" => "src/myprog" do This tells Rant that the "myprog" task depends on the "myprog" task defined in the Rantfile in the "src" directory. The same goes for the "clean" task. Let's examine what Rant thinks about our build specification: myprog % rant -T rant myprog # Build myprog. rant clean # Remove compiler products. Since the "myprog" task is the first in our Rantfile, we don't need to specify a task to build myprog: myprog % rant cc -c -o lib.o lib.c cc -c -o main.o main.c cc -o myprog main.o lib.o cp src/myprog myprog And to save disk space, remove generated files: myprog % rant clean rm -f lib.o main.o myprog rm -f myprog The nice thing about our buildfiles is, that we can use the src/Rantfile independent from our main Rantfile. Consider you are working on lib.c and only want to check that it compiles correctly. You can specify the task explicitely: myprog % rant src/lib.o cc -c -o lib.o lib.c But if you are working a long time only in the src/ directory it is convinient to do: myprog % cd src myprog/src % rant lib.o cc -c -o lib.o lib.c To clean only the src directory: myprog/src % rant clean rm -f lib.o main.o myprog You can find this example project in the doc/examples/myprog directory of the Rant distribution. Of course you can also have a subdirectory in src/ containing an Rantfile. Simply put a +subdirs+ command into the src/Rantfile. Rant integrates well into your file system :) If you want to refer to a task in the main Rantfile from a subdir Rantfile, put a @ in front of the task name: task "a" => "@b" But note that this Rantfile won't work as a standalone buildfile, because it refers to a main Rantfile. For further documentation about managing build files in project subdirectories, read doc/subdirs.rdoc[link:files/doc/subdirs_rdoc.html]. == See also Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Support for C/C++:: doc/c.rdoc[link:files/doc/c_rdoc.html] Packaging:: doc/package.rdoc[link:files/doc/package_rdoc.html] Ruby project howto:: doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html] Using filelists in Rantfiles:: doc/sys_filelist.rdoc[link:files/doc/sys_filelist_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/rubylib.rdoc0000644000175000017500000000150210527253223015133 0ustar xavierxavier == Rant libraries The Rant build tool contains much code that can be reused in other Ruby applications or libraries. This document provides an overview of what is "officially" supported. As Rant matures, more classes will be provided to drive Rant programmatically or just to allow reuse of library code. === Rant::FileList class This class provides an abstraction over the Dir.glob and File.fnmatch methods. In many cases it can be used instead of the +Find+ module in Ruby's standard library. Read doc/filelist.rdoc[link:files/doc/filelist_rdoc.html] for full documentation of the Rant::FileList class. == See also Rant Homepage:: http://make.ruby-co.de Rant Overview:: README[link:files/README.html] Rant::FileList documentation:: doc/filelist.rdoc[link:files/doc/filelist_rdoc.html] rant-0.5.8/doc/rubyproject.rdoc0000644000175000017500000001664310527253223016047 0ustar xavierxavier == Ruby project howto This section is a brief howto for automating some common tasks like running rdoc, unit tests and packaging. We are assuming a project with the following directories and files: Rantfile README bin/ wgrep lib/ wgrep.rb test/ tc_wgrep.rb You can find this little project in test/project_rb1 directory of the Rant package. You'll probably have more files in your project, but we will try to write our Rantfile as generic as possible: import %w(rubytest rubydoc rubypackage clean) lib_files = sys["lib/**/*.rb"] dist_files = lib_files + sys["README", "rantfile.rb", "{test,bin}/*"] desc "Run unit tests." gen RubyTest do |t| t.test_dir = "test" t.pattern = "tc_*.rb" end desc "Generate html documentation." gen RubyDoc do |t| t.opts = %w(--title wgrep --main README README) end desc "Create packages." gen RubyPackage, "wgrep" do |t| t.version = "1.0.0" t.summary = "Simple grep program." t.files = dist_files t.bindir = "bin" t.executable = "wgrep" t.package_task end desc "Remove autogenerated and backup files." gen Clean var[:clean].include "doc", "pkg", "*~" To verify how our tasks are named: % rant -T rant test # Run unit tests. rant doc # Generate html documentation. rant package # Create packages. rant clean # Remove autogenerated and backup files. Let's examine the code by running the tasks: % rant test cd test ruby -I /home/stefan/Ruby/lib/rant/test/project_rb1/lib -S testrb tc_wgrep.rb Loaded suite tc_wgrep.rb Started . Finished in 0.004588 seconds. 1 tests, 2 assertions, 0 failures, 0 errors cd - The +RubyTest+ generator automatically added the +lib+ directory to the LOAD_PATH for testing. Because we have set the +test_dir+ to "test", it changes to the +test+ directory, evaluates the +pattern+ to collect the testcases and then runs the _testrb_ command. To format the documentation: % rant doc README: wgrep.rb: mcc.... Generating HTML... Files: 2 Classes: 2 Modules: 1 Methods: 4 Elapsed: 0.335s All of this output originates from RDoc. The +RubyDoc+ generator gives the +lib+ directory and additionally the +opts+ we have set to RDoc. The most interesting task is the +package+ task: % rant package mkdir pkg mkdir pkg/wgrep-1.0.0 mkdir -p pkg/wgrep-1.0.0/lib mkdir -p pkg/wgrep-1.0.0/test mkdir -p pkg/wgrep-1.0.0/bin ln lib/wgrep.rb pkg/wgrep-1.0.0/lib/wgrep.rb ln rantfile.rb pkg/wgrep-1.0.0/rantfile.rb ln README pkg/wgrep-1.0.0/README ln test/text pkg/wgrep-1.0.0/test/text ln test/tc_wgrep.rb pkg/wgrep-1.0.0/test/tc_wgrep.rb ln bin/wgrep pkg/wgrep-1.0.0/bin/wgrep touch pkg/wgrep-1.0.0 cd pkg tar zcf wgrep-1.0.0.tar.gz wgrep-1.0.0 cd - cd pkg zip -yqr wgrep-1.0.0.zip wgrep-1.0.0 cd - Successfully built RubyGem Name: wgrep Version: 1.0.0 File: wgrep-1.0.0.gem mv wgrep-1.0.0.gem pkg This is what it does: 1. Link all package files to the new folder pkg/wgrep-1.0.0. 2. Create a tar.gz file in the +pkg+ directory. 3. Create a .zip file in the +pkg+ directory. 4. Create a RubyGem in +pkg+. The RubyGem will only be created if RubyGems is available on your system. The gem specification also contains the RDoc options we have given to the +RubyDoc+ generator and the +has_rdoc+ attribute is set to true. No need to duplicate this information in our Rantfile. And if we want to remove all autogenerated files (and backup files) we run the +clean+ task: % rant clean rm -rf doc rm -rf pkg rm -f README~ === More about the RubyPackage generator The argument given to the RubyPackage generator is used as package name (here "wgrep"): gen RubyPackage, "wgrep" do |t| The line which actually creates the package task(s) is this one: t.package_task Optionally you may give it a task name as argument: t.package_task :release Now you can run rant with the argument "release" for packaging: % rant release If you want to change the package directory (which defaults to +pkg+) you can set the +pkg_dir+ attribute: t.pkg_dir = "packages" t.package_task Perhaps you want to specify explicetely what packages you want to build. To accomplish this, each of the following methods listed takes an optional task name as argument: t.tar_task # create tar.gz archive, task name defaults to "tar" t.zip_task # create zip archive, task name defaults to "zip" t.gem_task # create gem, task name defaults to "gem" An example would be: desc "Create tar.gz package." t.tar_task :archive desc "Create RubyGem." t.gem_task # Not required: Creates a shortcut for the previously defined # package tasks ("archive" and "gem") desc "Create packages." t.package_task In our example we had only a few attributes for our package. Here are following two lists with the available attributes. * Attributes that take a single argument: name date description email has_rdoc homepage platform required_ruby_version rubyforge_project summary version * Attributes that take one or more values (if you give a single value it will automatically be converted to a list): author bindir executable extension files rdoc_options requires test_files test_suite The attributes can also be set without the assignment operator: t.executable "mybin" Additionally to all this attributes you can explicitely set an attribute for the gem specification by preceding it with +gem_+: t.gem_autorequire = "myproject" === More about the RubyDoc generator If an argument is given to the RubyDoc generator, it will be used as task name. The output directory for the html files defaults to +doc+. To change this set the +dir+ attribute: desc "Generate html documentation." gen RubyDoc, :html do |t| t.dir = "doc/html" As rant invokes RDoc programmatically, no command is printed when the task is run. If you want to see one, set the verbose attribute to +true+: t.verbose = true === Installation Since we get a RubyGem from the RubyPackage generator, our package is ready for distribution and installation. But probably you also want to provide a "normal" zip/tar archive. In this case you can use a conventional install.rb or setup.rb script for installation. If we install the "wgrep" script on Windows, it won't run out of the box, because Windows doesn't know that it has to run it with the Ruby interpreter. This problem can be solved with Rant. Add the following lines to your Rantfile: import "win32/rubycmdwrapper" desc "Install wgrep." task :install do # Run setup.rb with the Ruby interpreter. sys.ruby "setup.rb" end if Env.on_windows? # Create .cmd files for all scripts in the bin/ directory and # make the install task dependent on them. enhance :install => (gen Win32::RubyCmdWrapper, sys["bin/*"]) end == See also Rant Overview:: README[link:files/README.html] Writing an Rantfile:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] rant-0.5.8/doc/subdirs.rdoc0000644000175000017500000001126710527253223015147 0ustar xavierxavier == Buildfiles in subdirectories When a software project grows, it is often desired to split the build file up and create multiple buildfiles, e.g. one per directory. Rant has support for this case built in (unlike make). Part of this support is the +subdirs+ command which is described in doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html]. This section will describe one possible technique. First, a project needs to have a so called "root Rantfile". This file lives in the top directory of the project and is called +Rantfile+ or root.rant. Each subdirectory which needs specialized tasks, has a buildfile called sub.rant. A simple example would be a Ruby project where the test task(s) are defined in a seperate buildfile in the +test+ directory. The directory structure could look like: rubyproject/ README root.rant bin/ ... some scripts lib/ ... ruby library scripts test/ sub.rant tc_reader.rb tc_writer.rb tc_filter.rb tc_metautils.rb The project has one root.rant file and any number of sub.rant files (in this case only one). In root.rant we define tasks for packaging, installation, cleanup, etc. and in the test/sub.rant file we define tasks that run unit tests. root.rant could look like: import "md5" # use MD5 checksums instead of timestamps, optional import "rubypackage", "rubydoc", "autoclean" # reference the "all" task defined in test/sub.rant task :default => "test/all" desc "Create package for distribution." gen RubyPackage, "foo" do |t| t.version = "1.0.1" t.summary = "Foo is a ruby library and program for ..." t.files = sys["bin/*", "lib/**/*.rb", "test/tc_*.rb", "**/*.rant"].shun("pkg") t.package_task end desc "Generate HTML docs." gen RubyDoc desc "Remove generated files and directories." gen AutoClean, :clobber # read test/sub.rant subdirs "test" and test/sub.rant contains: desc "Run all unit tests." task :all do sys "testrb", sys["tc_*.rb"] end desc "Run IO related unit tests." task :io do sys "testrb tc_reader.rb tc_writer.rb" end desc "Test internals." task :internals do sys "testrb tc_filter.rb tc_metautils.rb" end Now we run from the shell: First let rant list all public tasks: $ rant -T rant # => test/all rant package # Create package for distribution. rant doc # Generate HTML docs. rant clobber # Remove generated files and directories. rant test/all # Run all unit tests. rant test/io # Run IO related unit tests. rant test/internals # Test internals. We see that if we run rant without arguments, it will run the test/all task. Change to the test directory: $ cd test $ rant -T (root is /home/stefan/Ruby/misc/rant/rbtest, in test) rant all # Run all unit tests. rant io # Run IO related unit tests. rant internals # Test internals. rant @package # Create package for distribution. rant @doc # Generate HTML docs. rant @clobber # Remove generated files and directories. If rant is started from a directory with a sub.rant file, it searches upwards in the directory tree until it finds a file called +Rantfile+ or root.rant. This file will be read first, the directory which contains this file is viewed as the projects root directory. Then it ensures that the sub.rant file in the current directory is read and starts operating. Task names given on the commandline are interpreted relative to the current directory. Tasks defined in other directories can be referenced by prepending them with an @ character. To run the IO related tests from the test directory we type: $ rant io (root is /home/stefan/Ruby/misc/rant/rbtest, in test) testrb tc_reader.rb tc_writer.rb ... output of testrb ... To run the clobber task from the toplevel directory: $ rant @clobber (root is /home/stefan/Ruby/misc/rant/rbtest, in test) (in /home/stefan/Ruby/misc/rant/rbtest) rm -f ... ... more output ... == See also Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Support for C/C++:: doc/c.rdoc[link:files/doc/c_rdoc.html] Packaging:: doc/package.rdoc[link:files/doc/package_rdoc.html] Ruby project howto:: doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/sys.rdoc0000644000175000017500000004463010527253223014312 0ustar xavierxavier == sys methods The +sys+ object, which is accessible from anywhere in an Rantfile, provides many methods for common file system operations like deleting, copying, moving, comparing and writing files. Unless explicitely mentioned otherwise, the following statements apply to all below documented methods: 1. Portable across all supported platforms. 2. Ignore the return value! 3. The messages printed to standard output may change. 4. Error conditions are reported through exceptions of class +SystemCallError+ or subclasses of +SystemCallError+. The following methods print messages to standard output: * cd(dir) Change the current directory to +dir+. +dir+ may be an absolute path or a path relative to the current directory. If a block is given, the current directory will be changed to +dir+, then the block is executed and it is ensured, that after block execution the old working directory is resumed (even if an exception is thrown during block execution). Examples: # relative path sys.pwd # => "/home/user" sys.cd "tmp" # prints "cd tmp" sys.pwd # => "/home/user/tmp" sys.cd ".." # prints "cd .." sys.pwd # => "/home/user" # absolute path sys.cd "/etc" # prints "cd /etc" sys.pwd # => "/etc" # relative path, with block sys.pwd # => "/home/user" sys.cd "tmp" do sys.pwd # => "/home/user/tmp" # perform some operations, may # also call sys.cd sys.cd "/etc" end sys.pwd # => "/home/user" * rm(file) Remove +file+. +file+ may be an absolute or relative path (string). If +file+ is an array of strings or a filelist, remove all entries of +file+. Examples: # remove the file "util.o" in the current directory sys.rm "util.o" # prints "rm util.o" # remove all files ending in ".o" in the "lib" directory sys.rm sys["lib/*.o"] Raises a +SystemCallError+ if +file+ doesn't exist or is a directory. * rm_f(file) Same as rm(file), but doesn't throw an exception if +file+ doesn't exist. Example: # remove "main.o" if it exists sys.rm_f "main.o" # prints "rm -f main.o" * rmdir(dir) Remove the empty directory +dir+. +dir+ may be a list of strings, a filelist or a string. Raises a +SystemCallError+ if +dir+ is not empty or doesn't exist. Examples: # remove empty directory "/home/user/tmp" sys.rmdir "/home/user/tmp" # remove empty directory, relative path sys.rmdir "tmp" # remove empty directories "tmp" and "/usr/local/tmp" sys.rmdir ["tmp", "/usr/local/tmp"] # remove all (empty) directories in the current directory ending # in ".t" sys.rmdir sys["*.t"] * rm_r(entry) If +entry+ is a (relative or absolute) path to a file, simply removes the file. If +entry+ is a directory, remove the directory and all its contents (including subdirectories). If +entry+ is an array of pathes or a filelist, remove all entries listed in +entry+ (directories are removed, including their contents, too). Examples: # remove the "tmp" directory and all its contents sys.rm_r "tmp" # prints "rm -r tmp" Raises a +SystemCallError+ if +entry+ doesn't exist. * rm_rf(entry) Does the same as rm_r(entry), but doesn't raise an exception if +entry+ doens't exist. Example: # remove the "tmp" directory and all its contents if it exists sys.rm_rf "tmp" # prints "rm -rf tmp" * mkdir(dir) If +dir+ is a string, create the new directory +dir+. +dir+ may be a relative or absolute path. Examples: # relative path sys.mkdir "foo" # absolute path sys.mkdir "/home/user/foo" If +dir+ is a list of strings, or a filelist, create all directories listed in +dir+. Example: # with array, creates directory "foo" and directory "bar" sys.mkdir ["foo", "bar"] Raises a +SystemCallError+ if a file/directory with this name already exists. * mkdir_p(dir) Creates the directory +dir+ and all its parent directories, if necessary. Does nothing if +dir+ already exists. If +dir+ is an array/filelist, creates all directories listed in +dir+. Examples: # creates "/usr" if a directory of this name doesn't exist # creates "/usr/local" if a directory of this name doesn't exist # creates "/usr/local/bin" if a directory of this name doesn't exist sys.mkdir_p "/usr/local/bin" # prints "mkdir -p /usr/local/bin" # creates the three given pathes sys.mkdir_p ["foo/include", "foo/src/util", "foo/src/ui"] * cp(src, dest) If +src+ is a (relative or absolute) path to a file, copy the file +src+ to +dest+. Example: # copy "main.c" to "build/main.c" sys.cp "main.c", "build/main.c" # prints "cp main.c build/main.c" If +dest+ is a directory, copy +src+ to dest/src. If +src+ is an array of strings or a filelist, copy all files listed in +src+ to the directory +dest+. Examples: # copy "main.c" to the "build" directory sys.cp "main.c", "build" # copy all files ending in ".c" from the current directory to the # "build" directory sys.cp sys["*.c"], "build" Raises a +SystemCallError+ if +src+ is a directory or doesn't exist. * cp_r(src, dest) Does the same as cp(src, dest), but also accepts a directory/directories as +src+. Directories are recursively copied to +dest+. Example: # Recursively copy all files/directories in the "src" directory # to the (existing) "/backup" directory. sys.cp_r sys["src/*"], "/backup" # prints "cp -r /backup" * mv(src, dest) If +src+ is a path (string), move the file +src+ to +dest+. Example: # move "build/foo.exe" to "dist/foo.exe" sys.mv "build/foo.exe", "dist/foo.exe" # prints "mv build/foo.exe dist/foo.exe" If +dest+ is a directory, move +src+ do dest/src. If +src+ is an array of pathes or a filelist, move all entries of +src+ to the directory +dest+. Example: # move all files ending in ".exe" from the "build" directory to # the "dist" directory sys.mv sys["build/*.exe"], "dist" +src+ may also be a (empty or non-empty) directory. Of course a mixed array/filelist of "normal" files and directories is also allowed. Raises a +SystemCallError+ if +src+ is an array/filelist and +dest+ is not a directory. * touch(file) +file+ may be a single path to a file, an array of pathes or a filelist. Updates the modification time and the access time of all files. If a file doesn't exist, creates an empty one with this name. Examples: # "main.c" is a file, update its modification time sys.touch "main.c" # "ts1" and "ts2" don't exist, create two empty files sys.touch ["ts1", "ts2"] * safe_ln(src, dest) This creates a hard link +dest+ which points to the same file as +src+, on platforms that support hard links. Simply copies +src+ to +dest+ on other platforms. Example: # link or copy "main.c" to "package/main.c" sys.safe_ln "main.c", "package/main.c" # prints "ln main.c package/main.c" if a hard link is created # prints "cp main.c package/main.c" if is main.c is copied * ln(src, dest) Creates a hard link +dest+ which points to the same file as +src+. If +dest+ is a directory, creates the hard link dest/src. Example: # link "main.c" to "package/main.c" sys.ln "main.c", "package" # prints "ln main.c package" Raises a +SystemCallError+ if +dest+ is a file or doesn't exist. Note:: Not all file systems and operating systems support hard links. On operating systems without support for hard links, a +NotImplementedError+ exception is risen. If the operating system supports hard links, but the file system not, a +SystemCallError+ is risen. * ln_f(src, dest) Same as ln(src, dest), but overwrites +dest+ if +dest+ is a file. Example: # link "main.c" to "package/main.c", overwriting any existing # "package/main.c" file sys.ln_f "main.c", "package" # ... equivalent to sys.ln_f "main.c", "package/main.c" * ln_s(src, dest) Creates a symbolic link +dest+ which points to +src+. If +dest+ is a directory, creates the symbolic link dest/src. Examples: # Create the symbolic link "NEWS" to the existing file "ChangeLog" sys.ln_s "ChangeLog", "NEWS" # prints "ln -s ChangeLog NEWS" Raises a +SystemCallError+ if +dest+ is the name of an existing file or +src+ doesn't exist. Note:: Not all file systems and operating systems support symbolic links. On operating systems without support for symbolic links, a +NotImplementedError+ exception is risen. If the operating system supports symbolic links, but the file system not, a +SystemCallError+ is risen. * ln_sf(src, dest) Same as ln_s(src, dest), but overwrites +dest+ if +dest+ exists. Example: # Create the symbolic link "NEWS" to the existing file # "ChangeLog", overwrite any existing "NEWS" file. sys.ln_s "ChangeLog", "NEWS" # prints "ln -sf ChangeLog NEWS" * install(src, dest, options = {}) Copy file +src+ to +dest+ if +dest+ doesn't exist or differs from +src+. Install +src+ to dest/src if +dest+ is a directory. If +src+ is an array/filelist, installs each entry in +src+ under the +dest+ directory. Options is a hash which may contain the following keys: :mode:: If given, after copying the mode of the target file(s) is changed to the integer given as value. :preserve:: Takes either +true+ or +false+. If given and +true+, the target file(s) will have the same access and modification times as the source file(s). Examples: # copy the file "./ruby" to "/usr/local/bin/ruby19" and change the # mode of the target file to 0755. sys.install "ruby", "/usr/local/bin/ruby19", :mode => 0755 # install all files in the "lib" directory in "/usr/lib/foo" and # change the access and modification times of the target files to # match those of their source files. sys.install sys["lib/*"], "/usr/lib/foo", :preserve => true * chmod(mode, file) +file+ may be a single file name or an array/filelist. Changes the file permissions of all given files to the bit pattern represented by the integer +mode+. Examples: # make file "/usr/local/bin/ruby" executable sys.chmod 0755, "/usr/local/bin/ruby" # prints "chmod 0755 /usr/local/bin/ruby" # make all files in the "bin" directory executable sys.chmod 0755, sys["bin/*"] Note:: Not all file systems/operating systems support the same permission bits. This method will only set the supported ones. * ruby(arg1, arg2, ...) Starts a new ruby interpreter with the given arguments. Example: sys.ruby "setup.rb", "--prefix=/usr" # prints " setup.rb --prefix=/usr" IMPORTANT:: It does NOT start a subshell. Bad Example: # This probably does not do what was intended. Ruby will search # for the script file with the name "setup.rb --prefix=/usr"! sys.ruby "setup.rb --prefix=/usr" Note:: Rant determines the absolute path to the ruby interpreter which is running the current Rant instance. It uses this path to start a new ruby interpreter. As a result, this method will also work when "ruby" is not on the PATH. * write_to_file(fn, text) Requires import "sys/more" Write the string +text+ to the file with name +fn+. If the file already exists, it is overwritten, otherwise a new file is created. Example: import "sys/more" sys.write_to_file "version", "1.2.0\n" # => prints "writing 6 bytes to file `version'" * write_to_binfile(fn, data) Requires import "sys/more" Like write_to_file(fn, data) but opens the file in binary mode. Example: import "sys/more" require "digest/md5" data = Digest::MD5.digest("some_string") sys.write_to_binfile "hash", data # => prints "writing 16 bytes to file `hash'" * unpack_tgz(fn, options = {}) Requires import "sys/tgz" Unpack the gzipped tar archive file with the file name +fn+ in the current directory. If +options+ contains the key :in, its value will be used as name of the output directory. Example: import "sys/tgz" # Creates the "pkg" directory if it doesn't exist and unpacks all # contents of "rant-0.4.6.tgz" in the "pkg" directory. sys.unpack_tgz "rant-0.4.6.tgz", :in => "pkg" Existing files will be overwritten. * unpack_zip(fn, options = {}) Requires import "sys/zip" Unpack the zip archive file with the file name +fn+ in the current directory. If +options+ contains the key :in, its value will be used as name of the output directory. Example: import "sys/zip" # Creates the "pkg" directory if it doesn't exist and unpacks all # contents of "rant-0.4.6.zip" in the "pkg" directory. sys.unpack_zip "rant-0.4.6.zip", :in => "pkg" Existing files will be overwritten. The following methods are "silent", i.e. they don't print messages to standard output: * pwd Returns the current working directory as string. Example: sys.pwd # => "/home/user" * compare_file(a, b) Returns true if the files +a+ and +b+ have the same contents, false otherwise. Example: unless sys.compare_file("lib/main.c", "/backup/main.c") puts "lib/main.c differs from /backup/main.c" end Raises a +SystemCallError+ if +a+ or +b+ is not a file. * uptodate?(new, old_list) Returns true if the file with name +new+ is newer (checked by file modification time) than all files listed in the +old_list+ array/filelist. A non-existent file (including +new+) is considered older than any other file. Example: unless sys.uptodate?("foo.exe", sys["src/*.c", "include/*.h"]) puts "(re)build of foo.exe required" end * expand_path(path) Resolves any "." or ".." elements in +path+ and expands +path+ to an absolute path. Replaces a leading @ character with an absolute path to the project's root directory. Returns the resulting path string. Examples, assuming current directory is "/home/user/project/sub" and project root directory is "/home/user/project": sys.expand_path("README") # => "/home/user/project/sub/README" sys.expand_path("./README") # => "/home/user/project/sub/README" sys.expand_path("@README") # => "/home/user/project/README" sys.expand_path("../../README") # => "/home/user/README" sys.expand_path("/@README") # => "/@README" sys.expand_path("subsub/./../") # => "/home/user/project/sub" * split_all(path) Splits +path+ into all its elements and returns and array. Examples: sys.split_all("abc") # => ["abc"] sys.split_all("foo/bar") # => ["foo", "bar"] sys.split_all("foo/bar/baz") # => ["foo", "bar", "baz"] * escape(arg) Escapes all spaces in +arg+ for the shell which is used on the current platform. Returns the escaped string. Example: sys.escape("foo bar") # gives on Windows: '"foo bar"' # other systems: 'foo\ bar' If +arg+ is an array (or filelist) all elements are escaped and the resulting strings joined together, seperated by spaces. Example: sys.escape(["foo bar", "arg 2"]) # gives on Windows: '"foo bar" "arg 2"' # other systems: 'foo\ bar arg\ 2' Note:: Might escape more special shell characters in the future. * sp(path) Does the same as escape(path), but also replaces all slashes with backslashes on windows. Example: libdir = "/home/user/program files/d" sources = ["foo bar.d", "util.d"] sys "dmd #{sys.sp sources} -offoo -I#{sys.sp libdir}" # executes the command # on windows: 'dmd "foo bar.d" util.d -offoo -I"/home/user/program files/d"' # other systems: 'dmd foo\ bar.d util.d -offoo -I/home/user/program\ files/d' * regular_filename(path) Replaces all platform dependent filename separators with a slash, thus returning a platform independent filename. Examples: # on windows: sys.regular_filename('foo\bar') # "foo/bar" # on all platforms: sys.regular_filename('foo//bar') # "foo/bar" * glob(pattern1, pattern2, ...) [pattern1, pattern2, ...] Returns a filelist including the given patterns. For a discussion of filelists, read doc/sys_filelist.rdoc[link:files/doc/sys_filelist_rdoc.html]. Examples: # the following two are equivalent sys.glob("*.c", "*.h") sys["*.c", "*.h"] Filelists created with this method, ignore entries starting with a dot. * glob_all(pattern1, pattern2, ...) Like glob(pattern1, pattern2, ...), but the created filelist doesn't ignore entries starting with a dot. * root_dir?(path) Returns true if the given path specifies the root directory on Linux/Unix, a drive followed by a slash or backslash on Windows. Examples: # on Linux/Unix: sys.root_dir?("/") # true sys.root_dir?("/bin") # false sys.root_dir?("bin") # false # on Windows: sys.root_dir?("C:\\") # true # (a reminder: a double backslash in a literal string is treated # as an escape sequence and converted to a single backslash by # ruby) sys.root_dir?("C:/") # true sys.root_dir?("bin") # false * absolute_path?(path) Returns true if the given path is an absolute path, false otherwise. Examples: # on all systems: sys.absolute_path?("/home/me/main.c") # true sys.absolute_path?("main.c") # false # on windows: sys.absolute_path?("C:\\Programs") # true sys.absolute_path?("C:/Programs") # true == See also Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Rant Overview:: README[link:files/README.html] rant-0.5.8/doc/sys_filelist.rdoc0000644000175000017500000000762210527253223016205 0ustar xavierxavier == Using filelists in Rantfiles You can find documentation for Rant's +FileList+ class in the file doc/filelist.rdoc[link:files/doc/filelist_rdoc.html]. This document describes how you can use filelists in your Rant build scripts (Rantfiles). First of all, you don't need to require or import anything to use filelists in Rantfiles. Additionally the Rant::FileList class is directly available as +FileList+. Thus, for example, instead of typing: c_files = Rant::FileList["*.c"] you can type: c_files = FileList["*.c"] Again, you don't need to require "rant/filelist". But if you want to use one of the methods +no_dir+, +files+ or +dirs+ import "filelist/std" is required. Often there are files/directories you want to always ignore when using filelists. This could be the "CVS" directories if you are using the CVS version control system or simply all backup files. Rant allows you to specify a set of patterns once for your project with the sys.ignore method. E.g. place the following line of code in your Rantfile: sys.ignore "CVS", /~$/ In the Rant::FileList documentation there are five ways to create a filelist documented. In Rantfiles: instead of | use ----------------------------------------------------------------- Rant::FileList.new | sys.filelist Rant::FileList[*patterns] | sys[*patterns] Rant::FileList.glob(*patterns) | sys.glob(*patterns) Rant::FileList.glob_all(*patterns) | sys.glob_all(*patterns) Rant::FileList(list) | sys.filelist(list) The sys variants of filelist creation all honour the project wide ignore patterns set by sys.ignore. Let's look at an example. First an Rantfile using the "normal" filelist constructors: all_files = FileList["**/*"].ignore("CVS", /~$/) lib_files = FileList["lib/**/*.rb"].ignore("CVS") pkg_files = FileList["bin/*", "lib/**/*.rb", "test/**/*"].ignore("CVS", /~$/) backup_files = FileList["**/*~"] # define tasks I admit that the example is a bit exaggerated, but in essence you always have to care that you don't accidently include a backup file or some file living under a "CVS" directory. Now look at the equivalent Rantfile using sys to create filelists. sys.ignore "CVS", /~$/ all_files = sys["**/*"] lib_files = sys["lib/**/*.rb"] pkg_files = sys["bin/*", "lib/**/*.rb", "test/**/*"] backup_files = FileList["**/*~"] # define tasks Now, per default CVS directories and backup files won't appear in filelists. We use a "stateless" filelist for +backup_files+, since all backup files would be ignored otherwise. == Where to use filelists? Basically, you can use a filelist wherever you can use an array. The following is a list of use cases where filelists are handy. * As prerequisite list for a task. Example: # get a list of all c files c_files = sys["**/*.c"] # get a list of all resulting object files obj_files = c_files.ext("o") # static library "foo" depends on all object files file "libfoo.a" => obj_files do |t| # build t.name from t.prerequisites (obj_files) end * As argument to file system operations. Example: task :clean do # remove all backup files sys.rm_f FileList["**/*~"] end * If you want to apply an operation to a list of files, e.g. substituting a variable in text files. Example: # Do something with all files in the project import "filelist/std" sys["**/*"].files.each { |filename| # do something with the file "filename" } == See also Rant Overview:: README[link:files/README.html] Rant::FileList documentation:: doc/filelist.rdoc[link:files/doc/filelist_rdoc.html] Rantfile basics:: doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Advanced Rantfiles:: doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] rant-0.5.8/lib/0000755000175000017500000000000010527254520012616 5ustar xavierxavierrant-0.5.8/lib/rant/0000755000175000017500000000000010527254520013562 5ustar xavierxavierrant-0.5.8/lib/rant/archive/0000755000175000017500000000000010527254520015203 5ustar xavierxavierrant-0.5.8/lib/rant/archive/rubyzip/0000755000175000017500000000000010527254520016707 5ustar xavierxavierrant-0.5.8/lib/rant/archive/rubyzip/ioextras.rb0000644000175000017500000000511510527253224021074 0ustar xavierxavier # Taken from the rubyzip package and slightly modified for Rant. module Rant; end module Rant::IOExtras #:nodoc: # Implements kind_of? in order to pretend to be an IO object module FakeIO def kind_of?(object) object == IO || super end end # Implements many of the convenience methods of IO # such as gets, getc, readline and readlines # depends on: input_finished?, produce_input and read module AbstractInputStream include Enumerable include FakeIO def initialize super @lineno = 0 @outputBuffer = "" end attr_accessor :lineno def readlines(aSepString = $/) retVal = [] each_line(aSepString) { |line| retVal << line } return retVal end def gets(aSepString=$/) @lineno = @lineno.next return read if aSepString == nil aSepString="#{$/}#{$/}" if aSepString == "" bufferIndex=0 while ((matchIndex = @outputBuffer.index(aSepString, bufferIndex)) == nil) bufferIndex=@outputBuffer.length if input_finished? return @outputBuffer.empty? ? nil : flush end @outputBuffer << produce_input end sepIndex=matchIndex + aSepString.length return @outputBuffer.slice!(0...sepIndex) end def flush retVal=@outputBuffer @outputBuffer="" return retVal end def readline(aSepString = $/) retVal = gets(aSepString) raise EOFError if retVal == nil return retVal end def each_line(aSepString = $/) while true yield readline(aSepString) end rescue EOFError end alias_method :each, :each_line end # Implements many of the output convenience methods of IO. # relies on << module AbstractOutputStream include FakeIO def write(data) self << data data.to_s.length end def print(*params) self << params.to_s << $\.to_s end def printf(aFormatString, *params) self << sprintf(aFormatString, *params) end def putc(anObject) self << case anObject when Fixnum then anObject.chr when String then anObject else raise TypeError, "putc: Only Fixnum and String supported" end anObject end def puts(*params) params << "\n" if params.empty? params.flatten.each { |element| val = element.to_s self << val self << "\n" unless val[-1,1] == "\n" } end end end # Rant::IOExtras namespace module # Copyright (C) 2002-2004 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rant-0.5.8/lib/rant/archive/rubyzip/stdrubyext.rb0000644000175000017500000000514410527253224021455 0ustar xavierxavier # Taken from the rubyzip package and integrated into Rant. unless Enumerable.method_defined?(:inject) module Enumerable #:nodoc:all def inject(n = 0) each { |value| n = yield(n, value) } n end end end module Enumerable #:nodoc:all # returns a new array of all the return values not equal to nil # This implementation could be faster def select_map(&aProc) map(&aProc).reject { |e| e.nil? } end end unless Object.method_defined?(:object_id) class Object #:nodoc:all # Using object_id which is the new thing, so we need # to make that work in versions prior to 1.8.0 alias object_id id end end unless File.respond_to?(:read) class File # :nodoc:all # singleton method read does not exist in 1.6.x def self.read(fileName) open(fileName) { |f| f.read } end end end class String #:nodoc:all def starts_with(aString) rindex(aString, 0) == 0 end def ends_with(aString) index(aString, -aString.size) end def ensure_end(aString) ends_with(aString) ? self : self + aString end def lchop slice(1, length) end end class Time #:nodoc:all #MS-DOS File Date and Time format as used in Interrupt 21H Function 57H: # # Register CX, the Time: # Bits 0-4 2 second increments (0-29) # Bits 5-10 minutes (0-59) # bits 11-15 hours (0-24) # # Register DX, the Date: # Bits 0-4 day (1-31) # bits 5-8 month (1-12) # bits 9-15 year (four digit year minus 1980) def to_binary_dos_time (sec/2) + (min << 5) + (hour << 11) end def to_binary_dos_date (day) + (month << 5) + ((year - 1980) << 9) end # Dos time is only stored with two seconds accuracy def dos_equals(other) to_i/2 == other.to_i/2 end def self.parse_binary_dos_format(binaryDosDate, binaryDosTime) second = 2 * ( 0b11111 & binaryDosTime) minute = ( 0b11111100000 & binaryDosTime) >> 5 hour = (0b1111100000000000 & binaryDosTime) >> 11 day = ( 0b11111 & binaryDosDate) month = ( 0b111100000 & binaryDosDate) >> 5 year = ((0b1111111000000000 & binaryDosDate) >> 9) + 1980 begin return Time.local(year, month, day, hour, minute, second) end end end class Module #:nodoc:all def forward_message(forwarder, *messagesToForward) methodDefs = messagesToForward.map { |msg| "def #{msg}; #{forwarder}(:#{msg}); end" } module_eval(methodDefs.join("\n")) end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rant-0.5.8/lib/rant/archive/rubyzip/tempfile_bugfixed.rb0000644000175000017500000001046710527253224022726 0ustar xavierxavier# # tempfile - manipulates temporary files # # $Id: tempfile_bugfixed.rb,v 1.2 2005/02/19 20:30:33 thomas Exp $ # require 'delegate' require 'tmpdir' module Rant module BugFix #:nodoc:all # A class for managing temporary files. This library is written to be # thread safe. class Tempfile < DelegateClass(File) MAX_TRY = 10 @@cleanlist = [] # Creates a temporary file of mode 0600 in the temporary directory # whose name is basename.pid.n and opens with mode "w+". A Tempfile # object works just like a File object. # # If tmpdir is omitted, the temporary directory is determined by # Dir::tmpdir provided by 'tmpdir.rb'. # When $SAFE > 0 and the given tmpdir is tainted, it uses # /tmp. (Note that ENV values are tainted by default) def initialize(basename, tmpdir=Dir::tmpdir) if $SAFE > 0 and tmpdir.tainted? tmpdir = '/tmp' end lock = nil n = failure = 0 begin Thread.critical = true begin tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n) lock = tmpname + '.lock' n += 1 end while @@cleanlist.include?(tmpname) or File.exist?(lock) or File.exist?(tmpname) Dir.mkdir(lock) rescue failure += 1 retry if failure < MAX_TRY raise "cannot generate tempfile `%s'" % tmpname ensure Thread.critical = false end @data = [tmpname] @clean_proc = Tempfile.callback(@data) ObjectSpace.define_finalizer(self, @clean_proc) @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600) @tmpname = tmpname @@cleanlist << @tmpname @data[1] = @tmpfile @data[2] = @@cleanlist super(@tmpfile) # Now we have all the File/IO methods defined, you must not # carelessly put bare puts(), etc. after this. Dir.rmdir(lock) end # Opens or reopens the file with mode "r+". def open @tmpfile.close if @tmpfile @tmpfile = File.open(@tmpname, 'r+') @data[1] = @tmpfile __setobj__(@tmpfile) end def _close # :nodoc: @tmpfile.close if @tmpfile @data[1] = @tmpfile = nil end protected :_close # Closes the file. If the optional flag is true, unlinks the file # after closing. # # If you don't explicitly unlink the temporary file, the removal # will be delayed until the object is finalized. def close(unlink_now=false) if unlink_now close! else _close end end # Closes and unlinks the file. def close! _close @clean_proc.call ObjectSpace.undefine_finalizer(self) end # Unlinks the file. On UNIX-like systems, it is often a good idea # to unlink a temporary file immediately after creating and opening # it, because it leaves other programs zero chance to access the # file. def unlink # keep this order for thread safeness File.unlink(@tmpname) if File.exist?(@tmpname) @@cleanlist.delete(@tmpname) if @@cleanlist end alias delete unlink if RUBY_VERSION > '1.8.0' def __setobj__(obj) @_dc_obj = obj end else def __setobj__(obj) @obj = obj end end # Returns the full path name of the temporary file. def path @tmpname end # Returns the size of the temporary file. As a side effect, the IO # buffer is flushed before determining the size. def size if @tmpfile @tmpfile.flush @tmpfile.stat.size else 0 end end alias length size class << self def callback(data) # :nodoc: pid = $$ lambda{ if pid == $$ path, tmpfile, cleanlist = *data print "removing ", path, "..." if $DEBUG tmpfile.close if tmpfile # keep this order for thread safeness File.unlink(path) if File.exist?(path) cleanlist.delete(path) if cleanlist print "done\n" if $DEBUG end } end # If no block is given, this is a synonym for new(). # # If a block is given, it will be passed tempfile as an argument, # and the tempfile will automatically be closed when the block # terminates. In this case, open() returns nil. def open(*args) tempfile = new(*args) if block_given? begin yield(tempfile) ensure tempfile.close end nil else tempfile end end end end end # module BugFix end # module Rant #if __FILE__ == $0 # $DEBUG = true # f = Tempfile.new("foo") # f.print("foo\n") # f.close # f.open # p f.gets # => "foo\n" # f.close! #end rant-0.5.8/lib/rant/archive/minitar.rb0000644000175000017500000007720610527253225017210 0ustar xavierxavier #-- # This is a slightly modified (especially module and classnames) # version of archive-tar-minitar 0.5.1. # # Archive::Tar::Minitar 0.5.1 # Copyright © 2004 Mauricio Julio Fernández Pradier and Austin Ziegler # # This program is based on and incorporates parts of RPA::Package from # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been # adapted to be more generic by Austin. # # It is licensed under the GNU General Public Licence or Ruby's licence. # # $Id: minitar.rb,v 1.3 2004/09/22 17:47:43 austin Exp $ #++ module Rant; end module Rant::Archive; end module Rant::Archive::Minitar; end # = Archive::Tar::PosixHeader # Implements the POSIX tar header as a Ruby class. The structure of # the POSIX tar header is: # # struct tarfile_entry_posix # { // pack/unpack # char name[100]; // ASCII (+ Z unless filled) a100/Z100 # char mode[8]; // 0 padded, octal, null a8 /A8 # char uid[8]; // ditto a8 /A8 # char gid[8]; // ditto a8 /A8 # char size[12]; // 0 padded, octal, null a12 /A12 # char mtime[12]; // 0 padded, octal, null a12 /A12 # char checksum[8]; // 0 padded, octal, null, space a8 /A8 # char typeflag[1]; // see below a /a # char linkname[100]; // ASCII + (Z unless filled) a100/Z100 # char magic[6]; // "ustar\0" a6 /A6 # char version[2]; // "00" a2 /A2 # char uname[32]; // ASCIIZ a32 /Z32 # char gname[32]; // ASCIIZ a32 /Z32 # char devmajor[8]; // 0 padded, octal, null a8 /A8 # char devminor[8]; // 0 padded, octal, null a8 /A8 # char prefix[155]; // ASCII (+ Z unless filled) a155/Z155 # }; # # The +typeflag+ may be one of the following known values: # # "0":: Regular file. NULL should be treated as a synonym, for # compatibility purposes. # "1":: Hard link. # "2":: Symbolic link. # "3":: Character device node. # "4":: Block device node. # "5":: Directory. # "6":: FIFO node. # "7":: Reserved. # # POSIX indicates that "A POSIX-compliant implementation must treat any # unrecognized typeflag value as a regular file." class Rant::Archive::Minitar::PosixHeader FIELDS = %w(name mode uid gid size mtime checksum typeflag linkname) + %w(magic version uname gname devmajor devminor prefix) FIELDS.each { |field| attr_reader field.intern } HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155" HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155" # Creates a new PosixHeader from a data stream. def self.new_from_stream(stream) data = stream.read(512) fields = data.unpack(HEADER_UNPACK_FORMAT) name = fields.shift mode = fields.shift.oct uid = fields.shift.oct gid = fields.shift.oct size = fields.shift.oct mtime = fields.shift.oct checksum = fields.shift.oct typeflag = fields.shift linkname = fields.shift magic = fields.shift version = fields.shift.oct uname = fields.shift gname = fields.shift devmajor = fields.shift.oct devminor = fields.shift.oct prefix = fields.shift empty = (data == "\0" * 512) new(:name => name, :mode => mode, :uid => uid, :gid => gid, :size => size, :mtime => mtime, :checksum => checksum, :typeflag => typeflag, :magic => magic, :version => version, :uname => uname, :gname => gname, :devmajor => devmajor, :devminor => devminor, :prefix => prefix, :empty => empty) end # Creates a new PosixHeader. A PosixHeader cannot be created unless the # #name, #size, #prefix, and #mode are provided. def initialize(vals) unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] raise ArgumentError end vals[:mtime] ||= 0 vals[:checksum] ||= "" vals[:typeflag] ||= "0" vals[:magic] ||= "ustar" vals[:version] ||= "00" FIELDS.each do |field| instance_variable_set("@#{field}", vals[field.intern]) end @empty = vals[:empty] end def empty? @empty end def to_s update_checksum header(@checksum) end # Update the checksum field. def update_checksum hh = header(" " * 8) @checksum = oct(calculate_checksum(hh), 6) end private def oct(num, len) if num.nil? "\0" * (len + 1) else "%0#{len}o" % num end end def calculate_checksum(hdr) hdr.unpack("C*").inject { |aa, bb| aa + bb } end def header(chksum) arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11), oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version, uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix] str = arr.pack(HEADER_PACK_FORMAT) str + "\0" * ((512 - str.size) % 512) end end require 'fileutils' require 'find' # = Archive::Tar::Minitar 0.5.1 # Archive::Tar::Minitar is a pure-Ruby library and command-line # utility that provides the ability to deal with POSIX tar(1) archive # files. The implementation is based heavily on Mauricio Fernández's # implementation in rpa-base, but has been reorganised to promote # reuse in other projects. # # This tar class performs a subset of all tar (POSIX tape archive) # operations. We can only deal with typeflags 0, 1, 2, and 5 (see # Archive::Tar::PosixHeader). All other typeflags will be treated as # normal files. # # NOTE::: support for typeflags 1 and 2 is not yet implemented in this # version. # # This release is version 0.5.1. The library can only handle files and # directories at this point. A future version will be expanded to # handle symbolic links and hard links in a portable manner. The # command line utility, minitar, can only create archives, extract # from archives, and list archive contents. # # == Synopsis # Using this library is easy. The simplest case is: # # require 'zlib' # require 'archive/tar/minitar' # include Archive::Tar # # # Packs everything that matches Find.find('tests') # File.open('test.tar', 'wb') { |tar| Minitar.pack('tests', tar) } # # Unpacks 'test.tar' to 'x', creating 'x' if necessary. # Minitar.unpack('test.tar', 'x') # # A gzipped tar can be written with: # # tgz = Zlib::GzipWriter.new(File.open('test.tgz', 'wb')) # # Warning: tgz will be closed! # Minitar.pack('tests', tgz) # # tgz = Zlib::GzipReader.new(File.open('test.tgz', 'rb')) # # Warning: tgz will be closed! # Minitar.unpack(tgz, 'x') # # As the case above shows, one need not write to a file. However, it # will sometimes require that one dive a little deeper into the API, # as in the case of StringIO objects. Note that I'm not providing a # block with Minitar::Output, as Minitar::Output#close automatically # closes both the Output object and the wrapped data stream object. # # begin # sgz = Zlib::GzipWriter.new(StringIO.new("")) # tar = Output.new(sgz) # Find.find('tests') do |entry| # Minitar.pack_file(entry, tar) # end # ensure # # Closes both tar and sgz. # tar.close # end # # == Copyright # Copyright 2004 Mauricio Julio Fernández Pradier and Austin Ziegler # # This program is based on and incorporates parts of RPA::Package from # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and # has been adapted to be more generic by Austin. # # 'minitar' contains an adaptation of Ruby/ProgressBar by Satoru # Takabayashi , copyright © 2001 - 2004. # # This program is free software. It may be redistributed and/or # modified under the terms of the GPL version 2 (or later) or Ruby's # licence. module Rant::Archive::Minitar VERSION = "0.5.1" # The exception raised when a wrapped data stream class is expected to # respond to #rewind or #pos but does not. class NonSeekableStream < StandardError; end # The exception raised when a block is required for proper operation of # the method. class BlockRequired < ArgumentError; end # The exception raised when operations are performed on a stream that has # previously been closed. class ClosedStream < StandardError; end # The exception raised when a filename exceeds 256 bytes in length, # the maximum supported by the standard Tar format. class FileNameTooLong < StandardError; end # The exception raised when a data stream ends before the amount of data # expected in the archive's PosixHeader. class UnexpectedEOF < StandardError; end # The class that writes a tar format archive to a data stream. class Writer # A stream wrapper that can only be written to. Any attempt to read # from this restricted stream will result in a NameError being thrown. class RestrictedStream def initialize(anIO) @io = anIO end def write(data) @io.write(data) end end # A RestrictedStream that also has a size limit. class BoundedStream < Rant::Archive::Minitar::Writer::RestrictedStream # The exception raised when the user attempts to write more data to # a BoundedStream than has been allocated. class FileOverflow < RuntimeError; end # The maximum number of bytes that may be written to this data # stream. attr_reader :limit # The current total number of bytes written to this data stream. attr_reader :written def initialize(io, limit) @io = io @limit = limit @written = 0 end def write(data) raise FileOverflow if (data.size + @written) > @limit @io.write(data) @written += data.size data.size end end # With no associated block, +Writer::open+ is a synonym for # +Writer::new+. If the optional code block is given, it will be # passed the new _writer_ as an argument and the Writer object will # automatically be closed when the block terminates. In this instance, # +Writer::open+ returns the value of the block. def self.open(anIO) writer = Writer.new(anIO) return writer unless block_given? begin res = yield writer ensure writer.close end res end # Creates and returns a new Writer object. def initialize(anIO) @io = anIO @closed = false end # Adds a file to the archive as +name+. +opts+ must contain the # following values: # # :mode:: The Unix file permissions mode value. # :size:: The size, in bytes. # # +opts+ may contain the following values: # # :uid: The Unix file owner user ID number. # :gid: The Unix file owner group ID number. # :mtime:: The *integer* modification time value. # # It will not be possible to add more than opts[:size] bytes # to the file. def add_file_simple(name, opts = {}) # :yields BoundedStream: raise Rant::Archive::Minitar::BlockRequired unless block_given? raise Rant::Archive::Minitar::ClosedStream if @closed name, prefix = split_name(name) header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime], :size => opts[:size], :gid => opts[:gid], :uid => opts[:uid], :prefix => prefix } header = Rant::Archive::Minitar::PosixHeader.new(header).to_s @io.write(header) os = BoundedStream.new(@io, opts[:size]) yield os # FIXME: what if an exception is raised in the block? min_padding = opts[:size] - os.written @io.write("\0" * min_padding) remainder = (512 - (opts[:size] % 512)) % 512 @io.write("\0" * remainder) end # Adds a file to the archive as +name+. +opts+ must contain the # following value: # # :mode:: The Unix file permissions mode value. # # +opts+ may contain the following values: # # :uid: The Unix file owner user ID number. # :gid: The Unix file owner group ID number. # :mtime:: The *integer* modification time value. # # The file's size will be determined from the amount of data written # to the stream. # # For #add_file to be used, the Archive::Tar::Minitar::Writer must be # wrapping a stream object that is seekable (e.g., it responds to # #pos=). Otherwise, #add_file_simple must be used. # # +opts+ may be modified during the writing to the stream. def add_file(name, opts = {}) # :yields RestrictedStream, +opts+: raise Rant::Archive::Minitar::BlockRequired unless block_given? raise Rant::Archive::Minitar::ClosedStream if @closed raise Rant::Archive::Minitar::NonSeekableStream unless @io.respond_to?(:pos=) name, prefix = split_name(name) init_pos = @io.pos @io.write("\0" * 512) # placeholder for the header yield RestrictedStream.new(@io), opts # FIXME: what if an exception is raised in the block? size = @io.pos - (init_pos + 512) remainder = (512 - (size % 512)) % 512 @io.write("\0" * remainder) final_pos = @io.pos @io.pos = init_pos header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime], :size => size, :gid => opts[:gid], :uid => opts[:uid], :prefix => prefix } header = Rant::Archive::Minitar::PosixHeader.new(header).to_s @io.write(header) @io.pos = final_pos end # Creates a directory in the tar. def mkdir(name, opts = {}) raise ClosedStream if @closed name, prefix = split_name(name) header = { :name => name, :mode => opts[:mode], :typeflag => "5", :size => 0, :gid => opts[:gid], :uid => opts[:uid], :mtime => opts[:mtime], :prefix => prefix } header = Rant::Archive::Minitar::PosixHeader.new(header).to_s @io.write(header) nil end # Passes the #flush method to the wrapped stream, used for buffered # streams. def flush raise ClosedStream if @closed @io.flush if @io.respond_to?(:flush) end # Closes the Writer. def close return if @closed @io.write("\0" * 1024) @closed = true end private def split_name(name) raise FileNameTooLong if name.size > 256 if name.size <= 100 prefix = "" else parts = name.split(/\//) newname = parts.pop nxt = "" loop do nxt = parts.pop break if newname.size + 1 + nxt.size > 100 newname = "#{nxt}/#{newname}" end prefix = (parts + [nxt]).join("/") name = newname raise FileNameTooLong if name.size > 100 || prefix.size > 155 end return name, prefix end end # The class that reads a tar format archive from a data stream. The data # stream may be sequential or random access, but certain features only work # with random access data streams. class Reader # This marks the EntryStream closed for reading without closing the # actual data stream. module InvalidEntryStream def read(len = nil); raise ClosedStream; end def getc; raise ClosedStream; end def rewind; raise ClosedStream; end end # EntryStreams are pseudo-streams on top of the main data stream. class EntryStream Rant::Archive::Minitar::PosixHeader::FIELDS.each do |field| attr_reader field.intern end def initialize(header, anIO) @io = anIO @name = header.name @mode = header.mode @uid = header.uid @gid = header.gid @size = header.size @mtime = header.mtime @checksum = header.checksum @typeflag = header.typeflag @linkname = header.linkname @magic = header.magic @version = header.version @uname = header.uname @gname = header.gname @devmajor = header.devmajor @devminor = header.devminor @prefix = header.prefix @read = 0 @orig_pos = @io.pos end # Reads +len+ bytes (or all remaining data) from the entry. Returns # +nil+ if there is no more data to read. def read(len = nil) return nil if @read >= @size len ||= @size - @read max_read = [len, @size - @read].min ret = @io.read(max_read) @read += ret.size ret end # Reads one byte from the entry. Returns +nil+ if there is no more data # to read. def getc return nil if @read >= @size ret = @io.getc @read += 1 if ret ret end # Returns +true+ if the entry represents a directory. def directory? @typeflag == "5" end alias_method :directory, :directory? # Returns +true+ if the entry represents a plain file. def file? @typeflag == "0" end alias_method :file, :file? # Returns +true+ if the current read pointer is at the end of the # EntryStream data. def eof? @read >= @size end # Returns the current read pointer in the EntryStream. def pos @read end # Sets the current read pointer to the beginning of the EntryStream. def rewind raise NonSeekableStream unless @io.respond_to?(:pos=) @io.pos = @orig_pos @read = 0 end def bytes_read @read end # Returns the full and proper name of the entry. def full_name if @prefix != "" File.join(@prefix, @name) else @name end end # Closes the entry. def close invalidate end private def invalidate extend InvalidEntryStream end end # With no associated block, +Reader::open+ is a synonym for # +Reader::new+. If the optional code block is given, it will be passed # the new _writer_ as an argument and the Reader object will # automatically be closed when the block terminates. In this instance, # +Reader::open+ returns the value of the block. def self.open(anIO) reader = Reader.new(anIO) return reader unless block_given? begin res = yield reader ensure reader.close end res end # Creates and returns a new Reader object. def initialize(anIO) @io = anIO @init_pos = anIO.pos end # Iterates through each entry in the data stream. def each(&block) each_entry(&block) end # Resets the read pointer to the beginning of data stream. Do not call # this during a #each or #each_entry iteration. This only works with # random access data streams that respond to #rewind and #pos. def rewind if @init_pos == 0 raise NonSeekableStream unless @io.respond_to?(:rewind) @io.rewind else raise NonSeekableStream unless @io.respond_to?(:pos=) @io.pos = @init_pos end end # Iterates through each entry in the data stream. def each_entry loop do return if @io.eof? header = Rant::Archive::Minitar::PosixHeader.new_from_stream(@io) return if header.empty? entry = EntryStream.new(header, @io) size = entry.size yield entry skip = (512 - (size % 512)) % 512 if @io.respond_to?(:seek) # avoid reading... @io.seek(size - entry.bytes_read, IO::SEEK_CUR) else pending = size - entry.bytes_read while pending > 0 bread = @io.read([pending, 4096].min).size raise UnexpectedEOF if @io.eof? pending -= bread end end @io.read(skip) # discard trailing zeros # make sure nobody can use #read, #getc or #rewind anymore entry.close end end def close end end # Wraps a Archive::Tar::Minitar::Reader with convenience methods and # wrapped stream management; Input only works with random access data # streams. See Input::new for details. class Input include Enumerable # With no associated block, +Input::open+ is a synonym for # +Input::new+. If the optional code block is given, it will be passed # the new _writer_ as an argument and the Input object will # automatically be closed when the block terminates. In this instance, # +Input::open+ returns the value of the block. def self.open(input) stream = Input.new(input) return stream unless block_given? begin res = yield stream ensure stream.close end res end # Creates a new Input object. If +input+ is a stream object that responds # to #read), then it will simply be wrapped. Otherwise, one will be # created and opened using Kernel#open. When Input#close is called, the # stream object wrapped will be closed. def initialize(input) if input.respond_to?(:read) @io = input else @io = open(input, "rb") end @tarreader = Rant::Archive::Minitar::Reader.new(@io) end # Iterates through each entry and rewinds to the beginning of the stream # when finished. def each(&block) @tarreader.each { |entry| yield entry } ensure @tarreader.rewind end # Extracts the current +entry+ to +destdir+. If a block is provided, it # yields an +action+ Symbol, the full name of the file being extracted # (+name+), and a Hash of statistical information (+stats+). # # The +action+ will be one of: # :dir:: The +entry+ is a directory. # :file_start:: The +entry+ is a file; the extract of the # file is just beginning. # :file_progress:: Yielded every 4096 bytes during the extract # of the +entry+. # :file_done:: Yielded when the +entry+ is completed. # # The +stats+ hash contains the following keys: # :current:: The current total number of bytes read in the # +entry+. # :currinc:: The current number of bytes read in this read # cycle. # :entry:: The entry being extracted; this is a # Reader::EntryStream, with all methods thereof. def extract_entry(destdir, entry) # :yields action, name, stats: stats = { :current => 0, :currinc => 0, :entry => entry } if entry.directory? dest = File.join(destdir, entry.full_name) yield :dir, entry.full_name, stats if block_given? if Rant::Archive::Minitar.dir?(dest) begin FileUtils.chmod(entry.mode, dest) rescue Exception nil end else FileUtils.mkdir_p(dest, :mode => entry.mode) FileUtils.chmod(entry.mode, dest) end fsync_dir(dest) fsync_dir(File.join(dest, "..")) return else # it's a file destdir = File.join(destdir, File.dirname(entry.full_name)) FileUtils.mkdir_p(destdir, :mode => 0755) destfile = File.join(destdir, File.basename(entry.full_name)) FileUtils.chmod(0600, destfile) rescue nil # Errno::ENOENT yield :file_start, entry.full_name, stats if block_given? File.open(destfile, "wb", entry.mode) do |os| loop do data = entry.read(4096) break unless data stats[:currinc] = os.write(data) stats[:current] += stats[:currinc] yield :file_progress, entry.full_name, stats if block_given? end os.fsync end FileUtils.chmod(entry.mode, destfile) fsync_dir(File.dirname(destfile)) fsync_dir(File.join(File.dirname(destfile), "..")) yield :file_done, entry.full_name, stats if block_given? end end # Returns the Reader object for direct access. def tar @tarreader end # Closes the Reader object and the wrapped data stream. def close @io.close @tarreader.close end private def fsync_dir(dirname) # make sure this hits the disc dir = open(dirname, 'rb') dir.fsync rescue # ignore IOError if it's an unpatched (old) Ruby nil ensure dir.close if dir rescue nil end end # Wraps a Archive::Tar::Minitar::Writer with convenience methods and # wrapped stream management; Output only works with random access data # streams. See Output::new for details. class Output # With no associated block, +Output::open+ is a synonym for # +Output::new+. If the optional code block is given, it will be passed # the new _writer_ as an argument and the Output object will # automatically be closed when the block terminates. In this instance, # +Output::open+ returns the value of the block. def self.open(output) stream = Output.new(output) return stream unless block_given? begin res = yield stream ensure stream.close end res end # Creates a new Output object. If +output+ is a stream object that # responds to #read), then it will simply be wrapped. Otherwise, one will # be created and opened using Kernel#open. When Output#close is called, # the stream object wrapped will be closed. def initialize(output) if output.respond_to?(:write) @io = output else @io = ::File.open(output, "wb") end @tarwriter = Rant::Archive::Minitar::Writer.new(@io) end # Returns the Writer object for direct access. def tar @tarwriter end # Closes the Writer object and the wrapped data stream. def close @tarwriter.close @io.close end end class << self # Tests if +path+ refers to a directory. Fixes an apparently # corrupted stat() call on Windows. def dir?(path) File.directory?((path[-1] == ?/) ? path : "#{path}/") end # A convenience method for wrapping Archive::Tar::Minitar::Input.open # (mode +r+) and Archive::Tar::Minitar::Output.open (mode +w+). No other # modes are currently supported. def open(dest, mode = "r", &block) case mode when "r" Input.open(dest, &block) when "w" Output.open(dest, &block) else raise "Unknown open mode for Rant::Archive::Minitar.open." end end # A convenience method to packs the file provided. +entry+ may either be # a filename (in which case various values for the file (see below) will # be obtained from File#stat(entry) or a Hash with the fields: # # :name:: The filename to be packed into the tarchive. # *REQUIRED*. # :mode:: The mode to be applied. # :uid:: The user owner of the file. (Ignored on Windows.) # :gid:: The group owner of the file. (Ignored on Windows.) # :mtime:: The modification Time of the file. # # During packing, if a block is provided, #pack_file yields an +action+ # Symol, the full name of the file being packed, and a Hash of # statistical information, just as with # Archive::Tar::Minitar::Input#extract_entry. # # The +action+ will be one of: # :dir:: The +entry+ is a directory. # :file_start:: The +entry+ is a file; the extract of the # file is just beginning. # :file_progress:: Yielded every 4096 bytes during the extract # of the +entry+. # :file_done:: Yielded when the +entry+ is completed. # # The +stats+ hash contains the following keys: # :current:: The current total number of bytes read in the # +entry+. # :currinc:: The current number of bytes read in this read # cycle. # :name:: The filename to be packed into the tarchive. # *REQUIRED*. # :mode:: The mode to be applied. # :uid:: The user owner of the file. (+nil+ on Windows.) # :gid:: The group owner of the file. (+nil+ on Windows.) # :mtime:: The modification Time of the file. def pack_file(entry, outputter) #:yields action, name, stats: outputter = outputter.tar if outputter.kind_of?(Rant::Archive::Minitar::Output) stats = {} if entry.kind_of?(Hash) name = entry[:name] entry.each { |kk, vv| stats[kk] = vv unless vv.nil? } else name = entry end name = name.sub(%r{\./}, '') stat = File.stat(name) stats[:mode] ||= stat.mode stats[:mtime] ||= stat.mtime stats[:size] = stat.size if RUBY_PLATFORM =~ /win32/ stats[:uid] = nil stats[:gid] = nil else stats[:uid] ||= stat.uid stats[:gid] ||= stat.gid end case when File.file?(name) outputter.add_file_simple(name, stats) do |os| stats[:current] = 0 yield :file_start, name, stats if block_given? File.open(name, "rb") do |ff| until ff.eof? stats[:currinc] = os.write(ff.read(4096)) stats[:current] += stats[:currinc] yield :file_progress, name, stats if block_given? end end yield :file_done, name, stats if block_given? end when dir?(name) yield :dir, name, stats if block_given? outputter.mkdir(name, stats) else raise "Don't yet know how to pack this type of file." end end # A convenience method to pack files specified by +src+ into +dest+. If # +src+ is an Array, then each file detailed therein will be packed into # the resulting Archive::Tar::Minitar::Output stream; if +recurse_dirs+ # is true, then directories will be recursed. # # If +src+ is an Array, it will be treated as the argument to Find.find; # all files matching will be packed. def pack(src, dest, recurse_dirs = true, &block) Output.open(dest) do |outp| if src.kind_of?(Array) src.each do |entry| pack_file(entry, outp, &block) if dir?(entry) and recurse_dirs Dir["#{entry}/**/**"].each do |ee| pack_file(ee, outp, &block) end end end else Find.find(src) do |entry| pack_file(entry, outp, &block) end end end end # A convenience method to unpack files from +src+ into the directory # specified by +dest+. Only those files named explicitly in +files+ # will be extracted. def unpack(src, dest, files = [], &block) Input.open(src) do |inp| if File.exist?(dest) and (not dir?(dest)) raise "Can't unpack to a non-directory." elsif not File.exist?(dest) FileUtils.mkdir_p(dest) end inp.each do |entry| if files.empty? or files.include?(entry.full_name) inp.extract_entry(dest, entry, &block) end end end end end end rant-0.5.8/lib/rant/archive/rubyzip.rb0000644000175000017500000012562210527253225017245 0ustar xavierxavier # This file and the files in the rubyzip subdirectory contain a # slightly modified (especially module and classnames) version of # rubyzip 0.5.8. The following four sections are copied from the README # file in the rubyzip package. # # = License # # rubyzip is distributed under the same license as ruby. See # http://www.ruby-lang.org/en/LICENSE.txt # # # = Website and Project Home # # http://rubyzip.sourceforge.net # # http://sourceforge.net/projects/rubyzip # # = Download (tarballs and gems) # # http://sourceforge.net/project/showfiles.php?group_id=43107&package_id=35377 # # = Authors # # Thomas Sondergaard (thomas at sondergaard.cc) # # extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org) require 'delegate' require 'singleton' require 'ftools' require 'zlib' require 'rant/archive/rubyzip/stdrubyext' require 'rant/archive/rubyzip/ioextras' require 'rant/tempfile' module Zlib #:nodoc:all if ! const_defined? :MAX_WBITS MAX_WBITS = Zlib::Deflate.MAX_WBITS end end module Rant; end module Rant::Archive; end module Rant::Archive::Rubyzip VERSION = '0.5.8' RUBY_MINOR_VERSION = RUBY_VERSION.split(".")[1].to_i # Ruby 1.7.x compatibility # In ruby 1.6.x and 1.8.0 reading from an empty stream returns # an empty string the first time and then nil. # not so in 1.7.x EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST = RUBY_MINOR_VERSION != 7 # ZipInputStream is the basic class for reading zip entries in a # zip file. It is possible to create a ZipInputStream object directly, # passing the zip file name to the constructor, but more often than not # the ZipInputStream will be obtained from a ZipFile (perhaps using the # ZipFileSystem interface) object for a particular entry in the zip # archive. # # A ZipInputStream inherits IOExtras::AbstractInputStream in order # to provide an IO-like interface for reading from a single zip # entry. Beyond methods for mimicking an IO-object it contains # the method get_next_entry for iterating through the entries of # an archive. get_next_entry returns a ZipEntry object that describes # the zip entry the ZipInputStream is currently reading from. # # Example that creates a zip archive with ZipOutputStream and reads it # back again with a ZipInputStream. # # require 'zip/zip' # # Zip::ZipOutputStream::open("my.zip") { # |io| # # io.put_next_entry("first_entry.txt") # io.write "Hello world!" # # io.put_next_entry("adir/first_entry.txt") # io.write "Hello again!" # } # # # Zip::ZipInputStream::open("my.zip") { # |io| # # while (entry = io.get_next_entry) # puts "Contents of #{entry.name}: '#{io.read}'" # end # } # # java.util.zip.ZipInputStream is the original inspiration for this # class. class ZipInputStream include Rant::IOExtras::AbstractInputStream # Opens the indicated zip file. An exception is thrown # if the specified offset in the specified filename is # not a local zip entry header. def initialize(filename, offset = 0) super() @archiveIO = File.open(filename, "rb") @archiveIO.seek(offset, IO::SEEK_SET) @decompressor = NullDecompressor.instance @currentEntry = nil end def close @archiveIO.close end # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block # returns. def ZipInputStream.open(filename) return new(filename) unless block_given? zio = new(filename) yield zio ensure zio.close if zio end # Returns a ZipEntry object. It is necessary to call this # method on a newly created ZipInputStream before reading from # the first entry in the archive. Returns nil when there are # no more entries. def get_next_entry @archiveIO.seek(@currentEntry.next_header_offset, IO::SEEK_SET) if @currentEntry open_entry end # Rewinds the stream to the beginning of the current entry def rewind return if @currentEntry.nil? @lineno = 0 @archiveIO.seek(@currentEntry.localHeaderOffset, IO::SEEK_SET) open_entry end # Modeled after IO.read def read(numberOfBytes = nil) @decompressor.read(numberOfBytes) end protected def open_entry @currentEntry = ZipEntry.read_local_entry(@archiveIO) if (@currentEntry == nil) @decompressor = NullDecompressor.instance elsif @currentEntry.compression_method == ZipEntry::STORED @decompressor = PassThruDecompressor.new(@archiveIO, @currentEntry.size) elsif @currentEntry.compression_method == ZipEntry::DEFLATED @decompressor = Inflater.new(@archiveIO) else raise ZipCompressionMethodError, "Unsupported compression method #{@currentEntry.compression_method}" end flush return @currentEntry end def produce_input @decompressor.produce_input end def input_finished? @decompressor.input_finished? end end class Decompressor #:nodoc:all CHUNK_SIZE=32768 def initialize(inputStream) super() @inputStream=inputStream end end class Inflater < Decompressor #:nodoc:all def initialize(inputStream) super @zlibInflater = Zlib::Inflate.new(-Zlib::MAX_WBITS) @outputBuffer="" @hasReturnedEmptyString = ! EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST end def read(numberOfBytes = nil) readEverything = (numberOfBytes == nil) while (readEverything || @outputBuffer.length < numberOfBytes) break if internal_input_finished? @outputBuffer << internal_produce_input end return value_when_finished if @outputBuffer.length==0 && input_finished? endIndex= numberOfBytes==nil ? @outputBuffer.length : numberOfBytes return @outputBuffer.slice!(0...endIndex) end def produce_input if (@outputBuffer.empty?) return internal_produce_input else return @outputBuffer.slice!(0...(@outputBuffer.length)) end end # to be used with produce_input, not read (as read may still have more data cached) def input_finished? @outputBuffer.empty? && internal_input_finished? end private def internal_produce_input @zlibInflater.inflate(@inputStream.read(Decompressor::CHUNK_SIZE)) end def internal_input_finished? @zlibInflater.finished? end # TODO: Specialize to handle different behaviour in ruby > 1.7.0 ? def value_when_finished # mimic behaviour of ruby File object. return nil if @hasReturnedEmptyString @hasReturnedEmptyString=true return "" end end class PassThruDecompressor < Decompressor #:nodoc:all def initialize(inputStream, charsToRead) super inputStream @charsToRead = charsToRead @readSoFar = 0 @hasReturnedEmptyString = ! EMPTY_FILE_RETURNS_EMPTY_STRING_FIRST end # TODO: Specialize to handle different behaviour in ruby > 1.7.0 ? def read(numberOfBytes = nil) if input_finished? hasReturnedEmptyStringVal=@hasReturnedEmptyString @hasReturnedEmptyString=true return "" unless hasReturnedEmptyStringVal return nil end if (numberOfBytes == nil || @readSoFar+numberOfBytes > @charsToRead) numberOfBytes = @charsToRead-@readSoFar end @readSoFar += numberOfBytes @inputStream.read(numberOfBytes) end def produce_input read(Decompressor::CHUNK_SIZE) end def input_finished? (@readSoFar >= @charsToRead) end end class NullDecompressor #:nodoc:all include Singleton def read(numberOfBytes = nil) nil end def produce_input nil end def input_finished? true end end class NullInputStream < NullDecompressor #:nodoc:all include Rant::IOExtras::AbstractInputStream end class ZipEntry STORED = 0 DEFLATED = 8 attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method, :name, :size, :localHeaderOffset, :zipfile, :fstype, :externalFileAttributes def initialize(zipfile = "", name = "", comment = "", extra = "", compressed_size = 0, crc = 0, compression_method = ZipEntry::DEFLATED, size = 0, time = Time.now) super() if name.starts_with("/") raise ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /" end @localHeaderOffset = 0 @internalFileAttributes = 1 @externalFileAttributes = 0 @version = 52 # this library's version @fstype = 0 # default is fat @zipfile, @comment, @compressed_size, @crc, @extra, @compression_method, @name, @size = zipfile, comment, compressed_size, crc, extra, compression_method, name, size @time = time unless ZipExtraField === @extra @extra = ZipExtraField.new(@extra.to_s) end end def time if @extra["UniversalTime"] @extra["UniversalTime"].mtime else # Atandard time field in central directory has local time # under archive creator. Then, we can't get timezone. @time end end alias :mtime :time def time=(aTime) unless @extra.member?("UniversalTime") @extra.create("UniversalTime") end @extra["UniversalTime"].mtime = aTime @time = aTime end def directory? return (%r{\/$} =~ @name) != nil end alias :is_directory :directory? def file? ! directory? end def local_entry_offset #:nodoc:all localHeaderOffset + local_header_size end def local_header_size #:nodoc:all LOCAL_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.size : 0) + (@extra ? @extra.local_size : 0) end def cdir_header_size #:nodoc:all CDIR_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.size : 0) + (@extra ? @extra.c_dir_size : 0) + (@comment ? @comment.size : 0) end def next_header_offset #:nodoc:all local_entry_offset + self.compressed_size end def to_s @name end protected def ZipEntry.read_zip_short(io) io.read(2).unpack('v')[0] end def ZipEntry.read_zip_long(io) io.read(4).unpack('V')[0] end public LOCAL_ENTRY_SIGNATURE = 0x04034b50 LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30 def read_local_entry(io) #:nodoc:all @localHeaderOffset = io.tell staticSizedFieldsBuf = io.read(LOCAL_ENTRY_STATIC_HEADER_LENGTH) unless (staticSizedFieldsBuf.size==LOCAL_ENTRY_STATIC_HEADER_LENGTH) raise ZipError, "Premature end of file. Not enough data for zip entry local header" end localHeader , @version , @fstype , @gpFlags , @compression_method, lastModTime , lastModDate , @crc , @compressed_size , @size , nameLength , extraLength = staticSizedFieldsBuf.unpack('VCCvvvvVVVvv') unless (localHeader == LOCAL_ENTRY_SIGNATURE) raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'" end set_time(lastModDate, lastModTime) @name = io.read(nameLength) extra = io.read(extraLength) if (extra && extra.length != extraLength) raise ZipError, "Truncated local zip entry header" else if ZipExtraField === @extra @extra.merge(extra) else @extra = ZipExtraField.new(extra) end end end def ZipEntry.read_local_entry(io) entry = new(io.path) entry.read_local_entry(io) return entry rescue ZipError return nil end def write_local_entry(io) #:nodoc:all @localHeaderOffset = io.tell io << [LOCAL_ENTRY_SIGNATURE , 0 , 0 , # @gpFlags , @compression_method , @time.to_binary_dos_time , # @lastModTime , @time.to_binary_dos_date , # @lastModDate , @crc , @compressed_size , @size , @name ? @name.length : 0, @extra? @extra.local_length : 0 ].pack('VvvvvvVVVvv') io << @name io << (@extra ? @extra.to_local_bin : "") end CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50 CDIR_ENTRY_STATIC_HEADER_LENGTH = 46 def read_c_dir_entry(io) #:nodoc:all staticSizedFieldsBuf = io.read(CDIR_ENTRY_STATIC_HEADER_LENGTH) unless (staticSizedFieldsBuf.size == CDIR_ENTRY_STATIC_HEADER_LENGTH) raise ZipError, "Premature end of file. Not enough data for zip cdir entry header" end cdirSignature , @version , # version of encoding software @fstype , # filesystem type @versionNeededToExtract, @gpFlags , @compression_method , lastModTime , lastModDate , @crc , @compressed_size , @size , nameLength , extraLength , commentLength , diskNumberStart , @internalFileAttributes, @externalFileAttributes, @localHeaderOffset , @name , @extra , @comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV') unless (cdirSignature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE) raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'" end set_time(lastModDate, lastModTime) @name = io.read(nameLength) if ZipExtraField === @extra @extra.merge(io.read(extraLength)) else @extra = ZipExtraField.new(io.read(extraLength)) end @comment = io.read(commentLength) unless (@comment && @comment.length == commentLength) raise ZipError, "Truncated cdir zip entry header" end end def ZipEntry.read_c_dir_entry(io) #:nodoc:all entry = new(io.path) entry.read_c_dir_entry(io) return entry rescue ZipError return nil end def write_c_dir_entry(io) #:nodoc:all io << [CENTRAL_DIRECTORY_ENTRY_SIGNATURE, @version , # version of encoding software @fstype , # filesystem type 0 , # @versionNeededToExtract , 0 , # @gpFlags , @compression_method , @time.to_binary_dos_time , # @lastModTime , @time.to_binary_dos_date , # @lastModDate , @crc , @compressed_size , @size , @name ? @name.length : 0 , @extra ? @extra.c_dir_length : 0 , @comment ? comment.length : 0 , 0 , # disk number start @internalFileAttributes , # file type (binary=0, text=1) @externalFileAttributes , # native filesystem attributes @localHeaderOffset , @name , @extra , @comment ].pack('VCCvvvvvVVVvvvvvVV') io << @name io << (@extra ? @extra.to_c_dir_bin : "") io << @comment end def == (other) return false unless other.class == ZipEntry # Compares contents of local entry and exposed fields (@compression_method == other.compression_method && @crc == other.crc && @compressed_size == other.compressed_size && @size == other.size && @name == other.name && @extra == other.extra && self.time.dos_equals(other.time)) end def <=> (other) return to_s <=> other.to_s end def get_input_stream zis = ZipInputStream.new(@zipfile, localHeaderOffset) zis.get_next_entry if block_given? begin return yield(zis) ensure zis.close end else return zis end end def write_to_zip_output_stream(aZipOutputStream) #:nodoc:all aZipOutputStream.copy_raw_entry(self) end def parent_as_string entry_name = name.chomp("/") slash_index = entry_name.rindex("/") slash_index ? entry_name.slice(0, slash_index+1) : nil end def get_raw_input_stream(&aProc) File.open(@zipfile, "rb", &aProc) end private def set_time(binaryDosDate, binaryDosTime) @time = Time.parse_binary_dos_format(binaryDosDate, binaryDosTime) rescue ArgumentError puts "Invalid date/time in zip entry" end end # ZipOutputStream is the basic class for writing zip files. It is # possible to create a ZipOutputStream object directly, passing # the zip file name to the constructor, but more often than not # the ZipOutputStream will be obtained from a ZipFile (perhaps using the # ZipFileSystem interface) object for a particular entry in the zip # archive. # # A ZipOutputStream inherits IOExtras::AbstractOutputStream in order # to provide an IO-like interface for writing to a single zip # entry. Beyond methods for mimicking an IO-object it contains # the method put_next_entry that closes the current entry # and creates a new. # # Please refer to ZipInputStream for example code. # # java.util.zip.ZipOutputStream is the original inspiration for this # class. class ZipOutputStream include Rant::IOExtras::AbstractOutputStream attr_accessor :comment # Opens the indicated zip file. If a file with that name already # exists it will be overwritten. def initialize(fileName) super() @fileName = fileName @outputStream = File.new(@fileName, "wb") @entrySet = ZipEntrySet.new @compressor = NullCompressor.instance @closed = false @currentEntry = nil @comment = nil end # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block # returns. def ZipOutputStream.open(fileName) return new(fileName) unless block_given? zos = new(fileName) yield zos ensure zos.close if zos end # Closes the stream and writes the central directory to the zip file def close return if @closed finalize_current_entry update_local_headers write_central_directory @outputStream.close @closed = true end # Closes the current entry and opens a new for writing. # +entry+ can be a ZipEntry object or a string. def put_next_entry(entry, level = Zlib::DEFAULT_COMPRESSION) raise ZipError, "zip stream is closed" if @closed newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@fileName, entry.to_s) init_next_entry(newEntry) @currentEntry=newEntry end def copy_raw_entry(entry) entry = entry.dup raise ZipError, "zip stream is closed" if @closed raise ZipError, "entry is not a ZipEntry" if !entry.kind_of?(ZipEntry) finalize_current_entry @entrySet << entry src_pos = entry.local_entry_offset entry.write_local_entry(@outputStream) @compressor = NullCompressor.instance @outputStream << entry.get_raw_input_stream { |is| is.seek(src_pos, IO::SEEK_SET) is.read(entry.compressed_size) } @compressor = NullCompressor.instance @currentEntry = nil end private def finalize_current_entry return unless @currentEntry finish @currentEntry.compressed_size = @outputStream.tell - @currentEntry.localHeaderOffset - @currentEntry.local_header_size @currentEntry.size = @compressor.size @currentEntry.crc = @compressor.crc @currentEntry = nil @compressor = NullCompressor.instance end def init_next_entry(entry, level = Zlib::DEFAULT_COMPRESSION) finalize_current_entry @entrySet << entry entry.write_local_entry(@outputStream) @compressor = get_compressor(entry, level) end def get_compressor(entry, level) case entry.compression_method when ZipEntry::DEFLATED then Deflater.new(@outputStream, level) when ZipEntry::STORED then PassThruCompressor.new(@outputStream) else raise ZipCompressionMethodError, "Invalid compression method: '#{entry.compression_method}'" end end def update_local_headers pos = @outputStream.tell @entrySet.each { |entry| @outputStream.pos = entry.localHeaderOffset entry.write_local_entry(@outputStream) } @outputStream.pos = pos end def write_central_directory cdir = ZipCentralDirectory.new(@entrySet, @comment) cdir.write_to_stream(@outputStream) end protected def finish @compressor.finish end public # Modeled after IO.<< def << (data) @compressor << data end end class Compressor #:nodoc:all def finish end end class PassThruCompressor < Compressor #:nodoc:all def initialize(outputStream) super() @outputStream = outputStream @crc = Zlib::crc32 @size = 0 end def << (data) val = data.to_s @crc = Zlib::crc32(val, @crc) @size += val.size @outputStream << val end attr_reader :size, :crc end class NullCompressor < Compressor #:nodoc:all include Singleton def << (data) raise IOError, "closed stream" end attr_reader :size, :compressed_size end class Deflater < Compressor #:nodoc:all def initialize(outputStream, level = Zlib::DEFAULT_COMPRESSION) super() @outputStream = outputStream @zlibDeflater = Zlib::Deflate.new(level, -Zlib::MAX_WBITS) @size = 0 @crc = Zlib::crc32 end def << (data) val = data.to_s @crc = Zlib::crc32(val, @crc) @size += val.size @outputStream << @zlibDeflater.deflate(data) end def finish until @zlibDeflater.finished? @outputStream << @zlibDeflater.finish end end attr_reader :size, :crc end class ZipEntrySet #:nodoc:all include Enumerable def initialize(anEnumerable = []) super() @entrySet = {} anEnumerable.each { |o| push(o) } end def include?(entry) @entrySet.include?(entry.to_s) end def <<(entry) @entrySet[entry.to_s] = entry end alias :push :<< def size @entrySet.size end alias :length :size def delete(entry) @entrySet.delete(entry.to_s) ? entry : nil end def each(&aProc) @entrySet.values.each(&aProc) end def entries @entrySet.values end # deep clone def dup newZipEntrySet = ZipEntrySet.new(@entrySet.values.map { |e| e.dup }) end def == (other) return false unless other.kind_of?(ZipEntrySet) return @entrySet == other.entrySet end def parent(entry) @entrySet[entry.parent_as_string] end #TODO attr_accessor :auto_create_directories protected attr_accessor :entrySet end class ZipCentralDirectory include Enumerable END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50 MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE = 65536 + 18 STATIC_EOCD_SIZE = 22 attr_reader :comment # Returns an Enumerable containing the entries. def entries @entrySet.entries end def initialize(entries = ZipEntrySet.new, comment = "") #:nodoc: super() @entrySet = entries.kind_of?(ZipEntrySet) ? entries : ZipEntrySet.new(entries) @comment = comment end def write_to_stream(io) #:nodoc: offset = io.tell @entrySet.each { |entry| entry.write_c_dir_entry(io) } write_e_o_c_d(io, offset) end def write_e_o_c_d(io, offset) #:nodoc: io << [END_OF_CENTRAL_DIRECTORY_SIGNATURE, 0 , # @numberOfThisDisk 0 , # @numberOfDiskWithStartOfCDir @entrySet? @entrySet.size : 0 , @entrySet? @entrySet.size : 0 , cdir_size , offset , @comment ? @comment.length : 0 ].pack('VvvvvVVv') io << @comment end private :write_e_o_c_d def cdir_size #:nodoc: # does not include eocd @entrySet.inject(0) { |value, entry| entry.cdir_header_size + value } end private :cdir_size def read_e_o_c_d(io) #:nodoc: buf = get_e_o_c_d(io) @numberOfThisDisk = ZipEntry::read_zip_short(buf) @numberOfDiskWithStartOfCDir = ZipEntry::read_zip_short(buf) @totalNumberOfEntriesInCDirOnThisDisk = ZipEntry::read_zip_short(buf) @size = ZipEntry::read_zip_short(buf) @sizeInBytes = ZipEntry::read_zip_long(buf) @cdirOffset = ZipEntry::read_zip_long(buf) commentLength = ZipEntry::read_zip_short(buf) @comment = buf.read(commentLength) raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0 end def read_central_directory_entries(io) #:nodoc: begin io.seek(@cdirOffset, IO::SEEK_SET) rescue Errno::EINVAL raise ZipError, "Zip consistency problem while reading central directory entry" end @entrySet = ZipEntrySet.new @size.times { @entrySet << ZipEntry.read_c_dir_entry(io) } end def read_from_stream(io) #:nodoc: read_e_o_c_d(io) read_central_directory_entries(io) end def get_e_o_c_d(io) #:nodoc: begin io.seek(-MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE, IO::SEEK_END) rescue Errno::EINVAL io.seek(0, IO::SEEK_SET) rescue Errno::EFBIG # FreeBSD 4.9 returns Errno::EFBIG instead of Errno::EINVAL io.seek(0, IO::SEEK_SET) end buf = io.read sigIndex = buf.rindex([END_OF_CENTRAL_DIRECTORY_SIGNATURE].pack('V')) raise ZipError, "Zip end of central directory signature not found" unless sigIndex buf=buf.slice!((sigIndex+4)...(buf.size)) def buf.read(count) slice!(0, count) end return buf end # For iterating over the entries. def each(&proc) @entrySet.each(&proc) end # Returns the number of entries in the central directory (and # consequently in the zip archive). def size @entrySet.size end def ZipCentralDirectory.read_from_stream(io) #:nodoc: cdir = new cdir.read_from_stream(io) return cdir rescue ZipError return nil end def == (other) #:nodoc: return false unless other.kind_of?(ZipCentralDirectory) @entrySet.entries.sort == other.entries.sort && comment == other.comment end end class ZipError < StandardError ; end class ZipEntryExistsError < ZipError; end class ZipDestinationFileExistsError < ZipError; end class ZipCompressionMethodError < ZipError; end class ZipEntryNameError < ZipError; end # ZipFile is modeled after java.util.zip.ZipFile from the Java SDK. # The most important methods are those inherited from # ZipCentralDirectory for accessing information about the entries in # the archive and methods such as get_input_stream and # get_output_stream for reading from and writing entries to the # archive. The class includes a few convenience methods such as # #extract for extracting entries to the filesystem, and #remove, # #replace, #rename and #mkdir for making simple modifications to # the archive. # # Modifications to a zip archive are not committed until #commit or # #close is called. The method #open accepts a block following # the pattern from File.open offering a simple way to # automatically close the archive when the block returns. # # The following example opens zip archive my.zip # (creating it if it doesn't exist) and adds an entry # first.txt and a directory entry a_dir # to it. # # require 'zip/zip' # # Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) { # |zipfile| # zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" } # zipfile.mkdir("a_dir") # } # # The next example reopens my.zip writes the contents of # first.txt to standard out and deletes the entry from # the archive. # # require 'zip/zip' # # Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) { # |zipfile| # puts zipfile.read("first.txt") # zipfile.remove("first.txt") # } # # ZipFileSystem offers an alternative API that emulates ruby's # interface for accessing the filesystem, ie. the File and Dir classes. class ZipFile < ZipCentralDirectory CREATE = 1 attr_reader :name # Opens a zip archive. Pass true as the second parameter to create # a new archive if it doesn't exist already. def initialize(fileName, create = nil) super() @name = fileName @comment = "" if (File.exists?(fileName)) File.open(name, "rb") { |f| read_from_stream(f) } elsif (create) @entrySet = ZipEntrySet.new else raise ZipError, "File #{fileName} not found" end @create = create @storedEntries = @entrySet.dup end # Same as #new. If a block is passed the ZipFile object is passed # to the block and is automatically closed afterwards just as with # ruby's builtin File.open method. def ZipFile.open(fileName, create = nil) zf = ZipFile.new(fileName, create) if block_given? begin yield zf ensure zf.close end else zf end end # Returns the zip files comment, if it has one attr_accessor :comment # Iterates over the contents of the ZipFile. This is more efficient # than using a ZipInputStream since this methods simply iterates # through the entries in the central directory structure in the archive # whereas ZipInputStream jumps through the entire archive accessing the # local entry headers (which contain the same information as the # central directory). def ZipFile.foreach(aZipFileName, &block) ZipFile.open(aZipFileName) { |zipFile| zipFile.each(&block) } end # Returns an input stream to the specified entry. If a block is passed # the stream object is passed to the block and the stream is automatically # closed afterwards just as with ruby's builtin File.open method. def get_input_stream(entry, &aProc) get_entry(entry).get_input_stream(&aProc) end # Returns an output stream to the specified entry. If a block is passed # the stream object is passed to the block and the stream is automatically # closed afterwards just as with ruby's builtin File.open method. def get_output_stream(entry, &aProc) newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s) if newEntry.directory? raise ArgumentError, "cannot open stream to directory entry - '#{newEntry}'" end zipStreamableEntry = ZipStreamableStream.new(newEntry) @entrySet << zipStreamableEntry zipStreamableEntry.get_output_stream(&aProc) end # Returns the name of the zip archive def to_s @name end # Returns a string containing the contents of the specified entry def read(entry) get_input_stream(entry) { |is| is.read } end # Convenience method for adding the contents of a file to the archive def add(entry, srcPath, &continueOnExistsProc) continueOnExistsProc ||= proc { false } check_entry_exists(entry, continueOnExistsProc, "add") newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s) if is_directory(newEntry, srcPath) @entrySet << ZipStreamableDirectory.new(newEntry) else @entrySet << ZipStreamableFile.new(newEntry, srcPath) end end # Removes the specified entry. def remove(entry) @entrySet.delete(get_entry(entry)) end # Renames the specified entry. def rename(entry, newName, &continueOnExistsProc) foundEntry = get_entry(entry) check_entry_exists(newName, continueOnExistsProc, "rename") foundEntry.name=newName end # Replaces the specified entry with the contents of srcPath (from # the file system). def replace(entry, srcPath) check_file(srcPath) add(remove(entry), srcPath) end # Extracts entry to file destPath. def extract(entry, destPath, &onExistsProc) onExistsProc ||= proc { false } foundEntry = get_entry(entry) if foundEntry.is_directory create_directory(foundEntry, destPath, &onExistsProc) else write_file(foundEntry, destPath, &onExistsProc) end end # Commits changes that has been made since the previous commit to # the zip archive. def commit return if ! commit_required? on_success_replace(name) { |tmpFile| ZipOutputStream.open(tmpFile) { |zos| @entrySet.each { |e| e.write_to_zip_output_stream(zos) } zos.comment = comment } true } initialize(name) end # Closes the zip file committing any changes that has been made. def close commit end # Returns true if any changes has been made to this archive since # the previous commit def commit_required? return @entrySet != @storedEntries || @create == ZipFile::CREATE end # Searches for entry with the specified name. Returns nil if # no entry is found. See also get_entry def find_entry(entry) @entrySet.detect { |e| e.name.sub(/\/$/, "") == entry.to_s.sub(/\/$/, "") } end # Searches for an entry just as find_entry, but throws Errno::ENOENT # if no entry is found. def get_entry(entry) selectedEntry = find_entry(entry) unless selectedEntry raise Errno::ENOENT, entry end return selectedEntry end # Creates a directory def mkdir(entryName, permissionInt = 0) #permissionInt ignored if find_entry(entryName) raise Errno::EEXIST, "File exists - #{entryName}" end @entrySet << ZipStreamableDirectory.new(ZipEntry.new(name, entryName.to_s.ensure_end("/"))) end private def create_directory(entry, destPath) if File.directory? destPath return elsif File.exists? destPath if block_given? && yield(entry, destPath) File.rm_f destPath else raise ZipDestinationFileExistsError, "Cannot create directory '#{destPath}'. "+ "A file already exists with that name" end end Dir.mkdir destPath end def is_directory(newEntry, srcPath) srcPathIsDirectory = File.directory?(srcPath) if newEntry.is_directory && ! srcPathIsDirectory raise ArgumentError, "entry name '#{newEntry}' indicates directory entry, but "+ "'#{srcPath}' is not a directory" elsif ! newEntry.is_directory && srcPathIsDirectory newEntry.name += "/" end return newEntry.is_directory && srcPathIsDirectory end def check_entry_exists(entryName, continueOnExistsProc, procedureName) continueOnExistsProc ||= proc { false } if @entrySet.detect { |e| e.name == entryName } if continueOnExistsProc.call remove get_entry(entryName) else raise ZipEntryExistsError, procedureName+" failed. Entry #{entryName} already exists" end end end def write_file(entry, destPath, continueOnExistsProc = proc { false }) if File.exists?(destPath) && ! yield(entry, destPath) raise ZipDestinationFileExistsError, "Destination '#{destPath}' already exists" end File.open(destPath, "wb") { |os| entry.get_input_stream { |is| os << is.read } } end def check_file(path) unless File.readable? path raise Errno::ENOENT, path end end def on_success_replace(aFilename) tmpfile = get_tempfile tmpFilename = tmpfile.path tmpfile.close if yield tmpFilename File.move(tmpFilename, name) end end def get_tempfile tempFile = Rant::Tempfile.new(File.basename(name), File.dirname(name)) tempFile.binmode tempFile end end class ZipStreamableFile < DelegateClass(ZipEntry) #:nodoc:all def initialize(entry, filepath) super(entry) @delegate = entry @filepath = filepath end def get_input_stream(&aProc) File.open(@filepath, "rb", &aProc) end def write_to_zip_output_stream(aZipOutputStream) aZipOutputStream.put_next_entry(self) aZipOutputStream << get_input_stream { |is| is.read } end def == (other) return false unless other.class == ZipStreamableFile @filepath == other.filepath && super(other.delegate) end protected attr_reader :filepath, :delegate end class ZipStreamableDirectory < DelegateClass(ZipEntry) #:nodoc:all def initialize(entry) super(entry) end def get_input_stream(&aProc) return yield(NullInputStream.instance) if block_given? NullInputStream.instance end def write_to_zip_output_stream(aZipOutputStream) aZipOutputStream.put_next_entry(self) end end class ZipStreamableStream < DelegateClass(ZipEntry) #nodoc:all def initialize(entry) super(entry) @tempFile = Rant::Tempfile.new(File.basename(name), File.dirname(zipfile)) @tempFile.binmode end def get_output_stream if block_given? begin yield(@tempFile) ensure @tempFile.close end else @tempFile end end def get_input_stream if ! @tempFile.closed? raise StandardError, "cannot open entry for reading while its open for writing - #{name}" end @tempFile.open # reopens tempfile from top if block_given? begin yield(@tempFile) ensure @tempFile.close end else @tempFile end end def write_to_zip_output_stream(aZipOutputStream) aZipOutputStream.put_next_entry(self) aZipOutputStream << get_input_stream { |is| is.read } end end class ZipExtraField < Hash ID_MAP = {} # Meta class for extra fields class Generic def self.register_map if self.const_defined?(:HEADER_ID) ID_MAP[self.const_get(:HEADER_ID)] = self end end def self.name self.to_s.split("::")[-1] end # return field [size, content] or false def initial_parse(binstr) if ! binstr # If nil, start with empty. return false elsif binstr[0,2] != self.class.const_get(:HEADER_ID) $stderr.puts "Warning: weired extra feild header ID. skip parsing" return false end [binstr[2,2].unpack("v")[0], binstr[4..-1]] end def ==(other) self.class != other.class and return false each { |k, v| v != other[k] and return false } true end def to_local_bin s = pack_for_local self.class.const_get(:HEADER_ID) + [s.length].pack("v") + s end def to_c_dir_bin s = pack_for_c_dir self.class.const_get(:HEADER_ID) + [s.length].pack("v") + s end end # Info-ZIP Additional timestamp field class UniversalTime < Generic HEADER_ID = "UT" register_map def initialize(binstr = nil) @ctime = nil @mtime = nil @atime = nil @flag = nil binstr and merge(binstr) end attr_accessor :atime, :ctime, :mtime, :flag def merge(binstr) binstr == "" and return size, content = initial_parse(binstr) size or return @flag, mtime, atime, ctime = content.unpack("CVVV") mtime and @mtime ||= Time.at(mtime) atime and @atime ||= Time.at(atime) ctime and @ctime ||= Time.at(ctime) end def ==(other) @mtime == other.mtime && @atime == other.atime && @ctime == other.ctime end def pack_for_local s = [@flag].pack("C") @flag & 1 != 0 and s << [@mtime.to_i].pack("V") @flag & 2 != 0 and s << [@atime.to_i].pack("V") @flag & 4 != 0 and s << [@ctime.to_i].pack("V") s end def pack_for_c_dir s = [@flag].pack("C") @flag & 1 == 1 and s << [@mtime.to_i].pack("V") s end end # Info-ZIP Extra for UNIX uid/gid class IUnix < Generic HEADER_ID = "Ux" register_map def initialize(binstr = nil) @uid = 0 @gid = 0 binstr and merge(binstr) end attr_accessor :uid, :gid def merge(binstr) binstr == "" and return size, content = initial_parse(binstr) # size: 0 for central direcotry. 4 for local header return if(! size || size == 0) uid, gid = content.unpack("vv") @uid ||= uid @gid ||= gid end def ==(other) @uid == other.uid && @gid == other.gid end def pack_for_local [@uid, @gid].pack("vv") end def pack_for_c_dir "" end end ## start main of ZipExtraField < Hash def initialize(binstr = nil) binstr and merge(binstr) end def merge(binstr) binstr == "" and return i = 0 while i < binstr.length id = binstr[i,2] len = binstr[i+2,2].to_s.unpack("v")[0] if id && ID_MAP.member?(id) field_name = ID_MAP[id].name if self.member?(field_name) self[field_name].mergea(binstr[i, len+4]) else field_obj = ID_MAP[id].new(binstr[i, len+4]) self[field_name] = field_obj end elsif id unless self["Unknown"] s = "" class << s alias_method :to_c_dir_bin, :to_s alias_method :to_local_bin, :to_s end self["Unknown"] = s end if ! len || len+4 > binstr[i..-1].length self["Unknown"] << binstr[i..-1] break; end self["Unknown"] << binstr[i, len+4] end i += len+4 end end def create(name) field_class = nil ID_MAP.each { |id, klass| if klass.name == name field_class = klass break end } if ! field_class raise ZipError, "Unknown extra field '#{name}'" end self[name] = field_class.new() end def to_local_bin s = "" each { |k, v| s << v.to_local_bin } s end alias :to_s :to_local_bin def to_c_dir_bin s = "" each { |k, v| s << v.to_c_dir_bin } s end def c_dir_length to_c_dir_bin.length end def local_length to_local_bin.length end alias :c_dir_size :c_dir_length alias :local_size :local_length alias :length :local_length alias :size :local_length end # end ZipExtraField end # Zip namespace module # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rant-0.5.8/lib/rant/c/0000755000175000017500000000000010527254520014004 5ustar xavierxavierrant-0.5.8/lib/rant/c/include.rb0000644000175000017500000000324210527253225015756 0ustar xavierxavier # include.rb - Support for C - parsing #include statements. # # Copyright (C) 2005 Stefan Lang module Rant end module Rant::C module Include # Searches for all `#include' statements in the C/C++ source # from the string +src+. # # Returns two arguments: # 1. A list of all standard library includes (e.g. #include ). # 2. A list of all local includes (e.g. #include "stdio.h"). def parse_includes(src) if src.respond_to? :to_str src = src.to_str else raise ArgumentError, "src has to be a string" end s_includes = [] l_includes = [] in_block_comment = false prev_line = nil src.each { |line| line.chomp! if block_start_i = line.index("/*") c_start_i = line.index("//") if !c_start_i || block_start_i < c_start_i if block_end_i = line.index("*/") if block_end_i > block_start_i line[block_start_i..block_end_i+1] = "" end end end end if prev_line line = prev_line << line prev_line = nil end if line =~ /\\$/ prev_line = line.chomp[0...line.length-1] end if in_block_comment in_block_comment = false if line =~ %r|\*/| next end case line when /\s*#\s*include\s+"([^"]+)"/ l_includes << $1 when /\s*#\s*include\s+<([^>]+)>/ s_includes << $1 when %r|(?!//)[^/]*/\*| in_block_comment = true end } [s_includes, l_includes] end module_function :parse_includes end # module Include end # module Rant::C rant-0.5.8/lib/rant/csharp/0000755000175000017500000000000010527254520015042 5ustar xavierxavierrant-0.5.8/lib/rant/csharp/base_compiler_adapter.rb0000644000175000017500000000601110527253224021671 0ustar xavierxavier# Helper method to add a to_cs_arg method to a class (yay meta!) def add_to_cs_arg_method klass, &block klass.instance_eval { define_method :to_cs_arg, &block } end def add_to_cmd_array klass, &block klass.instance_eval { define_method :to_cmd_array, &block} end # Methods to convert types into arguments for the compiler add_to_cs_arg_method(Object) do |key, compiler| compiler.string_argument(key, self.to_s) end add_to_cs_arg_method(TrueClass) do |key, compiler| compiler.boolean_argument(key, true) end add_to_cs_arg_method(FalseClass) do |key, compiler| compiler.boolean_argument(key, false) end add_to_cs_arg_method(Array) do |key, compiler| self.collect {|x| x.to_cs_arg(key, compiler) } end add_to_cmd_array(Object) do |context| [context.sys.sp(self.to_s)] end add_to_cmd_array(Array) do |context| self.collect {|x| context.sys.sp(x) } end add_to_cmd_array(Rant::FileList) do |context| [self.arglist] end module Rant::CSharp class BaseCompilerAdapter attr_accessor :bin attr_accessor :switch_map def initialize bin = "" @bin = bin @switch_map = {} raise Exception.new("Must specify an executable") if !@bin || @bin.length == 0 end def cmd target, cs_args, context @context = context if !target || target.length == 0 raise Exception.new("Target must be specified and have a length " + "greater than 0") end # Generic argument processing args = [] sources = cs_args.delete(:sources) cs_args[:target] ||= guess_target(target) cs_args.each_key do |key| args.push(cs_args[key].to_cs_arg(key, self)) end src_list = sources.to_cmd_array(context) ([bin, outfile(target)] + args.flatten + src_list).join(' ') end # Map rant arguments to compiler arguments def map_arg arg switch_map[arg] ? switch_map[arg] : arg end def outfile target string_argument "out", target end def boolean_argument arg, on switch = map_arg(arg) ret = "#{self.argument_prefix}#{switch}" ret += on ? "" : "-" end def string_argument arg, value switch = map_arg(arg) # Assume all string arguments except target are # files value = @context.sys.sp(value) if arg != :target "#{self.argument_prefix}#{switch}:#{value}" end def argument_prefix "" end # Try to automatically guess the type of output file # based on the extension def guess_target outfile target = "library" ext = outfile.match(/\.([^\.]+)$/) ext = ext[1] if ext if ext case ext.downcase when "netmodule" target = map_target("module") when "exe" target = map_target("winexe") end end target end # Allows subclasses to override default target names def map_target target target end end end rant-0.5.8/lib/rant/csharp/compiler_adapter_factory.rb0000644000175000017500000000211410527253224022426 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp/csc_compiler') require File.expand_path(File.dirname(__FILE__) + '/../csharp/mcs_compiler') require File.expand_path(File.dirname(__FILE__) + '/../csharp/gmcs_compiler') module Rant::CSharp class CompilerAdapterFactory attr_accessor :context attr_accessor :compiler_map def initialize context @context = context @compiler = nil # Default compiler mappings @compiler_map ||= { "csc" => CscCompiler, "mcs" => McsCompiler, "gmcs" => GmcsCompiler } end def compiler if !@compiler compiler_map.each_key do |key| if context.env.find_bin(key) @compiler = compiler_map[key].new(context) break end end if !@compiler raise Exception.new("Could not find C# compiler in path (csc, mcs, gmcs). " + "Please amend your path or explicitly define one with the :compiler option") end end @compiler end end end rant-0.5.8/lib/rant/csharp/csc_compiler.rb0000644000175000017500000000055210527253224020033 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp/base_compiler_adapter') module Rant::CSharp class CscCompiler < BaseCompilerAdapter def initialize bin = 'csc /nologo' super @switch_map = { :resources => "res", :libs => "r"} end def argument_prefix "/" end end endrant-0.5.8/lib/rant/csharp/gmcs_compiler.rb0000644000175000017500000000030310527253224020206 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp/mcs_compiler') module Rant::CSharp class GmcsCompiler < McsCompiler def initialize bin = 'gmcs' super end end end rant-0.5.8/lib/rant/csharp/mcs_compiler.rb0000644000175000017500000000040110527253224020036 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp/base_compiler_adapter') module Rant::CSharp class McsCompiler < BaseCompilerAdapter def initialize bin = 'mcs' super end def argument_prefix "-" end end end rant-0.5.8/lib/rant/import/0000755000175000017500000000000010527254520015074 5ustar xavierxavierrant-0.5.8/lib/rant/import/archive/0000755000175000017500000000000010527254520016515 5ustar xavierxavierrant-0.5.8/lib/rant/import/archive/tgz.rb0000644000175000017500000000256510527253230017653 0ustar xavierxavier # tgz.rb - Archive::Tgz generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/import/archive' #require 'rant/archive/minitar' #rant-import:uncomment module Rant::Generators::Archive # Use this class as a generator to create gzip compressed tar # archives. class Tgz < Base def initialize(*args) super @extension = ".tgz" end # Ensure to set #rac first. # Creates a file task wich invokes tar to create a tgz # archive. Returns the created task. def define_task if ::Rant::Env.have_tar? define_tar_task else define_minitar_task end end private def define_tar_task define_cmd_task { |path, t| @rac.cx.sys "tar -h --no-recursion --files-from #{path} -czf #{t.name}" } end def define_minitar_task define_cmd_task do |path, t| minitar_tgz t.name, @res_files end end def minitar_tgz fn, files, opts = {:recurse => false} require 'zlib' require 'rant/archive/minitar' #rant-import:remove @rac.cmd_msg "minitar #{fn}" files = files.to_ary if files.respond_to? :to_ary tgz = Zlib::GzipWriter.new(File.open(fn, 'wb')) # pack closes tgz Rant::Archive::Minitar.pack(files, tgz, opts[:recurse]) nil end end # class Tgz end # module Rant::Generators::Archive rant-0.5.8/lib/rant/import/archive/zip.rb0000644000175000017500000000367110527253230017650 0ustar xavierxavier # zip.rb - Archive::Zip generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/import/archive' #require 'rant/archive/rubyzip' #rant-import:uncomment module Rant::Generators::Archive # Use this class as a generator to create zip archives. class Zip < Base def initialize(*args) super @extension = ".zip" end # Ensure to set #rac first. # Creates a file task wich invokes zip to create a zip # archive. Returns the created task. def define_task if ::Rant::Env.have_zip? define_zip_task else define_rubyzip_task end end def define_zip_task define_cmd_task { |path, t| # Add -y option to store symlinks instead of # referenced files. cmd = "zip -@q #{t.name}" @rac.cmd_msg cmd IO.popen cmd, "w" do |z| z.print IO.read(path) end raise Rant::CommandError.new(cmd, $?) unless $?.success? } end def define_rubyzip_task define_cmd_task do |path, t| rubyzip t.name, @res_files end end def rubyzip fn, files, opts = {:recurse => false} require 'rant/archive/rubyzip' # rubyzip creates only a new file if fn doesn't exist @rac.sys.rm_f fn if test ?e, fn @rac.cmd_msg "rubyzip #{fn}" Rant::Archive::Rubyzip::ZipFile.open fn, Rant::Archive::Rubyzip::ZipFile::CREATE do |z| if opts[:recurse] require 'find' files.each { |f| if test ?d, f Find.find(f) { |f2| z.add f2, f2 } else z.add f, f end } else files.each { |f| z.add f, f } end end nil end end # class Zip end # module Rant::Generators::Archive rant-0.5.8/lib/rant/import/c/0000755000175000017500000000000010527254520015316 5ustar xavierxavierrant-0.5.8/lib/rant/import/c/dependencies.rb0000644000175000017500000000723310527253230020273 0ustar xavierxavier # dependencies.rb - C::Dependencies generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/rantlib' require 'rant/c/include' module Rant::Generators::C end class Rant::Generators::C::Dependencies def self.rant_gen(rac, ch, args, &block) c_files, out_fn, include_pathes, opts = nil # args validation if block rac.warn_msg "C::Dependencies: ignoring block" end case args.size when 0 # noop when 1 farg = args.first Hash === farg ? (opts = farg) : (out_fn = farg) when 2 out_fn = args.first opts = args[1] else rac.abort_at(ch, "C::Dependencies takes one or two arguments.") end correct_case = false if opts if opts.respond_to? :to_hash opts = opts.to_hash else rac.abort_at(ch, "C::Dependencies: second argument has to be a hash.") end opts.each { |k, v| case k when :sources c_files = v when :search, :search_pathes, :include_pathes include_pathes = if v.respond_to? :to_str [v.to_str] else v end when :correct_case correct_case = !!v else rac.abort_at(ch, "C::Dependencies: no such option -- #{k}") end } end out_fn ||= "c_dependencies" c_files ||= rac.cx.sys["**/*.{c,cpp,cc,h,hpp}"] include_pathes ||= ["."] if out_fn.respond_to? :to_str out_fn = out_fn.to_str else rac.abort_at(ch, "filename has to be a string") end unless ::Rant::FileList === c_files if c_files.respond_to? :to_ary c_files = c_files.to_ary else rac.abort_at(ch, "sources has to be a list of files") end end unless ::Rant::FileList === include_pathes if include_pathes.respond_to? :to_ary include_pathes = include_pathes.to_ary else rac.abort_at(ch, "search has to be a list of directories") end end # define file task rac.cx.file({:__caller__ => ch, out_fn => c_files}) do |t| tmp_rac = ::Rant::RantApp.new depfile_ts = Time.at(0) if File.exist? t.name tmp_rac.source(t.name) depfile_ts = File.mtime(t.name) end rf_str = "" c_files.each { |cf| f_task = nil unless test(?f, cf) rac.warn_msg "#{t.name}: no such file -- #{cf}" next end f_task = tmp_rac.tasks[cf.to_str] deps = f_task ? f_task.prerequisites : nil if !deps or File.mtime(cf) > depfile_ts rac.cmd_msg "scanning #{cf}" std_includes, local_includes = ::Rant::C::Include.parse_includes(File.read(cf)) deps = [] (std_includes + local_includes).each { |fn| path = existing_file( include_pathes, fn, correct_case) deps << path if path } end rf_str << file_deps(cf, deps) << "\n" } rac.vmsg 1, "writing C source dependencies to #{t.name}" open(t.name, "w") { |f| f.puts f.puts "# #{t.name}" f.puts "# C source dependencies generated by Rant #{Rant::VERSION}" f.puts "# WARNING: Modifications to this file will get lost!" f.puts f.write rf_str } end end def self.existing_file(dirs, fn, correct_case) dirs.each { |dir| path = dir == "." ? fn : File.join(dir, fn) if test ?f, path return path unless correct_case found_file = File.basename(fn) found_in_dir = File.dirname(File.join(dir, fn)) Dir.entries(found_in_dir).each { |dentry| return File.join(found_in_dir, dentry) if dentry.downcase == found_file.downcase } end } nil end def self.file_deps(target, deps) s = "gen SourceNode, #{target.to_str.dump} => " s << "[#{ deps.map{ |fn| fn.to_str.dump }.join(', ')}]" end end # class Rant::Generators::C::Dependencies rant-0.5.8/lib/rant/import/filelist/0000755000175000017500000000000010527254520016707 5ustar xavierxavierrant-0.5.8/lib/rant/import/filelist/core.rb0000644000175000017500000003244010527253231020165 0ustar xavierxavier # core.rb - Core functionality for the Rant::FileList class. # # Copyright (C) 2005 Stefan Lang module Rant def FileList(arg) if arg.respond_to?(:to_rant_filelist) arg.to_rant_filelist elsif arg.respond_to?(:to_ary) FileList.new(arg.to_ary) # or? #FileList.new.concat(arg.to_ary) else raise TypeError, "cannot convert #{arg.class} into Rant::FileList" end end module_function :FileList class FileList include Enumerable ESC_SEPARATOR = Regexp.escape(File::SEPARATOR) ESC_ALT_SEPARATOR = File::ALT_SEPARATOR ? Regexp.escape(File::ALT_SEPARATOR) : nil class << self def [](*patterns) new.hide_dotfiles.include(*patterns) end def glob(*patterns) fl = new.hide_dotfiles.ignore(".", "..").include(*patterns) if block_given? then yield fl else fl end end def glob_all(*patterns) fl = new.ignore(".", "..").include(*patterns) if block_given? then yield fl else fl end end end def initialize(store = []) @pending = false @def_glob_dotfiles = true @items = store @ignore_rx = nil @keep = {} @actions = [] end alias _object_dup dup private :_object_dup def dup c = _object_dup c.items = @items.dup c.actions = @actions.dup c.ignore_rx = @ignore_rx.dup if @ignore_rx c.instance_variable_set(:@keep, @keep.dup) c end def copy c = _object_dup c.items = @items.map { |entry| entry.dup } c.actions = @actions.dup c.ignore_rx = @ignore_rx.dup if @ignore_rx # alternative approach: copy & freeze "keep" entries on # inclusion in the keep hash h_keep = {} @keep.each_key { |entry| h_keep[entry] = true } c.instance_variable_set(:@keep, h_keep) c end # Currently for Rant internal use only. Might go in future # releases. def glob_dotfiles? @def_glob_dotfiles end # Currently for Rant internal use only. Might go in future # releases. def glob_dotfiles=(flag) @def_glob_dotfiles = flag ? true : false end # Has the same effect as glob_dotfiles = false. # # Returns self. # # Currently for Rant internal use only. Might go in future # releases. def hide_dotfiles @def_glob_dotfiles = false self end # Has the same effect as glob_dotfiles = true. # # Returns self. # # Currently for Rant internal use only. Might go in future # releases. def glob_dotfiles @def_glob_dotfiles = true self end protected attr_accessor :actions, :items attr_accessor :pending attr_accessor :ignore_rx public def each(&block) resolve if @pending @items.each(&block) self end def to_ary resolve if @pending @items end alias to_a to_ary alias entries to_ary # entries: defined in Enumerable def to_rant_filelist self end def +(other) if other.respond_to? :to_rant_filelist c = other.to_rant_filelist.dup c.actions.concat(@actions) c.items.concat(@items) c.pending = !c.actions.empty? c elsif other.respond_to? :to_ary c = dup c.actions << [:apply_ary_method_1, :concat, other.to_ary.dup] c.pending = true c else raise TypeError, "cannot add #{other.class} to Rant::FileList" end end # Use this method to append +file+ to this list. +file+ will # stay in this list even if it matches an exclude or ignore # pattern. # # Returns self. def <<(file) @actions << [:apply_ary_method_1, :push, file] @keep[file] = true @pending = true self end # Add +entry+ to this filelist. Position of +entry+ in this # list is undefined. More efficient than #<<. +entry+ will # stay in this list even if it matches an exclude or ignore # pattern. # # Returns self. def keep(entry) @keep[entry] = true @items << entry self end # Append the entries of +ary+ (an array like object) to # this list. def concat(ary) if @pending ary = ary.to_ary.dup @actions << [:apply_ary_method_1, :concat, ary] else ix = ignore_rx and ary = ary.to_ary.reject { |f| f =~ ix } @items.concat(ary) end self end # Number of entries in this filelist. def size resolve if @pending @items.size end alias length size def empty? resolve if @pending @items.empty? end def join(sep = ' ') resolve if @pending @items.join(sep) end def pop resolve if @pending @items.pop end def push(entry) resolve if @pending @items.push(entry) if entry !~ ignore_rx self end def shift resolve if @pending @items.shift end def unshift(entry) resolve if @pending @items.unshift(entry) if entry !~ ignore_rx self end if Object.method_defined?(:fcall) || Object.method_defined?(:funcall) # in Ruby 1.9 like __send__ @@__send_private__ = Object.method_defined?(:fcall) ? :fcall : :funcall def resolve @pending = false @actions.each{ |action| self.__send__(@@__send_private__, *action) }.clear ix = ignore_rx if ix @items.reject! { |f| f =~ ix && !@keep[f] } end self end else # Force evaluation of all patterns. def resolve @pending = false @actions.each{ |action| self.__send__(*action) }.clear ix = ignore_rx if ix @items.reject! { |f| f =~ ix && !@keep[f] } end self end end # Include entries matching one of +patterns+ in this filelist. def include(*pats) @def_glob_dotfiles ? glob_all(*pats) : glob_unix(*pats) end alias glob include # Unix style glob: hide files starting with a dot def glob_unix(*patterns) patterns.flatten.each { |pat| @actions << [:apply_glob_unix, pat] } @pending = true self end def glob_all(*patterns) patterns.flatten.each { |pat| @actions << [:apply_glob_all, pat] } @pending = true self end if RUBY_VERSION < "1.8.2" # Dir.glob of Ruby releases before 1.8.2 returned dotfiles # even if File::FNM_DOTMATCH was not set. FN_DOTFILE_RX_ = ESC_ALT_SEPARATOR ? /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)\..* ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x : /(^|#{ESC_SEPARATOR}+)\..* (#{ESC_SEPARATOR}+|$)/x def apply_glob_unix(pattern) inc_files = Dir.glob(pattern) # it's not 100% correct, but it works for most use # cases unless pattern =~ /(^|\/)\./ inc_files.reject! { |fn| fn =~ FN_DOTFILE_RX_ } end @items.concat(inc_files) end else def apply_glob_unix(pattern) @items.concat(Dir.glob(pattern)) end end private :apply_glob_unix def apply_glob_all(pattern) @items.concat(Dir.glob(pattern, File::FNM_DOTMATCH)) end private :apply_glob_all # Exclude all entries matching one of +patterns+ from this # filelist. # # Note: Only applies to entries previousely included. def exclude(*patterns) patterns.each { |pat| if Regexp === pat @actions << [:apply_exclude_rx, pat] else @actions << [:apply_exclude, pat] end } @pending = true self end def ignore(*patterns) patterns.each { |pat| add_ignore_rx(Regexp === pat ? pat : mk_all_rx(pat)) } @pending = true self end def add_ignore_rx(rx) @ignore_rx = if @ignore_rx Regexp.union(@ignore_rx, rx) else rx end end private :add_ignore_rx def apply_exclude(pattern) @items.reject! { |elem| File.fnmatch?(pattern, elem, File::FNM_DOTMATCH) && !@keep[elem] } end private :apply_exclude def apply_exclude_rx(rx) @items.reject! { |elem| elem =~ rx && !@keep[elem] } end private :apply_exclude_rx def exclude_name(*names) names.each { |name| @actions << [:apply_exclude_rx, mk_all_rx(name)] } @pending = true self end alias shun exclude_name if File::ALT_SEPARATOR # TODO: check for FS case sensitivity? def mk_all_rx(file) /(^|(#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+)#{Regexp.escape(file)} ((#{ESC_SEPARATOR}|#{ESC_ALT_SEPARATOR})+|$)/x end else def mk_all_rx(file) /(^|#{ESC_SEPARATOR}+)#{Regexp.escape(file)} (#{ESC_SEPARATOR}+|$)/x end end private :mk_all_rx def exclude_path(*patterns) patterns.each { |pat| @actions << [:apply_exclude_path, pat] } @pending = true self end def apply_exclude_path(pattern) flags = File::FNM_DOTMATCH|File::FNM_PATHNAME @items.reject! { |elem| File.fnmatch?(pattern, elem, flags) && !@keep[elem] } end private :apply_exclude def select(&block) d = dup d.actions << [:apply_select, block] d.pending = true d end alias find_all select def apply_select blk @items = @items.select(&blk) end private :apply_select def map(&block) d = dup d.actions << [:apply_ary_method, :map!, block] d.pending = true d end alias collect map def sub_ext(ext, new_ext=nil) map { |f| f._rant_sub_ext ext, new_ext } end def ext(ext_str) sub_ext(ext_str) end # Get a string with all entries. This is very usefull # if you invoke a shell: # files # => ["foo/bar", "with space"] # sh "rdoc #{files.arglist}" # will result on windows: # rdoc foo\bar "with space" # on other systems: # rdoc foo/bar with\ space def arglist Rant::Sys.sp to_ary end alias to_s arglist alias object_inspect inspect # Same as #uniq! but evaluation is delayed until the next read # access (e.g. by calling #each). Always returns self. def uniq! @actions << [:apply_ary_method, :uniq!] @pending = true self end # Same as #sort! but evaluation is delayed until the next read # access (e.g. by calling #each). Always returns self. def sort! @actions << [:apply_ary_method, :sort!] @pending = true self end # Same as #map! but evaluation is delayed until the next read # access (e.g. by calling #each). Always returns self. def map!(&block) @actions << [:apply_ary_method, :map!, block] @pending = true self end def reject!(&block) @actions << [:apply_ary_method, :reject!, block] @pending = true self end private def apply_ary_method(meth, block=nil) @items.send meth, &block end def apply_ary_method_1(meth, arg1, block=nil) @items.send meth, arg1, &block end =begin def apply_lazy_operation(meth, args, block) @items.send(meth, *args, &block) end =end end # class FileList end # module Rant rant-0.5.8/lib/rant/import/filelist/inspect.rb0000644000175000017500000000341210527253231020677 0ustar xavierxavier # inspect.rb - Custom inspect method for Rant::FileList instances. # # Copyright (C) 2005 Stefan Lang module Rant class FileList # Note that the default Object#inspect implementation is # available as +object_inspect+. def inspect # empirisch ermittelt ;) s = "#<#{self.class}:0x%x " % (object_id << 1) s << "glob:" << (glob_dotfiles? ? "all" : "unix") << " " if ix = ignore_rx is = ix.source.dup is.gsub!(/ +/, ' ') is.gsub!(/\n/, '\n') is.gsub!(/\t/, '\t') is[10..-1] = "..." if is.length > 12 s << "i:#{is} " end if @pending s << "res:#{@actions.size} " end unless @keep.empty? s << "keep:#{@keep.size} " end s << "entries:#{items.size}" if @items.size > 0 s << "[" if @items.size == 1 is = @items.first.dup is[15..-1] = "..." if is.length > 16 is = '"' << is << '"' else fs = @items.first.dup fs[11..-1] = "..." if fs.length > 12 fs = '"' << fs << '"' ls = @items.last.dup ls[0..-11] = "..." if ls.length > 12 ls = '"' << ls << '"' if @items.size == 2 is = "#{fs}, #{ls}" else is = "#{fs}, ..., #{ls}" end end s << "#{is}]" end s << ">" end end # class FileList end # module Rant rant-0.5.8/lib/rant/import/filelist/more.rb0000644000175000017500000000234010527253231020173 0ustar xavierxavier # more.rb - More Rant::FileList methods. # # Copyright (C) 2005 Stefan Lang module Rant class FileList # Remove all files which have the given name. def no_file(name) @actions << [:apply_ary_method, :reject!, lambda { |entry| entry == name && !@keep[entry] && test(?f, entry) }] @pending = true self end # Remove all entries which contain an element # with the given suffix. def no_suffix(suffix) @actions << [:no_suffix, suffix] @pending = true self end def apply_no_suffix(suffix) elems = nil elem = nil @files.reject! { |entry| elems = Sys.split_all(entry) elems.any? { |elem| elem =~ /#{suffix}$/ && !@keep[entry] } } end private :apply_no_suffix # Remove all entries which contain an element # with the given prefix. def no_prefix(prefix) @actions << [:no_prefix, prefix] @pending = true self end def apply_no_prefix(prefix) elems = elem = nil @files.reject! { |entry| elems = Sys.split_all(entry) elems.any? { |elem| elem =~ /^#{prefix}/ && !@keep[entry] } } end private :apply_no_prefix end # class FileList end # module Rant rant-0.5.8/lib/rant/import/filelist/std.rb0000644000175000017500000000362410527253231020031 0ustar xavierxavier # std.rb - Additional set of Rant::FileList instance methods. # # Copyright (C) 2005 Stefan Lang # What should this file be used for: # # Place import "filelist/std" in an Rantfile to get all # functionality as defined after require "rant/filelist" # (for use as library). Read doc/filelist.rdoc. require 'rant/import/filelist/inspect' module Rant class FileList # Remove all entries which contain a directory with the # given name. # If no argument or +nil+ given, remove all directories. # # Example: # file_list.no_dir "CVS" # would remove the following entries from file_list: # CVS/ # src/CVS/lib.c # CVS/foo/bar/ def no_dir(name = nil) @actions << [:apply_no_dir, name] @pending = true self end def apply_no_dir(name) entry = nil unless name @items.reject! { |entry| test(?d, entry) && !@keep[entry] } return end elems = nil @items.reject! { |entry| next if @keep[entry] elems = Sys.split_all(entry) i = elems.index(name) if i path = File.join(*elems[0..i]) test(?d, path) else false end } end private :apply_no_dir # Get a new filelist containing only the existing files from # this filelist. def files select { |entry| test ?f, entry } end # Get a new filelist containing only the existing directories # from this filelist. def dirs select { |entry| test ?d, entry } end end # class FileList end # module Rant rant-0.5.8/lib/rant/import/nodes/0000755000175000017500000000000010527254520016204 5ustar xavierxavierrant-0.5.8/lib/rant/import/nodes/default.rb0000644000175000017500000003105010527253230020151 0ustar xavierxavier # default.rb - Default node types for Rant. # # Copyright (C) 2005 Stefan Lang module Rant def self.init_import_nodes__default(rac, *rest) rac.node_factory = DefaultNodeFactory.new end class DefaultNodeFactory def new_task(rac, name, pre, blk) Task.new(rac, name, pre, &blk) end def new_file(rac, name, pre, blk) FileTask.new(rac, name, pre, &blk) end def new_dir(rac, name, pre, blk) DirTask.new(rac, name, pre, &blk) end def new_source(rac, name, pre, blk) SourceNode.new(rac, name, pre, &blk) end def new_custom(rac, name, pre, blk) UserTask.new(rac, name, pre, &blk) end def new_auto_subfile(rac, name, pre, blk) AutoSubFileTask.new(rac, name, pre, &blk) end end class Task include Node attr_accessor :receiver def initialize(rac, name, prerequisites = [], &block) super() @rac = rac or raise ArgumentError, "rac not given" @name = name or raise ArgumentError, "name not given" @pre = prerequisites || [] @pre_resolved = false @block = block @run = false @receiver = nil end # Get a list of the *names* of all prerequisites. The # underlying list of prerequisites can't be modified by the # value returned by this method. def prerequisites @pre.collect { |pre| pre.to_s } end alias deps prerequisites # First prerequisite. def source @pre.first.to_s end # True if this task has at least one action (block to be # executed) associated. def has_actions? @block or @receiver && @receiver.has_pre_action? end # Add a prerequisite. def <<(pre) @pre_resolved = false @pre << pre end # Was this task ever invoked? If this is true, it doesn't # necessarily mean that the run was successfull! def invoked? !@success.nil? end # True if last task run fail. def fail? @success == false end # Enhance this task with the given dependencies and blk. def enhance(deps = nil, &blk) if deps @pre_resolved = false @pre.concat deps end if @block if blk first_block = @block @block = lambda { |t| first_block[t] blk[t] } end else @block = blk end end # Returns a true value if task was acutally run. # Raises Rant::TaskFail to signal task (or prerequiste) failure. def invoke(opt = INVOKE_OPT) return circular_dep if @run @run = true begin return if done? internal_invoke opt ensure @run = false end end def internal_invoke(opt, ud_init = true) goto_task_home update = ud_init || opt[:force] dep = nil uf = false each_dep { |dep| if dep.respond_to? :timestamp handle_timestamped(dep, opt) && update = true elsif Node === dep handle_node(dep, opt) && update = true else dep, uf = handle_non_node(dep, opt) uf && update = true dep end } if @receiver goto_task_home update = true if @receiver.update?(self) end # Never run a task block for a "needed?" query. return update if opt[:needed?] run if update @success = true # IMPORTANT: return update flag update rescue StandardError => e @success = false self.fail(nil, e) end private :internal_invoke # Called from internal_invoke. +dep+ is a prerequisite which # is_a? Node, but not a FileTask. +opt+ are opts as given to # Node#invoke. # # Override this method in subclasses to modify behaviour of # prerequisite handling. # # See also: handle_timestamped, handle_non_node def handle_node(dep, opt) dep.invoke opt end # Called from internal_invoke. +dep+ is a prerequisite which # is_a? FileTask. +opt+ are opts as given to Node#invoke. # # Override this method in subclasses to modify behaviour of # prerequisite handling. # # See also: handle_node, handle_non_node def handle_timestamped(dep, opt) dep.invoke opt end # Override in subclass if specific task can handle # non-task-prerequisites. # # Notes for overriding: # This method should do one of the two following: # [1] Fail with an exception. # [2] Return two values: replacement_for_dep, update_required # # See also: handle_node, handle_timestamped def handle_non_node(dep, opt) @rac.err_msg "Unknown task `#{dep}',", "referenced in `#{rantfile.path}', line #{@line_number}!" self.fail end # For each non-worker prerequiste, the value returned from yield # will replace the original prerequisite (of course only if # @pre_resolved is false). def each_dep t = nil if @pre_resolved return @pre.each { |t| yield(t) } end my_full_name = full_name my_project_subdir = project_subdir @pre.map! { |t| if Node === t # Remove references to self from prerequisites! if t.full_name == my_full_name nil else yield(t) t end else t = t.to_s if Symbol === t if t == my_full_name #TODO nil else #STDERR.puts "selecting `#{t}'" selection = @rac.resolve t, my_project_subdir #STDERR.puts selection.size if selection.empty? # use return value of yield yield(t) else selection.each { |st| yield(st) } selection end end end } if @pre.kind_of? Rant::FileList @pre.resolve else @pre.flatten! @pre.compact! end @pre_resolved = true end end # class Task # A UserTask is equivalent to a Task, but it additionally takes a # block (see #needed) which is used to determine if it is needed?. class UserTask < Task def initialize(*args) super # super will set @block to a given block, but the block is # used for initialization, not ment as action @block = nil @needed = nil @target_files = nil # allow setting of @block and @needed yield self if block_given? end def act(&block) @block = block end def needed(&block) @needed = block end def file_target? @target_files and @target_files.include? @name end def each_target(&block) goto_task_home @target_files.each(&block) if @target_files end def file_target(*args) args.flatten! args << @name if args.empty? if @target_files @target_files.concat(args) else @target_files = args end end # We simply override this method and call internal_invoke with # the +ud_init+ flag according to the result of a call to the # +needed+ block. def invoke(opt = INVOKE_OPT) return circular_dep if @run @run = true begin return if done? internal_invoke(opt, ud_init_by_needed) ensure @run = false end end private def ud_init_by_needed if @needed goto_task_home @needed.arity == 0 ? @needed.call : @needed[self] #else: true #?? end end end # class UserTask class FileTask < Task def initialize(*args) super @ts = T0 end def file_target? true end def invoke(opt = INVOKE_OPT) return circular_dep if @run @run = true begin return if done? goto_task_home if File.exist? @name @ts = File.mtime @name internal_invoke opt, false else @ts = T0 internal_invoke opt, true end ensure @run = false end end def timestamp(opt = INVOKE_OPT) File.exist?(@name) ? File.mtime(@name) : T0 end def handle_node(dep, opt) #STDERR.puts "treating #{dep.full_name} as file dependency" return true if dep.file_target? && dep.invoke(opt) if File.exist? dep.name File.mtime(dep.name) > @ts elsif !dep.file_target? @rac.err_msg @rac.pos_text(rantfile.path, line_number), "in prerequisites: no such file: `#{dep.full_name}'" self.fail end end def handle_timestamped(dep, opt) return true if dep.invoke opt #puts "***`#{dep.name}' requires update" if dep.timestamp > @ts dep.timestamp(opt) > @ts end def handle_non_node(dep, opt) goto_task_home # !!?? unless File.exist? dep @rac.err_msg @rac.pos_text(rantfile.path, line_number), "in prerequisites: no such file or task: `#{dep}'" self.fail end [dep, File.mtime(dep) > @ts] end def each_target goto_task_home yield name end end # class FileTask module AutoInvokeDirNode private def run goto_task_home return if @rac.running_task(self) dir = File.dirname(name) @rac.build dir unless dir == "." || dir == "/" return unless @block @block.arity == 0 ? @block.call : @block[self] end end class AutoSubFileTask < FileTask include AutoInvokeDirNode end # An instance of this class is a task to create a _single_ # directory. class DirTask < Task def initialize(*args) super @ts = T0 @isdir = nil end def invoke(opt = INVOKE_OPT) return circular_dep if @run @run = true begin return if done? goto_task_home @isdir = test(?d, @name) if @isdir @ts = @block ? test(?M, @name) : Time.now internal_invoke opt, false else @ts = T0 internal_invoke opt, true end ensure @run = false end end def file_target? true end def handle_node(dep, opt) #STDERR.puts "treating #{dep.full_name} as file dependency" return true if dep.file_target? && dep.invoke(opt) if File.exist? dep.name File.mtime(dep.name) > @ts elsif !dep.file_target? @rac.err_msg @rac.pos_text(rantfile.path, line_number), "in prerequisites: no such file: `#{dep.full_name}'" self.fail end end def handle_timestamped(dep, opt) return @block if dep.invoke opt @block && dep.timestamp(opt) > @ts end def handle_non_node(dep, opt) goto_task_home unless File.exist? dep @rac.err_msg @rac.pos_text(rantfile.path, line_number), "in prerequisites: no such file or task: `#{dep}'" self.fail end [dep, @block && File.mtime(dep) > @ts] end def run return if @rac.running_task(self) @rac.sys.mkdir @name unless @isdir if @block @block.arity == 0 ? @block.call : @block[self] goto_task_home @rac.sys.touch @name end end def each_target goto_task_home yield name end end # class DirTask # A SourceNode describes dependencies between source files. Thus # there is no action attached to a SourceNode. The target should # be an existing file as well as all dependencies. # # An example would be a C source file which depends on other C # source files because of #include statements. # # Rantfile usage: # gen SourceNode, "myext.c" => %w(ruby.h myext.h) class SourceNode include Node def initialize(rac, name, prerequisites = []) super() @rac = rac @name = name or raise ArgumentError, "name not given" @pre = prerequisites @run = false # The timestamp is the latest of this file and all # dependencies: @ts = nil end # Use this readonly! def prerequisites @pre end # Note: The timestamp will only be calculated once! def timestamp(opt = INVOKE_OPT) # Circular dependencies don't generate endless # recursion/loops because before calling the timestamp # method of any other node, we set @ts to some non-nil # value. return @ts if @ts goto_task_home if File.exist?(@name) @ts = File.mtime @name else rac.abort_at(ch, "SourceNode: no such file -- #@name") end sd = project_subdir @pre.each { |f| nodes = rac.resolve f, sd if nodes.empty? if File.exist? f mtime = File.mtime f @ts = mtime if mtime > @ts else rac.abort_at(ch, "SourceNode: no such file -- #{f}") end else nodes.each { |node| node.invoke(opt) if node.respond_to? :timestamp node_ts = node.timestamp(opt) goto_task_home @ts = node_ts if node_ts > @ts else rac.abort_at(ch, "SourceNode can't depend on #{node.name}") end } end } @ts end def invoke(opt = INVOKE_OPT) # self.timestamp would be required for correctness... false end def related_sources @pre end end # class SourceNode end # module Rant rant-0.5.8/lib/rant/import/nodes/signed.rb0000644000175000017500000000574510527253230020012 0ustar xavierxavier # signed.rb - "Signed" node types for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/import/signedfile' module Rant def self.init_import_nodes__signed(rac, *rest) rac.node_factory = SignedNodeFactory.new end class SignedNodeFactory < DefaultNodeFactory def new_file(rac, name, pre, blk) Generators::SignedFile.new(rac, name, pre, &blk) end def new_dir(rac, name, pre, blk) Generators::SignedDirectory.new(rac, name, pre, &blk) end def new_auto_subfile(rac, name, pre, blk) Generators::AutoSubSignedFile.new(rac, name, pre, &blk) end def new_source(rac, name, pre, blk) SignedSourceNode.new(rac, name, pre, &blk) end end class SignedSourceNode < SourceNode def initialize(*args) super @signature = nil end # Invokes prerequisites and returns a signature of the source # file and all related source files. # Note: The signature will only be calculated once. def signature(opt = INVOKE_OPT) return circular_dep if @run @run = true begin return @signature if @signature goto_task_home sig_list = [] sig = @rac.var._get("__signature__") if test(?f, @name) @signature = sig.signature_for_file(@name) else @rac.abort_at(ch, "SourceNode: no such file -- #@name") end sd = project_subdir handled = {@name => true} @pre.each { |f| f = f.to_rant_target next if handled.include? f nodes = rac.resolve f, sd if nodes.empty? if test(?f, f) sig_list << sig.signature_for_file(f) else rac.abort_at(ch, "SourceNode: no such file -- #{f}") end else file_sig = nil nodes.each { |node| node.invoke(opt) if node.respond_to? :signature sig_list << node.signature goto_task_home else rac.abort_at(ch, "SourceNode can't depend on #{node.name}") end } sig_list << file_sig if file_sig end handled[f] = true } sig_list.sort! @signature << sig_list.join ensure @run = false end end end # class SignedSourceNode end # module Rant rant-0.5.8/lib/rant/import/package/0000755000175000017500000000000010527254520016467 5ustar xavierxavierrant-0.5.8/lib/rant/import/package/tgz.rb0000644000175000017500000000205410527253230017616 0ustar xavierxavier # tgz.rb - Package::Tgz generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/import/archive/tgz' # The classes in this module act as generators which create archives. # The difference to the Archive::* generators is, that the Package # generators move all archive entries into a toplevel directory. module Rant::Generators::Package class Tgz < Rant::Generators::Archive::Tgz def define_tar_task define_task_for_dir do |t| fn = @dist_dirname + (@extension ? @extension : "") old_pwd = Dir.pwd Dir.chdir @dist_root @rac.cx.sys %W(tar zcf #{fn} #@dist_dirname) Dir.chdir old_pwd end end def define_minitar_task define_task_for_dir do fn = @dist_dirname + (@extension ? @extension : "") old_pwd = Dir.pwd begin Dir.chdir @dist_root minitar_tgz fn, @dist_dirname, :recurse => true ensure Dir.chdir old_pwd end end end end # class Tgz end # module Rant::Generators::Package rant-0.5.8/lib/rant/import/package/zip.rb0000644000175000017500000000177610527253230017626 0ustar xavierxavier # zip.rb - Package::Zip generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/import/archive/zip' module Rant::Generators::Package class Zip < Rant::Generators::Archive::Zip def define_zip_task define_task_for_dir do fn = @dist_dirname + (@extension ? @extension : "") old_pwd = Dir.pwd Dir.chdir @dist_root # zip adds to existing archive @rac.cx.sys.rm_f fn if test ?e, fn # zip options: # y: store symlinks instead of referenced files # r: recurse into directories # q: quiet operation @rac.cx.sys %W(zip -yqr #{fn} #@dist_dirname) Dir.chdir old_pwd end end def define_rubyzip_task define_task_for_dir do fn = @dist_dirname + (@extension ? @extension : "") old_pwd = Dir.pwd begin Dir.chdir @dist_root rubyzip fn, @dist_dirname, :recurse => true ensure Dir.chdir old_pwd end end end end # class Zip end # module Rant::Generators::Package rant-0.5.8/lib/rant/import/signature/0000755000175000017500000000000010527254520017075 5ustar xavierxavierrant-0.5.8/lib/rant/import/signature/md5.rb0000644000175000017500000000211610527253230020104 0ustar xavierxavier # md5.rb - Recognize file changes by md5 checksums. # # Copyright (C) 2005 Stefan Lang require 'digest/md5' module Rant def self.init_import_signature__md5(rac, *rest) sig = Signature::MD5.new(rac) rac.var._set("__signature_md5__", sig) rac.var._init("__signature__", sig) end module Signature class MD5 def initialize(rac) #@rac = rac end def name "md5" end def signature_for_file(filename) signature_for_string(File.read(filename)) end def signature_for_dir(dirname) entries = Dir.entries(dirname) entries.sort! signature_for_string(entries.join << entries.size.to_s) end def signature_for_io(io) signature_for_string(io.read) end def signature_for_string(str) Digest::MD5.hexdigest(str) end end # class MD5 end # module Signature end # module Rant rant-0.5.8/lib/rant/import/sys/0000755000175000017500000000000010527254520015712 5ustar xavierxavierrant-0.5.8/lib/rant/import/sys/more.rb0000644000175000017500000000150610527253231017201 0ustar xavierxavier # more.rb - Experimental sys methods. # # Copyright (C) 2005 Stefan Lang module Rant module Sys def write_to_file(fn, content) content = content.to_str fu_output_message "writing #{content.size} bytes to file `#{fn}'" File.open fn, "w" do |f| f.write content end end def write_to_binfile(fn, content) content = content.to_str fu_output_message "writing #{content.size} bytes to file `#{fn}'" File.open fn, "wb" do |f| f.write content end end def clean(entry) if test ?f, entry rm_f entry elsif test ?e, entry rm_rf entry end end end # module Sys end # module Rant rant-0.5.8/lib/rant/import/sys/tgz.rb0000644000175000017500000000307610527253231017047 0ustar xavierxavier # tgz.rb - +sys+ methods for tgz archiving. # # Copyright (C) 2005 Stefan Lang #require 'rant/archive/minitar' #rant-import:uncomment module Rant module Sys # Unpack the gzipped tar archive, to which the +archive+ path # points. Use the :in => "some/dir" option to specify # a output directory. It defaults to the working directory. def unpack_tgz(archive, opts={}) output_dir = opts[:to] || opts[:in] || "." mkpath output_dir unless test ?d, output_dir if Env.have_tar? sh "tar", "-xzf", archive, "-C", output_dir else minitar_unpack(archive, output_dir) end nil end private def minitar_tgz(fn, files, opts) require 'zlib' require 'rant/archive/minitar' #rant-import:remove fu_output_message "minitar #{fn}" files = files.to_ary if files.respond_to? :to_ary tgz = Zlib::GzipWriter.new(File.open(fn, 'wb')) # pack closes tgz Rant::Archive::Minitar.pack(files, tgz, opts[:recurse]) nil end def minitar_unpack(archive, output_dir) fu_output_message "unpacking #{archive} in #{output_dir}" require 'zlib' require 'rant/archive/minitar' #rant-import:remove tgz = Zlib::GzipReader.new(File.open(archive, 'rb')) # unpack closes tgz Archive::Minitar.unpack(tgz, output_dir) end end # module Sys end # module Rant rant-0.5.8/lib/rant/import/sys/zip.rb0000644000175000017500000000270510527253231017043 0ustar xavierxavier # zip.rb - +sys+ methods for zip archiving. # # Copyright (C) 2005 Stefan Lang #require 'rant/archive/rubyzip' #rant-import:uncomment module Rant module Sys # Unpack the zip archive, to which the +archive+ path points. # Use the :in => "some/dir" option to specify a # output directory. It defaults to the working directory. def unpack_zip(archive, opts={}) output_dir = opts[:to] || opts[:in] || "." mkpath output_dir unless test ?d, output_dir if Env.find_bin("unzip") sh "unzip", "-q", archive, "-d", output_dir else rubyzip_unpack(archive, output_dir) end nil end private def rubyzip_unpack(archive, output_dir) fu_output_message "unpacking #{archive} in #{output_dir}" require 'rant/archive/rubyzip' #rant-import:remove f = Archive::Rubyzip::ZipFile.open archive f.entries.each { |e| fn = e.name dir = File.dirname fn if dir == "." dir = output_dir elsif output_dir != "." dir = File.join(output_dir, dir) end FileUtils.mkpath dir unless test ?d, dir f.extract(e, File.join(output_dir, fn)) } f.close end end # module Sys end # module Rant rant-0.5.8/lib/rant/import/var/0000755000175000017500000000000010527254520015664 5ustar xavierxavierrant-0.5.8/lib/rant/import/var/booleans.rb0000644000175000017500000000234610527253230020015 0ustar xavierxavier # booleans.rb - Constraints for Rantfile boolean variables. # # Copyright (C) 2005 Stefan Lang module Rant module RantVar module Constraints class Bool include Constraint class << self alias rant_constraint new end def filter(val) if ::Symbol === val or ::Integer === val val = val.to_s end if val == true true elsif val == false || val == nil false elsif val.respond_to? :to_str case val.to_str when /^\s*true\s*$/i: true when /^\s*false\s*$/i: false when /^\s*y(es)?\s*$/i: true when /^\s*n(o)?\s*$/: false when /^\s*on\s*$/i: true when /^\s*off\s*$/i: false when /^\s*1\s*$/: true when /^\s*0\s*$/: false else raise ConstraintError.new(self, val) end else raise ConstraintError.new(self, val) end end def default false end def to_s "bool" end end class BoolTrue < Bool def default true end end #-- # perhaps this should stay a secret ;) #++ def true.rant_constraint BoolTrue.rant_constraint end def false.rant_constraint Bool.rant_constraint end end # module Constraints end # module RantVar end # module Rant rant-0.5.8/lib/rant/import/var/lists.rb0000644000175000017500000000105610527253230017346 0ustar xavierxavier # lists.rb - Constraints for Rantfile list variables. # # Copyright (C) 2005 Stefan Lang module Rant module RantVar module Constraints class List include Constraint class << self alias rant_constraint new end def filter(val) if val.respond_to? :to_ary val.to_ary else raise ConstraintError.new(self, val) end end def default [] end def to_s "list (Array)" end end Array = List end # module Constraints end # module RantVar end # module Rant rant-0.5.8/lib/rant/import/var/numbers.rb0000644000175000017500000000354510527253230017670 0ustar xavierxavier # numbers.rb - Constraints for numeric Rantfile variables. # # Copyright (C) 2005 Stefan Lang module Rant module RantVar module Constraints class Integer include Constraint class << self def rant_constraint(range = nil) if range IntegerInRange.new(range) else self.new end end end def filter(val) Kernel::Integer(val) rescue raise ConstraintError.new(self, val) end def default 0 end def to_s "integer" end end class IntegerInRange < Integer def initialize(range) @range = range end def filter(val) i = super if @range === i i else raise ConstraintError.new(self, val) end end def default @range.min end def to_s super + " #{@range}" end end class Float include Constraint class << self def rant_constraint(range = nil) if range FloatInRange.new(range) else self.new end end end def filter(val) Kernel::Float(val) rescue raise ConstraintError.new(self, val) end def default 0.0 end def to_s "float" end end class FloatInRange < Float def initialize(range) @range = range end def filter(val) i = super if @range === i i else raise ConstraintError.new(self, val) end end def default @range.first end def to_s super + " #{@range}" end end end # module Constraints end # module RantVar end # module Rant class Range def rant_constraint case first when ::Integer Rant::RantVar::Constraints::IntegerInRange.new(self) when ::Float Rant::RantVar::Constraints::FloatInRange.new(self) else raise NotAConstraintFactoryError.new(self) end end end rant-0.5.8/lib/rant/import/var/strings.rb0000644000175000017500000000130610527253230017677 0ustar xavierxavier # strings.rb - Constraints for Rantfile string variables. # # Copyright (C) 2005 Stefan Lang module Rant module RantVar module Constraints class String include Constraint class << self alias rant_constraint new end def filter(val) if val.respond_to? :to_str val.to_str elsif Symbol === val val.to_s else raise ConstraintError.new(self, val) end end def default "" end def to_s "string" end end class ToString < String class << self alias rant_constraint new end def filter(val) val.to_s end end end # module Constraints end # module RantVar end # module Rant rant-0.5.8/lib/rant/import/win32/0000755000175000017500000000000010527254520016036 5ustar xavierxavierrant-0.5.8/lib/rant/import/win32/rubycmdwrapper.rb0000644000175000017500000000175110527253230021432 0ustar xavierxavier # rubycmdwrapper.rb - "Win32::RubyCmdWrapper" generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/rantlib' module Rant::Generators module Win32 module RubyCmdWrapper def self.rant_gen(rac, ch, args, &block) fl = args.first unless args.size == 1 and fl.respond_to? :to_ary rac.abort_at(ch, "Win32::RubyCmdWrapper takes a list of filenames.") end if fl.respond_to? :exclude fl.exclude "*.cmd" end fl = fl.to_ary cmd_files = fl.map { |f| f.sub_ext "cmd" } cmd_files.zip(fl).each { |cmd, bin| # the .cmd file does not depend on the bin file rac.cx.file cmd do |t| open(t.name, "w") { |f| i_bin = File.join(::Rant::Env::RUBY_BINDIR, File.basename(bin)) rac.cmd_msg "Writing #{t.name}: #{i_bin}" # TODO: Better use Env::RUBY_EXE? f.puts "@#{rac.cx.sys.sp ::Rant::Env::RUBY} #{rac.cx.sys.sp i_bin} %*" } end } cmd_files end end end end rant-0.5.8/lib/rant/import/archive.rb0000644000175000017500000002367210527253231017052 0ustar xavierxavier # archive.rb - Archiving support for Rant. # # Copyright (C) 2005 Stefan Lang # # This file currently doesn't contain a generator. Thus an import # "archive" doesn't make sense. Do an import # "archive/tgz" or import "archive/zip" instead. require 'rant/rantlib' require 'rant/import/subfile' require 'rant/metautils' #require 'rant/progress' #rant-import:uncomment #require 'rant/tempfile' #rant-import:uncomment module Rant::Generators::Archive # A subclass has to provide a +define_task+ method to act as a # generator. class Base extend Rant::MetaUtils def self.rant_gen(rac, ch, args, &block) pkg_name = args.shift unless pkg_name rac.abort_at(ch, "#{self} takes at least one argument (package name)") end opts = nil flags = [] arg = args.shift case arg when String basedir = pkg_name pkg_name = arg when Symbol flags << arg else opts = arg end flags << arg while Symbol === (arg = args.shift) opts ||= (arg || {}) unless args.empty? rac.abort_at(ch, "#{self}: too many arguments") end pkg = self.new(pkg_name) pkg.basedir = basedir if basedir pkg.rac = rac pkg.ch = ch flags.each { |f| case f when :manifest pkg.manifest = "MANIFEST" when :verbose # TODO when :quiet # TODO else rac.warn_msg( "#{self}: ignoring unknown flag #{flag}") end } if opts.respond_to? :to_hash opts = opts.to_hash else rac.abort_at(ch, "#{self}: option argument has to be a hash.") end opts.each { |k, v| case k when :version pkg.version = v when :extension pkg.extension = v when :files pkg.files = v when :manifest pkg.manifest = v when :files_only pkg.files_only = v else rac.warn_msg( "#{self}: ignoring unknown option #{k}") end } desc = pkg.rac.pop_desc if opts[:files] and opts[:manifest] || flags.include?(:manifest) pkg.define_manifest_task end pkg.rac.cx.desc desc pkg.define_task pkg end string_attr :name string_attr :version string_attr :basedir string_attr :extension rant_attr :files string_attr :manifest attr_reader :archive_path # If this is true, directories won't be included for packaging # (only files). Defaults to true. rant_attr :files_only # Caller information, e.g.: {:file => "Rantfile", :ln => 10} attr_accessor :ch def initialize(name, files = nil) self.name = name or raise "package name required" @files = files @version, @extension, @archive_path = nil @rac = nil @pkg_task = nil @ch = nil @files_only = false @manifest_task = nil @basedir = nil @res_files = nil @manifest = nil @dist_dir_task = nil end def rac @rac end def rac=(val) @rac = val @pkg_task = nil end # Path to archive file. def path if basedir File.join(basedir, get_archive_path) else get_archive_path end end alias to_rant_target path # Path to archive without basedir. def get_archive_path return @archive_path if @archive_path path = name.dup path << "-#@version" if @version path << @extension if @extension @archive_path = path end # This method sets @res_files to the return value, a list of # files to include in the archive. def get_files return @res_files if @res_files fl = @files ? @files.dup : [] if @manifest fl = read_manifest unless @files fl = Rant::FileList(fl) fl.keep(@manifest) elsif @files_only fl = Rant::FileList(fl) fl.no_dirs else fl = Rant::FileList(fl) end # remove leading `./' relicts @res_files = fl.map! { |fn| fn.sub(/^\.\/(?=.)/,'') } if defined?(@dist_path) && @dist_path # Remove entries from the dist_path directory, which # would create some sort of weird recursion. # # Normally, the Rantfile writer should care himself, # but since I tapped into this trap frequently now... @res_files.exclude(/^#{Regexp.escape @dist_path}(\/.*)?$/) # exclude the final archive file as well? end @res_files.uniq!.sort! end # Creates an (eventually) temporary manifest file and yields # with the path of this file as argument. def with_manifest fl = get_files if @manifest rac.build @manifest yield @manifest else require 'rant/tempfile' #rant-import:remove tf = Rant::Tempfile.new "rant" begin fl.each { |path| tf.puts path } tf.close yield(tf.path) ensure tf.unlink end end nil end def define_manifest_task return @manifest_task if @manifest_task @manifest_task = ::Rant::Generators::Task.rant_gen( @rac, @ch, [@manifest]) do |t| t.file_target t.needed { # fl refers to @res_files fl = get_files if test ?f, @manifest read_manifest != @res_files.to_ary else true end } t.act { write_manifest get_files } end end private def read_manifest fl = [] open @manifest do |f| f.each { |line| line.chomp! fl << line unless line.strip.empty? } end fl end def write_manifest fl @rac.cmd_msg "writing #@manifest" if @rac open @manifest, "w" do |f| fl.each { |path| f.puts path } end end def define_cmd_task return @pkg_task if @pkg_task targ = {get_archive_path => get_files} @pkg_task = ::Rant::Generators::SubFile.rant_gen( @rac, @ch, [basedir, targ].compact) do |t| with_manifest { |path| yield(path, t) } end end # Define a task to package one dir. For usage in subclasses. # This method sets the following instance variables: # [@dist_dirname] The name of the directory which shall be # the root of all entries in the archive. # [@dist_root] The directory in which the @dist_dirname # directory will be created with contents for # archiving. # [@dist_path] @dist_root/@dist_dirname (or just # @dist_dirname if @dist_root is ".") # # The block supplied to this method will be the action # to create the archive file (e.g. by invoking the tar # command). def define_task_for_dir(&block) return @pkg_task if @pkg_task @dist_dirname = File.split(name).last @dist_dirname << "-#@version" if @version @dist_root, = File.split path @dist_path = (@dist_root == "." ? @dist_dirname : File.join(@dist_root, @dist_dirname)) get_files # set @res_files targ = {get_archive_path => [@dist_path]} #STDERR.puts "basedir: #{basedir}, fn: #@archive_path" @pkg_task = ::Rant::Generators::SubFile.rant_gen( @rac, @ch, [basedir, targ].compact, &block) define_dist_dir_task @pkg_task end # This method sets the instance variable @dist_dir_task. # Assumes that @res_files is set. # # Returns a task which creates the directory @dist_path and # links/copies @res_files to @dist_path. def define_dist_dir_task return if @dist_dir_task cx = @rac.cx if @basedir @basedir.sub!(/\/$/, '') if @basedir.length > 1 c_dir = @dist_path.sub(/^#@basedir\//, '') targ = {c_dir => @res_files} else targ = {@dist_path => @res_files} end @dist_dir_task = Rant::Generators::Directory.rant_gen( @rac, @ch, [@basedir, targ].compact) { |t| # ensure to create new and empty destination directory if Dir.entries(@dist_path).size > 2 # "." and ".." cx.sys.rm_rf(@dist_path) cx.sys.mkdir(@dist_path) end # evaluate directory structure first dirs = [] fl = [] @res_files.each { |e| if test(?d, e) dirs << e unless dirs.include? e else # assuming e is a file fl << e dir = File.dirname(e) dirs << dir unless dir == "." || dirs.include?(dir) end } require 'rant/progress' #rant-import:remove # create directory structure progress = Rant::ProgressCountdown.new(dirs.size, @rac) dir_msg = "Creating directories under #@dist_path: " msg_len = dir_msg.length unless dirs.empty? @rac.cmd_print dir_msg dirs.each { |dir| FileUtils.mkpath(File.join(@dist_path, dir)) progress.inc } @rac.cmd_msg "done" end # link/copy files to package directory f = fl.first if f progress = Rant::ProgressCountdown.new(fl.size, @rac) dest = File.join(@dist_path, f) ln_supported = true begin FileUtils.ln(f, dest) fl.shift @rac.cmd_print "Linking " rescue Exception #Errno::EOPNOTSUPP ln_supported = false @rac.cmd_print "Copying " end @rac.cmd_print \ "#{progress.total} files to #@dist_path: ".ljust(msg_len - 8) progress.inc if ln_supported fl.each { |f| dest = File.join(@dist_path, f) if ln_supported FileUtils.ln(f, dest) else FileUtils.cp(f, dest) end progress.inc } @rac.cmd_msg "done" end } end end # class Base end # module Rant::Generators::Archive rant-0.5.8/lib/rant/import/autoclean.rb0000644000175000017500000000474510527253231017404 0ustar xavierxavier # autoclean.rb - "AutoClean" generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/rantlib' require 'rant/import/clean' require 'rant/import/sys/more' class Rant::Generators::AutoClean def self.rant_gen(rac, ch, args, &block) # validate args if args.size > 1 rac.abort_at(ch, "AutoClean doesn't take more than one argument.") end tname = args.first || "autoclean" # we generate a normal clean task too, so that the user can # add files to clean via a var ::Rant::Generators::Clean.rant_gen(rac, ch, [tname]) # create task rac.task :__caller__ => ch, tname => [] do |t| add_common_dirs = {} rac.tasks.each { |name, node| if Array === node f = node.first if f.file_target? add_common_dirs[File.dirname(f.full_name)] = true end node.each { |subw| subw.each_target { |entry| rac.sys.clean entry } } else if node.file_target? add_common_dirs[File.dirname(node.full_name)] = true end node.each_target { |entry| rac.sys.clean entry } end } target_rx = nil rac.resolve_hooks.each { |hook| if hook.respond_to? :each_target hook.each_target { |entry| add_common_dirs[File.expand_path(File.dirname(entry))] = true rac.sys.clean entry } elsif hook.respond_to? :target_rx next(rx) unless (t_rx = hook.target_rx) target_rx = target_rx.nil? ? t_rx : Regexp.union(target_rx, t_rx) end } t.goto_task_home if target_rx rac.vmsg 1, "searching for rule products" rac.sys["**/*"].each { |entry| if entry =~ target_rx add_common_dirs[File.dirname(entry)] = true rac.sys.clean entry end } end common = rac.var._get("__autoclean_common__") if common rac.rantfiles.each{ |rf| sd = rf.project_subdir common.each { |fn| path = sd.empty? ? fn : File.join(sd, fn) rac.sys.clean path } } #STDERR.puts add_common_dirs.inspect add_common_dirs.each { |dir, _| common.each { |fn| rac.sys.clean File.join(dir, fn) } } end end end end rant-0.5.8/lib/rant/import/clean.rb0000644000175000017500000000205210527253231016500 0ustar xavierxavier # clean.rb - "Clean" generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/rantlib' class Rant::Generators::Clean def self.rant_gen(rac, ch, args, &block) # validate args if args.size > 1 rac.abort_at(ch, "Clean doesn't take more than one argument.") end tname = args.first || "clean" # set var with task name to a MultiFileList case rac.var[tname] when nil rac.var[tname] = Rant::MultiFileList.new(rac) when Rant::RacFileList ml = Rant::MultiFileList.new(rac) rac.var[tname] = ml.add(rac.var[tname]) when Rant::MultiFileList # ok, nothing to do else # TODO: refine error message rac.abort_at(ch, "var `#{tname}' already exists.", "Clean uses var with the same name as the task name.") end # create task rac.task :__caller__ => ch, tname => [] do |t| rac.var[tname].each_entry { |entry| if test ?e, entry if test ?f, entry rac.cx.sys.rm_f entry else rac.cx.sys.rm_rf entry end end } end end end # class Rant::Generators::Clean rant-0.5.8/lib/rant/import/command.rb0000644000175000017500000001710010527253231017034 0ustar xavierxavier # command.rb - File tasks with command change recognition. # # Copyright (C) 2005 Stefan Lang #require 'rant/import/metadata' #rant-import:uncomment #require 'rant/import/signature/md5' #rant-import:uncomment module Rant def self.init_import_command(rant, *rest) rant.import "metadata" unless rant.var._get("__metadata__") rant.import "signature/md5" unless rant.var._get("__signature__") end module Generators class Command def self.rant_gen(rant, ch, args, &block) if args.size == 1 && block return \ rant.prepare_task(args.first, nil, ch) { |n,pre,_| t = rant.node_factory.new_file(rant, n, pre, nil) t.receiver = CommandManager.new(nil, block) t } elsif args.size < 2 rant.abort_at(ch, "Command: At least two " + "arguments required: task name and command.") elsif args.size > 3 rant.abort_at(ch, "Command: Too many arguments.") end # determine task name name = args.shift if name.respond_to? :to_str name = name.to_str else rant.abort_at(ch, "Command: task name (string) " + "as first argument required") end if args.size == 1 && args.first.respond_to?(:to_hash) parse_keyword_syntax(rant, ch, name, block, args[0].to_hash) else parse_plain_syntax(rant, ch, name, block, args[0], args[1]) end end def self.parse_plain_syntax(rant, ch, name, block, pre, cmd) # determine prerequisites pre ||= [] # determine command (cmd = pre; pre = []) unless cmd if cmd.respond_to? :to_str cmd = cmd.to_str else rant.abort_at(ch, "Command: command argument has " + "to be a string.") end rant.prepare_task({name => pre}, nil, ch) { |n,pre,_| t = rant.node_factory.new_file(rant, n, pre, nil) t.receiver = CommandManager.new(cmd, block) t } end def self.parse_keyword_syntax(rant, ch, name, block, hash) # TODO rant.abort_at(ch, "Command: syntax error") end end # class Command end # module Generators module Node def interp_vars!(str) str.gsub!(/\$\((\w+)\)/) { |_| val = val_for_interp_var($1) unless val.respond_to?(:to_ary) val = val.to_s interp_vars!(val) end Sys.sp(val) } str.gsub!(/\$\{(\w+)\}/) { |_| val = val_for_interp_var($1) unless val.respond_to?(:to_ary) val = val.to_s interp_vars!(val) end Sys.escape(val) } str.gsub!(/\$\[(\w+)\]/) { |_| val = val_for_interp_var($1) if val.respond_to?(:to_ary) val.to_ary.join(' ') else val = val.to_s interp_vars!(val) end val } str end def interp_symbolic_vars!(str) str.gsub!(/\$\((-|<|>)\)/) { |_| Sys.sp(val_for_interp_sym($1)) } str.gsub!(/\$\{(-|<|>)\}/) { |_| Sys.escape(val_for_interp_sym($1)) } str.gsub!(/\$\[(-|<|>)\]/) { |_| val = val_for_interp_sym($1) val.respond_to?(:to_ary) ? val.to_ary.join(' ') : val.to_s } str end private def val_for_interp_var(var) case var when "name": self.name when "prerequisites": prerequisites.map { |n| rac.resolve_root_ref(n) } when "source": rac.resolve_root_ref(source) else cx = rac.cx val = cx.var._get(var) || ( if cx.instance_eval("defined? @#{var}") cx.instance_variable_get "@#{var}" else rac.warn_msg(rac.pos_text( rantfile.path, line_number), "Command: undefined variable `#{var}'") "" end ) if val.respond_to?(:call) && val.respond_to?(:arity) val.arity == 0 ? val.call : val.call(self) elsif val.respond_to?(:to_hash) rac.warn_msg( "`#{var}' -- Avoid interpolation of hashes.\n" + "Behaviour is undecided.") "" #val.to_hash[full_name] else val end end end def val_for_interp_sym(sym) case sym when ">": name when "<": prerequisites.map { |n| rac.resolve_root_ref(n) } when "-": rac.resolve_root_ref(source) end end end class CommandManager def initialize(cmd_str, cmd_block) @cmd_str = cmd_str @cmd_block = cmd_block @command = nil end def update?(node) res_command(node) @command_changed end def has_pre_action? true end def pre_run(node) dir = File.dirname(node.name) unless dir == "." || dir == "/" node.rac.build dir, :type => :file end @command.split(/\n/).each { |cmd| node.rac.sys cmd } if @command_changed node.goto_task_home @md.path_set(@cmd_key, @new_sig, node.name) end @command_changed = @cmd_key = @new_sig = @md = nil end def pre_action_descs [@command ? "SHELL\n#@command" : "SHELL"] end private def res_command(node) return if @command @command = if @cmd_block cmd = (@cmd_block.arity == 0 ? @cmd_block.call : @cmd_block[node]) if cmd.respond_to? :to_str cmd.to_str.dup else node.rac.abort_at(node.ch, "Command: block has to return command string.") end else node.interp_vars!(@cmd_str.to_str.dup) end var = node.rac.var @md = var._get "__metadata__" sigs = var._get "__signature__" @cmd_key = "command_sig_#{sigs.name}" old_sig = @md.path_fetch(@cmd_key, node.name) sig_str = @command.gsub(/( |\t)+/, ' ') sig_str.gsub!(/\[#.*?#\]/, '') @command.gsub!(/\[#(.*?)#\]/) { |_| $1 } node.interp_symbolic_vars!(@command) @new_sig = sigs.signature_for_string(sig_str) @command_changed = old_sig != @new_sig end end # class CommandManager end # module Rant rant-0.5.8/lib/rant/import/csharp.rb0000644000175000017500000000266710527253231016712 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp/compiler_adapter_factory') # Generator for compiling c sharp sources class Rant::Generators::CSharp def self.rant_gen(rant, ch, args, &block) target = args.shift cs_args = args.shift # Verify arguments rant.abort_at(ch, "CSharp requires a target") if !target || target.empty? rant.abort_at(ch, "CSharp requires sources") if !cs_args[:sources] || cs_args[:sources].empty? # Massage argument hash dependencies = [] dependencies += cs_args[:sources] dependencies += cs_args[:resources] if cs_args[:resources] dependencies += cs_args[:libs] if cs_args[:libs] # Create a file target to the output file, # depend on all source files, resources, # and libs rant.file target => dependencies do |t| cmd = get_compiler(rant.context, cs_args).cmd(target, cs_args) rant.context.sys.sh cmd end end def self.compiler_adapter_factory @@compiler_adapter_factory ||= Rant::CSharp::CompilerAdapterFactory.new end def self.get_compiler(context, cs_args) if cs_args[:compiler] if cs_args[:compiler].respond_to?(:new) compiler = cs_args[:compiler].new else compiler = cs_args[:compiler] end cs_args.delete(:compiler) else compiler = compiler_adapter_factory.compiler(context) end compiler end end rant-0.5.8/lib/rant/import/directedrule.rb0000644000175000017500000000624110527253231020075 0ustar xavierxavier # directedrule.rb - "DirectedRule" generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/rantlib' class Rant::Generators::DirectedRule def self.rant_gen(rac, ch, args, &block) unless args.size == 1 rac.abort_at(ch, "DirectedRule takes one arguments.") end h = args.first if h.respond_to? :to_hash h = h.to_hash else rac.abort_at(ch, "Argument has to be a hash.") end ts_h, dir_h = nil, nil h.each { |k, v| v.respond_to?(:to_ary) ? dir_h = { k => v } : ts_h = { k => v } } unless dir_h rac.abort_at(ch, "Source directory argument has to be a list.") end target, source = nil, nil ts_h.each { |target, source| } target_dir, source_dirs = nil, nil dir_h.each { |target_dir, source_dirs| } if target_dir.respond_to? :to_str target_dir = target_dir.to_str else rac.abort_at(ch, "String required as target directory.") end if source_dirs.respond_to? :to_ary source_dirs = source_dirs.to_ary elsif source_dirs.respond_to? :to_str source_dirs = [source_dirs.to_str] else rac.abort_at(ch, "List of strings or string required for source directories.") end target = ".#{target}" if Symbol === target source = ".#{source}" if Symbol === source if target.respond_to? :to_str target = target.to_str else rac.abort_at(ch, "target has to be a string") end if source.respond_to? :to_str source = source.to_str else rac.abort_at(ch, "source has to be a string or symbol") end blk = self.new(rac, ch, target_dir, source_dirs, target, source, &block) blk.define_hook blk end def initialize(rac, ch, target_dir, source_dirs, target, source, &block) @rac = rac @ch = ch @source_dirs = source_dirs @target_dir = target_dir # target should be a string (file extension) @target = target.sub(/^\./, '') @target_rx = /#{Regexp.escape(target)}$/ # source should be a string (file extension) @source = source.sub(/^\./, '') @esc_target_dir = Regexp.escape(target_dir) @block = block end def [](name, rel_project_dir) #puts "rule (#@target) for #{name} ?" if name =~ /^#@esc_target_dir\// && name =~ @target_rx #puts " matches" fn = File.basename(name) src_fn = fn.sub_ext(@source) #puts " source filename #{src_fn}" src = nil @source_dirs.each { |d| path = File.join(d, src_fn) #puts " #{path} exist?" (src = path) && break if test(?e, path) } if src # pre 0.3.7 #[@rac.file(:__caller__ => @ch, name => src, &@block)] [@rac.prepare_task({name => src}, @block, @ch) { |name,pre,blk| @rac.node_factory.new_auto_subfile(@rac, name, pre, blk) }] else nil end else nil end end def define_hook @rac.resolve_hooks << self end def each_target(&block) @rac.cx.sys["#@target_dir/*"].each { |entry| yield entry if entry =~ @target_rx } end def candidates sources.map { |src| File.join(@target_dir, File.basename(src).sub_ext(@target)) } end def sources # TODO: returning a file list would be more efficient cl = [] @source_dirs.each { |dir| cl.concat(@rac.cx.sys["#{dir}/*.#@source"]) } cl end end # class Rant::Generators::DirectedRule rant-0.5.8/lib/rant/import/md5.rb0000644000175000017500000000070610527253231016107 0ustar xavierxavier # md5.rb - Use md5 checksums to recognize source changes. # # Copyright (C) 2005 Stefan Lang #require 'rant/import/signature/md5' #rant-import:uncomment #require 'rant/import/metadata' #rant-import:uncomment #require 'rant/import/nodes/signed' #rant-import:uncomment module Rant def self.init_import_md5(rac, *rest) rac.import "signature/md5" rac.import "metadata" rac.import "nodes/signed" end end rant-0.5.8/lib/rant/import/metadata.rb0000644000175000017500000001536610527253231017212 0ustar xavierxavier # metadata.rb - Management of meta-information for Rant targets. # # Copyright (C) 2005 Stefan Lang module Rant def self.init_import_metadata(rac, *rest) mi = MetaData::Interface.new(rac) rac.var._set("__metadata__", mi) rac.at_return(&mi.method(:at_rant_return)) rac.var._init("__autoclean_common__", []) << MetaData::META_FN end module MetaData META_FN = ".rant.meta" class Interface def initialize(rac) @rac = rac # the keys in this hash are project directory names, # the corresponding values are again hashes and their # keys are target names @store = {} # just a set @modified_dirs = {} # just a set @read_dirs = {} end # Fetch the meta value associated with the given key for # target in dir. Note that the value will probably end in # a newline. Very important is, that the +dir+ (third # argument, relative to the projects root directory) has # to be the current working directory! An example: # project root directory: /home/foo/myproject # dir: bar # => Dir.pwd has to be: /home/foo/myproject/bar # # Returns nil only if the value doesn't exist. def fetch(key, target, dir=@rac.current_subdir, meta_dir=nil) # first check if a value for the given key, target and # dir already exists dstore = @store[dir] if dstore tstore = dstore[target] if tstore val = tstore[key] return val if val end end # check if the meta file in dir was already read unless @read_dirs.include? dir read_meta_file_in_dir(dir, meta_dir) end tstore = @store[dir][target] tstore[key] if tstore end # Set the key-value pair for the given target in dir. Note # that if value.class is not String, the value will be # replaced with a newline! key should also be a string and # mustn't contain a newline. # # Returns nil. def set(key, value, target, dir=@rac.current_subdir) value = "\n" unless value.class == String @modified_dirs[dir] ||= true dstore = @store[dir] unless dstore @store[dir] = {target => {key => value}} return end tstore = dstore[target] if tstore tstore[key] = value else dstore[target] = {key => value} end nil end def path_fetch(key, target_path) tdir, fn = File.split target_path sdir = @rac.current_subdir if tdir == '.' fetch(key, fn, sdir) else dir = sdir.empty? ? tdir : "#{sdir}/#{tdir}" fetch(key, fn, dir, tdir) end end def path_set(key, value, target_path) tdir, fn = File.split target_path sdir = @rac.current_subdir if tdir == '.' dir = sdir else dir = sdir.empty? ? tdir : "#{sdir}/#{tdir}" end #STDERR.puts "#{key}:#{value}:#{fn}:#{dir}" set(key, value, fn, dir) end def at_rant_return save unless @rac[:dry_run] end # Assumes to be called from the projects root directory. def save @modified_dirs.each_key { |dir| write_dstore(@store[dir], dir) } nil end private # assumes that dir is already the current working # directory if meta_dir is nil def read_meta_file_in_dir(dir, meta_dir) #puts "in dir: #{dir}, pwd: #{Dir.pwd}" @read_dirs[dir] = true #fn = dir.empty? ? META_FN : File.join(dir, META_FN) fn = meta_dir ? File.join(meta_dir, META_FN) : META_FN dstore = @store[dir] @store[dir] = dstore = {} unless dstore return unless File.exist? fn open fn do |f| # first line should only contain "Rant", later # Rant versions can add version and other # information invalid_format(fn) unless f.readline == "Rant\n" until f.eof? target_name = f.readline.chomp! dstore[target_name] = read_target_data(f) end end rescue invalid_format(fn) end def read_target_data(file) h = {} num_of_entries = file.readline.to_i num_of_entries.times { |i| key = file.readline.chomp! value = "" file.readline.to_i.times { |j| value << file.readline } value.chomp! h[key] = value } h end # assumes to be called from the projects root directory def write_dstore(dstore, dir) fn = dir.empty? ? META_FN : File.join(dir, META_FN) target = sigs = key = value = lines = nil open fn, "w" do |f| f.puts "Rant" dstore.each { |target, sigs| f.puts target f.puts sigs.size sigs.each { |key, value| f.puts key lines = value.split(/\n/) f.puts lines.size f.puts lines } } end end def invalid_format(fn) raise Rant::Error, "The file `#{fn}' is used by " + "Rant to store meta information, it is in an\n" + "invalid state. Check that it doesn't contain\n" + "important data, remove it and retry." end end end # module MetaData end # module Rant rant-0.5.8/lib/rant/import/nunittest.rb0000644000175000017500000000171010527253231017453 0ustar xavierxavier# Generator for running NUnit tests class Rant::Generators::NUnitTest def self.rant_gen(rant, ch, args, &block) target = args.shift gen_args = args.shift # Verify arguments rant.abort_at(ch, "NUnitTest requires a task name") if !target || target.empty? rant.abort_at(ch, "NUnitTest requires dlls") if !gen_args[:dlls] || gen_args[:dlls].empty? # Define test task rant.task target do |t| gen_args[:bin] ||= "nunit-console /nologo" dlls = process_dlls(rant, gen_args[:dlls]) rant.context.sys.sh "#{gen_args[:bin]} #{dlls}" end end def self.process_dlls(rant, dlls) if dlls.respond_to?(:arglist) dlls = dlls.arglist elsif dlls.kind_of?(Array) dlls = dlls.collect{|x| rant.context.sys.sp(x)}.join(" ") else dlls = rant.context.sys.sp(dlls) end dlls end end rant-0.5.8/lib/rant/import/resgen.rb0000644000175000017500000000142210527253231016701 0ustar xavierxavier# Create a file task for compiling resources class Rant::Generators::Resgen def self.rant_gen(rant, ch, args, &block) gen_args = args.shift gen_args[:build_dir] ||= '' gen_args[:build_dir] += '/' if !gen_args[:build_dir].empty? gen_args[:namespace] ||= '' gen_args[:namespace] += '.' if !gen_args[:namespace].empty? prefix = Regexp.escape(gen_args[:build_dir] + gen_args[:namespace]) regex = Regexp.new("#{prefix}(.+?)\\.resources") src = lambda { |target| [regex.match(target)[1].gsub(/\./, "/") + ".resx"] } rant.context.gen Rant::Generators::Rule, regex => src do |t| rant.context.sys.sh "resgen /useSourcePath /compile " + rant.context.sys.sp("#{t.source},#{t.name}") end end end rant-0.5.8/lib/rant/import/rubydoc.rb0000644000175000017500000000636410527253231017077 0ustar xavierxavier require 'rant/rantlib' module Rant class Generators::RubyDoc class << self def rant_gen(app, ch, args, &block) if !args || args.empty? self.new(app, ch, &block) elsif args.size == 1 name, pre = app.normalize_task_arg(args.first, ch) self.new(app, ch, name, pre, &block) else app.abort(app.pos_text(file, ln), "RubyDoc takes only one additional argument, " + "which should be like one given to the `task' command.") end end end # Directory where (html) output goes to. # Defaults to "doc". attr_accessor :op_dir alias dir op_dir alias dir= op_dir= # Files and directories to document. Initialized to an array # with the single entry "lib" if a directory of this name # exists, or to an empty array otherwise. attr_accessor :files # List of other options. Initialized to and empty array. attr_accessor :opts # Print rdoc command if true. Defaults to false. attr_accessor :verbose # Task name of rdoc-task. attr_reader :name def initialize(app, ch, name = :doc, prerequisites = [], &block) @name = name @pre = prerequisites @op_dir = "doc" @files = test(?d, "lib") ? ["lib"] : [] @opts = [] @verbose = false yield self if block_given? app.var[:rubydoc_opts] = self.rdoc_opts.dup @pre ||= [] @pre.concat(self.rdoc_source_deps) index = self.op_html_index # define task task with given name first, so that it takes # any previously set description t = app.task(:__caller__ => ch, @name => []) t.instance_variable_set(:@rdoc_op_dir, @op_dir) def t.each_target super yield @rdoc_op_dir if @rdoc_op_dir end # The task which will actually run rdoc. t << app.file(:__caller__ => ch, index => @pre) { |t| # We delay the require of the RDoc code until it is # actually needed, so it will be only loaded if the # rdoc task has to be run. begin require 'rdoc/rdoc' rescue LoadError app.abort_at(ch, "RDoc not available.") end args = self.rdoc_args app.cmd_msg "rdoc #{args.join(' ')}" if @verbose begin RDoc::RDoc.new.document(args) rescue RDoc::RDocError => e $stderr.puts e.message t.fail end } end # Get a list of all options as they would be given to +rdoc+ # on the commandline. def rdoc_opts optlist = [] optlist << "-o" << @op_dir if @op_dir if @opts # validate opts case @opts when Array # ok, nothing to do when String @opts = @opts.split else if @opts.respond_to? :to_ary @opts = @opts.to_ary else raise RantfileException, "RDoc options should be a string or a list of strings." end end optlist.concat @opts end optlist end alias options rdoc_opts # Get a list with all arguments which would be given to rdoc # on the commandline. def rdoc_args al = rdoc_opts al.concat @files if @files al end def op_html_index File.join(@op_dir, "index.html") end def rdoc_source_deps # TODO: optimize and refine deps = [] @files.each { |e| if test(?d, e) deps.concat Dir["#{e}/**/*.rb"] else deps << e end } deps end end # class Generators::RubyDoc end # module Rant rant-0.5.8/lib/rant/import/rubypackage.rb0000644000175000017500000002364110527253231017722 0ustar xavierxavier require 'rant/rantlib' class Rant::Generators::RubyPackage class << self def rant_gen(app, ch, args, &block) if !args || args.empty? self.new(:app => app, :__caller__ => ch, &block) elsif args.size == 1 pkg_name = case args.first when String: args.first when Symbol: args.first.to_s else app.abort("RubyPackage takes only one additional " + "argument, which should be a string or symbol.") end self.new(:app => app, :__caller__ => ch, :name => pkg_name, &block) else app.abort(app.pos_text(file, ln), "RubyPackage takes only one additional argument, " + "which should be a string or symbol.") end end end # Attributes with a single value. PACKAGE_SINGLE_ATTRS = [ "name", "date", "description", "email", "has_rdoc", "homepage", "platform", "required_ruby_version", "rubyforge_project", "summary", "version", ] # These attributes may be set to a single value, which will be # converted to an array with a single element. PACKAGE_TO_LIST_ATTRS = [ "author", "bindir", "executable", "extension", "files", "rdoc_options", "requires", "test_files", "test_suite", ] PACKAGE_ATTRS = PACKAGE_SINGLE_ATTRS + PACKAGE_TO_LIST_ATTRS EXPLICIT_GEM_MAPPING = { "executable" => "executables", "requires" => "requirements", # add requires => requirements ? } PACKAGE_NO_VAL = Object.new PACKAGE_SINGLE_ATTRS.each { |a| eval <<-EOM, binding def #{a}(val = PACKAGE_NO_VAL) if val.equal? PACKAGE_NO_VAL @data["#{a}"] else self.#{a} = val end end def #{a}=(val) @data["#{a}"] = val end EOM } PACKAGE_TO_LIST_ATTRS.each { |a| eval <<-EOM, binding def #{a}(val0 = PACKAGE_NO_VAL, *args) if val0.equal? PACKAGE_NO_VAL @data["#{a}"] else self.#{a} = [val0, *args].flatten end end def #{a}=(val) unless val.nil? || Array === val if val.respond_to? :to_ary val = val.to_ary else val = [val] end end @data["#{a}"] = val end EOM } # A hash containing all package information. attr_reader :data # Directory where packages go to. Defaults to "pkg". attr_accessor :pkg_dir def initialize(opts = {}) @app = opts[:app] or raise ":app argument required" @pkg_dir = "pkg" @pkg_dir_task = nil @gem_task = nil @tar_task = nil @zip_task = nil @package_task = nil name = opts[:name] @ch = opts[:__caller__] || Rant::Lib.parse_caller_elem(caller[0]) unless name # TODO: pos_text @app.warn_msg(@app.pos_text(@ch[:file], @ch[:ln]), "No package name given, using directory name.") # use directory name as project name name = File.split(Dir.pwd)[1] # reset name if it contains a slash or a backslash name = nil if name =~ /\/|\\/ end @data = { "name" => name } yield self if block_given? end def method_missing(sym, *args) super unless args.size == 1 a = sym.to_s if a =~ /^gem_([^=]+)=$/ @data["gem-#$1"] = args.first else super end end def validate_attrs(pkg_type = :general) %w(name files).each { |a| pkg_requires_attr a } case pkg_type when :gem %w(version summary).each { |a| gem_requires_attr a } end end private :validate_attrs def gem_requires_attr(attr_name) unless @data[attr_name] || @data["gem-#{attr_name}"] @app.abort("RubyPackaged defined: " + @app.pos_text(@ch[:file], @ch[:ln]), "gem specification requires `#{attr_name}' attribute") end end def pkg_requires_attr(attr_name) unless @data[attr_name] @app.abort("RubyPackaged defined: " + @app.pos_text(@ch[:file], @ch[:ln]), "`#{attr_name}' attribute required") end end def map_to_gemspec spec mapped_attrs = [] # Map attributes from data to the gem spec as explicitely # specified. EXPLICIT_GEM_MAPPING.each_pair { |attr, gem_attr| setter = "#{gem_attr}=" if @data.key? attr mapped_attrs << attr spec.send setter, @data[attr] end } # Try to map other attributes. @data.each_pair { |attr, val| next if attr =~ /^gem\-./ next if mapped_attrs.include? attr setter = "#{attr}=" if attr == "files" spec.send(setter, val.dup) if val next end spec.send(setter, val) if spec.respond_to? setter } # `gem-' attributes override others for gem spec @data.each_pair { |attr, val| if attr =~ /^gem\-(.+)$/ spec.send("#$1=", val) end } end private :map_to_gemspec def pkg_dir_task return if @pkg_dir_task @pkg_dir_task = Rant::Generators::Directory.rant_gen(@app, @ch, [@pkg_dir]) end # Create task for gem building. If tname is a true value, a # shortcut-task will be created. def gem_task(tname = :gem) validate_attrs(:gem) # We first define the task to create the gem, and afterwards # the task to create the pkg directory to ensure that a # pending description is used to describe the gem task. pkg_name = gem_pkg_path if tname # shortcut task @app.task({:__caller__ => @ch, tname => pkg_name}) end # actual gem-creating task @gem_task = @app.file({:__caller__ => @ch, pkg_name => [@pkg_dir] + files}) { |t| # We require rubygems as late as possible to save some # execution cycles if possible ;) begin require 'rubygems' rescue LoadError => e t.fail "Couldn't load `rubygems'. " + "Probably RubyGems isn't installed on your system." end Gem.manage_gems # map rdoc options from application vars @data["rdoc_options"] ||= @app.var[:rubydoc_opts] if @data["rdoc_options"] # remove the --op option, otherwise rubygems will # install the rdoc in the wrong directory (at least as # of version 0.8.6 of rubygems) @data["rdoc_options"] = without_rdoc_op_opt(@data["rdoc_options"]) # automatically set "has_rdoc" to true unless it was # explicitely set to false (but if someone sets # options for rdoc, he probably wants to run rdoc...) @data["has_rdoc"] = true if @data["has_rdoc"].nil? end spec = Gem::Specification.new do |s| map_to_gemspec(s) end # fix for YAML bug in Ruby 1.8.3 and 1.8.4 previews # # Update: That's not a bug in YAML 1.8.3 and later, it's a # non-backwards compatible change, because YAML 1.8.2 and # later is buggy. (My current understanding of this # issue.) Adding the "---" at the start ensures that it # can be read with all Ruby YAML versions. if RUBY_VERSION > "1.8.2" def spec.to_yaml(*args, &block) yaml = super yaml =~ /^---/ ? yaml : "--- #{yaml}" end end fn = nil begin fn = Gem::Builder.new(spec).build rescue Gem::InvalidSpecificationException => e t.fail "Invalid Gem specification: " + e.message rescue Gem::Exception => e t.fail "Gem error: " + e.message end @app.sys.mv(fn, @pkg_dir) if @pkg_dir } pkg_dir_task end def tar_task(tname = :tar) validate_attrs # Create tar task first to ensure that a pending description # is used for the tar task and not for the dist dir task. pkg_name = tar_pkg_path pkg_files = files if tname # shortcut task @app.cx.task({:__caller__ => @ch, tname => pkg_name}) end # actual tar-creating task @app.cx.import "package/tgz" @tar_task = Rant::Generators::Package::Tgz.rant_gen(@app, @ch, ["#@pkg_dir/#{pkg_base_name}", # we use tar.gz extension here for backwards compatibility {:files => pkg_files, :extension => ".tar.gz"}]) end def zip_task(tname = :zip) validate_attrs # Create zip task first to ensure that a pending description # is used for the zip task and not for the dist dir task. pkg_name = zip_pkg_path pkg_files = files if tname # shortcut task @app.task({:__caller__ => @ch, tname => pkg_name}) end # actual zip-creating task @app.cx.import "package/zip" @zip_task = Rant::Generators::Package::Zip.rant_gen(@app, @ch, ["#@pkg_dir/#{pkg_base_name}", {:files => pkg_files}]) end # Create a task which runs gem/zip/tar tasks. def package_task(tname = :package) def_tasks = [@gem_task, @tar_task, @zip_task].compact if def_tasks.empty? # take description for overall package task pdesc = @app.pop_desc unless def_available_tasks @app.desc pdesc @app.warn_msg("No tools for packaging available (tar, zip, gem):", "Can't generate task `#{tname}'.") return end @app.desc pdesc end pre = [] pre << tar_pkg_path if @tar_task pre << zip_pkg_path if @zip_task pre << gem_pkg_path if @gem_task @app.task(:__caller__ => @ch, tname => pre) end # Returns true if at least one task was defined. def def_available_tasks self.tar_task(nil) self.zip_task(nil) begin require 'rubygems' self.gem_task(nil) defined = true rescue LoadError end true end def pkg_base_name unless name @app.abort(@app.pos_text(@ch[:file], @ch[:ln]), "`name' required for packaging") end version ? "#{name}-#{version}" : name end def gem_pkg_path pkg_dist_dir + ".gem" end def tar_pkg_name pkg_base_name + ".tar.gz" end def tar_pkg_path pkg_dist_dir + ".tar.gz" end def zip_pkg_name pkg_base_name + ".zip" end def zip_pkg_path pkg_dist_dir + ".zip" end def pkg_dist_dir @pkg_dir ? File.join(@pkg_dir, pkg_base_name) : pkg_base_name end # Remove -o and --op options from rdoc arguments. # Note that this only works if -o isn't part of an argument with # multiple one-letter options! def without_rdoc_op_opt(rdoc_args) last_was_op = false rdoc_args.reject { |arg| if last_was_op last_was_op = false next true end case arg when /^(-o|--op)$/ last_was_op = true true when /^-o./ true else false end } end end # class Rant::Generators::RubyPackage rant-0.5.8/lib/rant/import/rubytest.rb0000644000175000017500000000730710527253231017307 0ustar xavierxavier require 'rant/rantlib' module Rant class Generators::RubyTest def self.rant_gen(app, ch, args, &block) if !args || args.empty? self.new(app, ch, &block) elsif args.size == 1 name, pre = app.normalize_task_arg(args.first, ch) self.new(app, ch, name, pre, &block) else app.abort_at(ch, "RubyTest takes only one additional argument, " + "which should be like one given to the `task' command.") end end attr_accessor :verbose attr_accessor :libs attr_accessor :options attr_accessor :test_dirs attr_accessor :pattern attr_accessor :test_files # Directory where to run unit tests. attr_accessor :test_dir # How to load tests. Possible values: # [:rant] Use Rant's loading mechanism. This is default. # [:testrb] Use the testrb script which comes with Ruby # 1.8.1 and newer installations. attr_accessor :loader def initialize(app, cinf, name = :test, prerequisites = [], &block) @rac = app @name = name @pre = prerequisites @block = block @verbose = nil cf = cinf[:file] @libs = [] libdir = File.join(File.dirname( File.expand_path(cf)), 'lib') @libs << libdir if test(?d, libdir) @options = [] @test_dirs = [] @pattern = nil @test_files = nil @test_dir = nil #@loader = RUBY_VERSION < "1.8.4" ? :testrb : :rant @loader = :rant yield self if block_given? @pattern = "test*.rb" if @pattern.nil? && @test_files.nil? @pre ||= [] # define the task app.task(:__caller__ => cinf, @name => @pre) { |t| args = [] if @libs && !@libs.empty? args << "-I#{@libs.join File::PATH_SEPARATOR}" end case @loader when :rant: script = rb_testloader_path if script args << script else args << "-S" << "testrb" app.warn_msg("Rant's test loader not found. " + "Using `testrb'.") end when :testrb: args << "-S" << "testrb" else @rac.abort_at(cinf, "RubyTest: No such test loader -- #@loader") end args.concat optlist if @test_dir app.sys.cd(@test_dir) { args.concat filelist app.sys.ruby args } else if test(?d, "test") @test_dirs << "test" elsif test(?d, "tests") @test_dirs << "tests" end args.concat filelist app.context.sys.ruby args end } end def optlist if @options.respond_to? :to_ary @options.to_ary elsif @options [@options.to_str] else [] end # previously Rant (0.4.4 and earlier versions) honoured # var["TESTOPTS"] end def filelist return @rac.sys[@rac.var['TEST']] if @rac.var['TEST'] filelist = @test_files || [] if filelist.empty? if @test_dirs && !@test_dirs.empty? @test_dirs.each { |dir| filelist.concat(@rac.sys[File.join(dir, @pattern)]) } else filelist.concat(@rac.sys[@pattern]) if @pattern end end filelist end def rb_testloader_path $LOAD_PATH.each { |libdir| path = File.join(libdir, "rant/script/rb_testloader.rb") return path if File.exist?(path) } nil end end # class Generators::RubyTest end # module Rant rant-0.5.8/lib/rant/import/signedfile.rb0000644000175000017500000002105410527253231017532 0ustar xavierxavier # signedfile.rb - File tasks with checksum change recognition. # # Copyright (C) 2005 Stefan Lang module Rant def self.init_import_signedfile(rac, *rest) rac.import "signature/md5" unless rac.var._get("__signature__") rac.import "metadata" unless rac.var._get("__metadata__") end module Generators class SignedFile include Node def self.rant_gen(rac, ch, args, &block) unless args.size == 1 rac.abort_at(ch, "SignedFile: too many arguments") end rac.prepare_task(args.first, block, ch) { |name,pre,blk| self.new(rac, name, pre, &blk) } end attr_accessor :receiver def initialize(rac, name, prerequisites, &block) super() @rac = rac @name = name @pre = prerequisites or raise ArgumentError, "prerequisites required" @block = block @run = false @receiver = nil end def prerequisites @pre end alias deps prerequisites # first prerequisite def source @pre.first.to_s end def has_actions? @block or @receiver && @receiver.has_pre_action? end def file_target? true end def <<(pre) @pre << pre end def invoked? !@success.nil? end def fail? @success == false end def enhance(deps = nil, &blk) @pre.concat(deps) if deps if @block if blk first_block = @block @block = lambda { |t| first_block[t] blk[t] } end else @block = blk end end def invoke(opt = INVOKE_OPT) return circular_dep if @run @run = true begin return if done? goto_task_home @cur_checksums = [] @sigs = @rac.var._get("__signature__") @md = @rac.var._get("__metadata__") key = "prerequisites_sig_#{@sigs.name}" target_key = "target_sig_#{@sigs.name}" up = signed_process_prerequisites(opt) up ||= opt[:force] up = true if @receiver && @receiver.update?(self) @cur_checksums.sort! check_str = @cur_checksums.join @cur_checksums = nil old_check_str = @md.path_fetch(key, @name) old_target_str = @md.path_fetch(target_key, @name) # check explicitely for plain file, thus allow the # target of a SignedFile to be a directory ;) if test(?f, @name) target_str = @sigs.signature_for_file(@name) else target_str = "" up ||= !File.exist?(@name) end check_str_changed = old_check_str != check_str target_changed = old_target_str != target_str up ||= check_str_changed || target_changed return up if opt[:needed?] return false unless up # run action and save checksums run goto_task_home target_str = test(?f, @name) ? @sigs.signature_for_file(@name) : "" target_changed = target_str != old_target_str if target_changed @md.path_set(target_key, target_str, @name) end if check_str_changed @md.path_set(key, check_str, @name) end @success = true return target_changed rescue TaskFail => e raise rescue Exception => e self.fail(nil, e) ensure @md = @sigs = nil @run = false end end def each_target goto_task_home yield @name end def timestamp(opt = INVOKE_OPT) File.exist?(@name) ? File.mtime(@name) : T0 end def signature goto_task_home sigs = @rac.var._get("__signature__") md = @rac.var._get("__metadata__") key = "target_sig_#{sigs.name}" md.path_fetch(key, @name) end private # returns true if update required def signed_process_prerequisites(opt) up = false # set with already handled prerequisites, don't # handle on prerequisite multiple times handled = {@name => true} my_subdir = project_subdir @pre.each { |dep| dep_str = dep.to_rant_target next if handled.include? dep_str if Node === dep up = true if handle_node(dep, dep_str, opt) else tasks = @rac.resolve(dep_str, my_subdir) if tasks.empty? if test(?d, dep_str) handle_dir(dep_str) elsif File.exist?(dep_str) handle_file(dep_str) else rac.err_msg @rac.pos_text(rantfile.path, line_number), "in prerequisites: no such file or task: `#{dep_str}'" self.fail end else tasks.each { |t| up = true if handle_node(t, dep_str, opt) } end end handled[dep_str] = true } up end def handle_node(node, dep_str, opt) up = node.invoke(opt) if node.file_target? if node.respond_to? :signature @cur_checksums << node.signature elsif test(?f, dep_str) # calculate checksum for plain file handle_file(dep_str) elsif File.exist?(dep_str) @cur_checksums << @sigs.signature_for_string(dep_str) elsif !node.file_target? self.fail "can't handle prerequisite `#{dep_str}'" end goto_task_home # changed in 0.4.7 false #up end def handle_file(path) @cur_checksums << @sigs.signature_for_file(path) end def handle_dir(path) @cur_checksums << @sigs.signature_for_string(path) end end # class SignedFile class AutoSubSignedFile < SignedFile include AutoInvokeDirNode end class SignedDirectory < SignedFile def respond_to?(meth) if meth == :signature @block else super end end def signature goto_task_home sigs = @rac.var._get("__signature__") md = @rac.var._get("__metadata__") key = "prerequisites_sig_#{sigs.name}" md.path_fetch(key, @name) end private def run return if @rac.running_task(self) @rac.cx.sys.mkdir @name unless test ?d, @name if @block @block.arity == 0 ? @block.call : @block[self] goto_task_home # for compatibility with mtime based tasks @rac.cx.sys.touch @name end end end # class SignedDirectory end # module Generators end # module Rant rant-0.5.8/lib/rant/import/subfile.rb0000644000175000017500000000246710527253231017061 0ustar xavierxavier # subfile.rb - "SubFile" generator for Rant. # # Copyright (C) 2005 Stefan Lang require 'rant/rantlib' class Rant::Generators::SubFile def self.rant_gen(rac, ch, args, &block) case args.size when 1 fine, basedir = args when 2 basedir, fine = args else rac.abort_at(ch, "SubFile takes one or two arguments.") end deps = [] if fine.respond_to? :to_hash # hash should contain only one element fine = fine.to_hash fine.each { |k, v| ch = v && next if k == :__caller__ fine = k if Rant::FileList === v deps = v.dup elsif v.respond_to? :to_ary deps.concat(v.to_ary) else deps << v end } end path = basedir ? File.join(basedir, fine) : fine file_desc = rac.pop_desc rac.prepare_task({path => deps}, block, ch) { |name,pre,blk| dir, file = File.split(fine.to_s) dirp = basedir ? File.join(basedir, dir) : dir unless dir == "." unless rac.tasks.include? dirp if basedir ::Rant::Generators::Directory.rant_gen( rac, ch, [basedir, dir]) else ::Rant::Generators::Directory.rant_gen( rac, ch, [dir]) end end pre << dirp end rac.cx.desc file_desc rac.node_factory.new_file(rac, name, pre, blk) } end end rant-0.5.8/lib/rant/import/truth.rb0000644000175000017500000000104010527253231016560 0ustar xavierxavier #-- # truth.rb - Import truth into rant ;) # # Copyright (C) 2005 Stefan Lang module Rant module Node def %(desc) @description = case @description when nil: desc when /\n$/: @description + desc else "#@description\n#{desc}" end self end end class RacFileList def %(fu_sym) @rac.cx.sys.send(fu_sym, to_ary) end end end module RantContext def drag(name, *args, &block) import(name.to_s.downcase) gen(::Rant::Generators.const_get(name), *args, &block) end end rant-0.5.8/lib/rant/plugin/0000755000175000017500000000000010527254520015060 5ustar xavierxavierrant-0.5.8/lib/rant/plugin/README0000644000175000017500000000330210527253227015741 0ustar xavierxavier WARNING:: The current plugin interface will vanish as soon as there are replacements for the two existing plugins ("Csharp" and "Configure") distributed with Rant. Don't write new plugins!. = Rant plugins An Rant plugin provides additional functionality for the Rant program. Every Ruby script (file ending in .rb) in the rant/plugin directory is considered a plugin. Normally, it contains one class in the Rant::Plugin module. == Requirements for a plugin One file ending in .rb in the rant/plugin directory. One class, defined in this file, in the Rant::Plugin namespace responding to the +new_plugin+ method. Note that the *class* has to respond! This is a list of all methods the plugin class/instance has to respond to: === Class methods: [plugin_create] Instantiates the plugin. Will be called every time a new Rant::RantApp is created. === Instance methods: [rant_plugin_init] Called on Rant startup. Usually directly after plugin instantiation. [rant_start] Called before Rant runs the first task. [rant_done] Called when Rant has *successfully* processed all required tasks. [rant_plugin_stop] Signals that the plugin should be stopped. The plugin object should respond by disposing all reserved resources, closing open files etc. [rant_quit] This is always called before the Rant application quits, even if there was a failure. Usually immediately called after +rant_plugin_stop+. == Startup Every time a Rant application is initialized, it calls the +plugin_create+ method of every class (which has this method) in the Plugin module. This method should return a new plugin instance. == Shutdown rant-0.5.8/lib/rant/plugin/configure.rb0000644000175000017500000002136210527253227017375 0ustar xavierxavier require 'rant/plugin_methods' require 'yaml' # Configure plugin for Rant module Rant::Plugin # === Startup of configure plugin # ==== Config file exists # The configuration file will be read and the data hash # set up accordingly. # ==== Config file doesn't exist # The configuration process is run with +init_modes+ which # has to be one of CHECK_MODES. +init_modes+ defaults to # :default, which means if the configfile doesn't exist, # all values will be set to their defaults on startup. # === Access to configuration in Rantfile # You can access all configuration values through the [] # and []= operators of the configure plugin. # # Example of configure in Rantfile: # # conf = plugin :Configure do |conf| # conf.task # define a task named :configure # conf.check "profile" do |c| # c.default "full" # c.guess { ENV["PROFILE"] } # c.interact { # conf.prompt "What build-profile should be used?" # } # end # conf.check "optimize" do |c| # c.default true # c.guess { ENV["OPTIMIZE"] } # c.interact { # conf.ask_yes_no "Optimize build?" # } # end # end # # # Set default target depending on profile: # task :default => conf["profile"] class Configure include ::Rant::PluginMethods include ::Rant::Console class << self def rant_plugin_new(app, cinf, *args, &block) if args.size > 1 app.abort(app.pos_text(cinf[:file], cinf[:ln]), "Configure plugin takes only one argument.") end self.new(app, args.first, &block) end end CHECK_MODES = [ :default, :env, :guess, :interact, ] # Name for this plugin instance. Defaults to "configure". attr_reader :name # Name of configuration file. attr_accessor :file # This flag is used to determine if data has changed and # should be saved to file. attr_accessor :modified # An array with all checks to perform. attr_reader :checklist # Decide what the configure plugin does on startup if the # configuration file doesn't exist. Initialized to # [:guess]. # # If you want to control when the plugin should initialize the # configuration values, set this to +[:explicit]+ and call the # +init+ method with the init_modes you like as argument. attr_accessor :init_modes # Decide what the configure plugin does *after* reading the # configuration file (or directly after running +init_modes+ # if the configuration file doesn't exist). # Initialized to [:env], probably the only usefull # value. attr_accessor :override_modes # Don't write to file, config values will be lost when # rant exits! attr_accessor :no_write # Don't read or write to configuration file nor run +guess+ or # +interact+ blocks if *first* target given on commandline # is in this list. This is usefull for targets that remove # the configuration file. # Defaults are "distclean", "clobber" and "clean". attr_reader :no_action_list def initialize(app, name = nil) @name = name || rant_plugin_type @app = app or raise ArgumentError, "no application given" @file = "config" @checklist = [] @init_modes = [:guess] @override_modes = [:env] @no_write = false @modified = false @no_action_list = ["distclean", "clobber", "clean"] @no_action = false @configured = false yield self if block_given? run_checklist([:default]) # we don't need to save our defaults @modified = false end # Get the value for +key+ from +checklist+ or +nil+ if there # isn't a check with the given +key+. def [](key) c = checklist.find { |c| c.key == key } c ? c.value : nil end # Creates new check with default value if key doesn't exist. def []=(key, val) c = checklist.find { |c| c.key == key } if c if c.value != val c.value = val @modified = true end else self.check(key) { |c| c.default val } end end # Sets the specified check if a check with the given key # exists. # Returns the value if it was set, nil otherwise. def set_if_exists(key, value) c = checklist.find { |c| c.key == key } if c c.value = value @modified = true else nil end end # Builds a hash with all key-value pairs from checklist. def data hsh = {} @checklist.each { |c| hsh[c.key] = c.value } hsh end # This is true, if either a configure task was run, or the # configuration file was read. def configured? @configured end # Define a task with +name+ that will run the configuration # process in the given +check_modes+. If no task name is given # or it is +nil+, the plugin name will be used as task name. def task(name = nil, check_modes = [:guess, :interact]) name ||= @name cinf = ::Rant::Lib.parse_caller_elem(caller[0]) file = cinf[:file] ln = cinf[:ln] || 0 if !Array === check_modes || check_modes.empty? @app.abort(@app.pos_text(file, ln), "check_modes given to configure task has to be an array", "containing at least one CHECK_MODE symbol") end check_modes.each { |cm| unless CHECK_MODES.include? cm @app.abort(@app.pos_text(file,ln), "Unknown checkmode `#{cm.to_s}'.") end } nt = @app.task(name) { |t| run_checklist(check_modes) save @configured = true } nt end def check(key, val = nil, &block) checklist << ConfigureCheck.new(key, val, &block) end def init modes = @init_modes if modes == [:explicit] modes = [:guess] end @no_action = @no_action_list.include? @app.cmd_targets.first @no_action ||= @app[:tasks] unless @no_action init_config modes @configured = true end end # Run the configure process in the given modes. def run_checklist(modes = [:guess, :interact]) @checklist.each { |c| c.run_check(modes) } @modified = true end # Write configuration if modified. def save return if @no_write write_yaml if @modified true end # Immediately write configuration to +file+. def write write_yaml @modified = false end ###### overriden plugin methods ############################## def rant_plugin_type "configure" end def rant_plugin_name @name end def rant_plugin_init init unless @init_modes == [:explicit] end def rant_plugin_stop @no_action || save end ############################################################## private # Returns true on success, nil on failure. def init_config modes if File.exist? @file read_yaml @configured = true elsif modes != [:default] run_checklist modes end if @override_modes && !@override_modes.empty? run_checklist @override_modes end end def write_yaml @app.vmsg 1, "Writing config to `#{@file}'." File.open(@file, "w") { |f| f << data.to_yaml f << "\n" } true rescue @app.abort("When writing configuration: " + $!.message, "Ensure writing to file (doesn't need to exist) `#{@file}'", "is possible and try to reconfigure!") end def read_yaml File.open(@file) { |f| YAML.load_documents(f) { |doc| if doc.is_a? Hash doc.each_pair { |k, v| self[k] = v } else @app.abort("Invalid config file `#{@file}'.", "Please remove this file or reconfigure.") end } } rescue @app.abort("When attempting to read config: " + $!.message) end end # class Configure class ConfigureCheck include ::Rant::Console public :msg, :prompt, :ask_yes_no attr_reader :key attr_accessor :value attr_writer :default attr_accessor :guess_block attr_accessor :interact_block attr_accessor :react_block def initialize(key, val = nil) @key = key or raise ArgumentError, "no key given" @value = @default = val @guess_block = nil @interact_block = nil @react_block = nil yield self if block_given? end def default(val) @default = val @value = @default if @value.nil? end def guess(&block) @guess_block = block end def interact(&block) @interact_block = block end def react(&block) @react_block = block end # Run checks as specified by +modes+. +modes+ has to be a list # of symbols from the Configure::CHECK_MODES. def run_check(modes = [:guess], env = ENV) val = nil modes.each { |mode| case mode when :default val = @default when :env val = env[@key] when :interact val = @interact_block[self] if @interact_block when :guess val = @guess_block[self] if @guess_block else raise "unknown configure mode" end break unless val.nil? } val.nil? or @value = val @react_block && @react_block[@value] @value end end # class ConfigureCheck end # module Rant::Plugin rant-0.5.8/lib/rant/plugin/csharp.rb0000644000175000017500000001364510527253227016701 0ustar xavierxavier # C# plugin for Rant. require 'rant/plugin_methods' require 'rant/cs_compiler' # TODO require 'rant/import/nodes/default' module Rant class Generators::Assembly < CsCompiler class << self def rant_gen(app, clr, args, &block) assembly = self.new(&block) if args.size == 1 targ = args.first # embed caller information for correct resolving # of source Rantfile if targ.is_a? Hash targ[:__caller__] = clr else targ = { :__caller__ => clr, targ => [] } end app.prepare_task(targ, nil) { |name,pre,blk| assembly.out = name t = AssemblyTask.new(app, assembly, &block) # TODO: optimize pre.each { |e| t << e } t } else cinf = ::Rant::Lib.parse_caller_elem(clr) app.abort(app.pos_text(cinf[:file], cinf[:ln]), "Assembly takes one argument, " + "which should be like one given to the `task' command.") end end def csc @csc end def csc= new_csc case new_csc when CsCompiler @csc = new_csc when String @csc = CsCompiler.new( CsCompiler.cs_compiler_name(new_csc)) @csc.csc_bin = new_csc when nil @csc = nil else self.csc = new_csc.to_s end @csc end end @csc = nil # Maybe: # ["object"] # Compile to object code. Not *that* usual for .NET. # ["dll"] # Create a shared library (also called DLL). # ["exe"] # Create an executable. attr_accessor :target def initialize(comp = nil, &init_block) super() @target = nil @init_block = init_block take_common_attrs comp if comp end # Synonym for +out+. def name out end # Synonym for +out=+. def name=(new_name) out = new_name end # Take common attributes like +optimize+, +csc+ and similar # from the compiler object +comp+. def take_common_attrs comp @csc_name = comp.csc_name @long_name = comp.long_name @csc_bin = comp.csc_bin @debug = comp.debug comp.defines.each { |e| @defines << e unless @defines.include? e } comp.lib_link_pathes.each { |e| @lib_link_pathes << e unless @lib_link_pathes.include? e } @optimize = comp.optimize @warnings = comp.warnings # TODO: we currently take unconditionally all misc- and # compiler specific args comp.misc_args.each { |e| @misc_args << e unless @misc_args.include? e } comp.specific_args.each_pair { |k,v| # k is a compiler name, v is a list of arguments # specific to this compiler type. cst = @specific_args[k] unless cst @specific_args[k] = v next end v.each { |e| cst << e unless cst.include? e } } end # Call the initialization block and intialize compiler # interface. def init # setup compiler interface comp = Plugin::Csharp.csc_for_assembly(self) || self.class.csc take_common_attrs comp if comp # call initialization block @init_block[self] if @init_block # set target type unless @target @target = case @out when /\.exe$/i: "exe" when /\.dll$/i: "dll" when /\.obj$/i: "object" else "exe" end end # TODO: verify some attributes like @target end def compile ::Rant::Sys.sh(self.send("cmd_" + @target)) end end # class Generators::Assembly class AssemblyTask < FileTask def initialize(app, assembly) @assembly = assembly super(app, @assembly.out) { |t| app.context.sys assembly.send("cmd_" + assembly.target) } end def invoke(force = false) goto_task_home @assembly.init @pre.concat(@assembly.sources) @pre.concat(@assembly.resources) if @assembly.resources super end end # class AssemblyTask end # module Rant module Rant::Plugin # This plugin class is currently designed to be instantiated only # once with +rant_plugin_new+. class Csharp include ::Rant::PluginMethods @plugin_object = nil class << self def rant_plugin_new(app, cinf, *args, &block) if args.size > 1 app.abort(app.pos_text(cinf[:file], cinf[:ln]), "Csharp plugin takes only one argument.") end self.new(app, args.first, &block) end attr_accessor :plugin_object def csc_for_assembly(task) if @plugin_object @plugin_object.csc_for_assembly(task) else nil end end end # Shortcut for rant_plugin_name. attr_reader :name # A "configure" plugin. attr_accessor :config # A compiler interface with settings resulting from config. attr_reader :config_csc def initialize(app, name = nil) @name = name || rant_plugin_type @app = app or raise ArgumentError, "no application given" @config_csc = nil @config = nil self.class.plugin_object = self yield self if block_given? define_config_checks end def csc_for_assembly(assembly) @config_csc ||= csc_from_config @config_csc.nil? ? nil : @config_csc.dup end def define_config_checks return unless @config @config.check "csc" do |c| c.default "cscc" c.guess { Rant::CsCompiler.look_for_cs_compiler } c.interact { c.prompt "Command to invoke your C# Compiler: " } #c.react { # c.msg "Using `#{c.value}' as C# compiler." #} end @config.check "csc-optimize" do |c| c.default true c.interact { c.ask_yes_no "Optimize C# compilation?" } end @config.check "csc-debug" do |c| c.default false c.interact { c.ask_yes_no "Compile C# sources for debugging?" } end end def csc_from_config return nil unless @config return nil unless @config.configured? csc_bin = @config["csc"] #puts "using csc from config: " + csc_bin csc = Rant::CsCompiler.new csc.csc = csc_bin csc.optimize = @config["csc-optimize"] csc.debug = @config["csc-debug"] csc end ###### methods override from PluginMethods ################### def rant_plugin_type "csharp" end def rant_plugin_name @name end ############################################################## end # class Csharp end # module Rant::Plugin rant-0.5.8/lib/rant/script/0000755000175000017500000000000010527254520015066 5ustar xavierxavierrant-0.5.8/lib/rant/script/rb_testloader.rb0000644000175000017500000000003710527253230020241 0ustar xavierxavier ARGV.each { |fn| require fn } rant-0.5.8/lib/rant/coregen.rb0000644000175000017500000002246410527253231015537 0ustar xavierxavier # coregen.rb - Generators available in all Rantfiles. # # Copyright (C) 2005 Stefan Lang module Rant module Generators class Task def self.rant_gen(rac, ch, args, &block) unless args.size == 1 rac.abort("Task takes only one argument " + "which has to be like one given to the " + "`task' function") end rac.prepare_task(args.first, nil, ch) { |name,pre,blk| rac.node_factory.new_custom(rac, name, pre, block) } end end class Directory # Generate a task for making a directory path. # Prerequisites can be given, which will be added as # prerequistes for the _last_ directory. # # A special feature is used if you provide a block: The # block will be called after complete directory creation. # After the block execution, the modification time of the # directory will be updated. def self.rant_gen(rac, ch, args, &block) case args.size when 1 name, pre = rac.normalize_task_arg(args.first, ch) self.task(rac, ch, name, pre, &block) when 2 basedir = args.shift if basedir.respond_to? :to_str basedir = basedir.to_str else rac.abort_at(ch, "Directory: basedir argument has to be a string.") end name, pre = rac.normalize_task_arg(args.first, ch) self.task(rac, ch, name, pre, basedir, &block) else rac.abort_at(ch, "Directory takes one argument, " + "which should be like one given to the `task' command.") end end # Returns the task which creates the last directory # element (and has all other necessary directories as # prerequisites). def self.task(rac, ch, name, prerequisites=[], basedir=nil, &block) dirs = ::Rant::Sys.split_all(name) if dirs.empty? rac.abort_at(ch, "Not a valid directory name: `#{name}'") end path = basedir last_task = nil task_block = nil desc_for_last = rac.pop_desc dirs.each { |dir| pre = [path] pre.compact! if dir.equal?(dirs.last) rac.cx.desc desc_for_last # add prerequisites to pre # if prerequisites is a FileList: there is # only one save (no later removal) way to add # an entry: with << dp = prerequisites.dup pre.each { |elem| dp << elem } pre = dp task_block = block end path = path.nil? ? dir : File.join(path, dir) last_task = rac.prepare_task({:__caller__ => ch, path => pre}, task_block) { |name,pre,blk| rac.node_factory.new_dir(rac, name, pre, blk) } } last_task end end # class Directory class SourceNode def self.rant_gen(rac, ch, args) unless args.size == 1 rac.abort_at(ch, "SourceNode takes one argument.") end if block_given? rac.abort_at(ch, "SourceNode doesn't take a block.") end rac.prepare_task(args.first, nil, ch) { |name, pre, blk| rac.node_factory.new_source(rac, name, pre, blk) } end end class Rule # Generate a rule by installing an at_resolve hook for # +rac+. def self.rant_gen(rac, ch, args, &block) unless args.size == 1 rac.abort_at(ch, "Rule takes only one argument.") end rac.abort_at(ch, "Rule: block required.") unless block arg = args.first target = nil src_arg = nil if Symbol === arg target = ".#{arg}" elsif arg.respond_to? :to_str target = arg.to_str elsif Regexp === arg target = arg elsif Hash === arg && arg.size == 1 arg.each_pair { |target, src_arg| } src_arg = src_arg.to_str if src_arg.respond_to? :to_str target = target.to_str if target.respond_to? :to_str src_arg = ".#{src_arg}" if Symbol === src_arg target = ".#{target}" if Symbol === target else rac.abort_at(ch, "Rule argument " + "has to be a hash with one key-value pair.") end esc_target = nil target_rx = case target when String esc_target = Regexp.escape(target) /#{esc_target}$/ when Regexp target else rac.abort_at(ch, "rule target has " + "to be a string or regular expression") end src_proc = case src_arg when String, Array unless String === target rac.abort(ch, "rule target has to be " + "a string if source is a string") end if src_arg.kind_of? String lambda { |name| name.sub(/#{esc_target}$/, src_arg) } else lambda { |name| src_arg.collect { |s_src| s_src = ".#{s_src}" if Symbol === s_src name.sub(/#{esc_target}$/, s_src) } } end when Proc: src_arg when nil: lambda { |name| [] } else rac.abort_at(ch, "rule source has to be a " + "String, Array or Proc") end rac.resolve_hooks << (block.arity == 2 ? Hook : FileHook).new( rac, ch, target_rx, src_proc, block) nil end class Hook attr_accessor :target_rx def initialize(rant, ch, target_rx, src_proc, block) @rant = rant @ch = ch @target_rx = target_rx @src_proc = src_proc @block = block end def call(target, rel_project_dir) if @target_rx =~ target have_src = true src = @src_proc[target] if src.respond_to? :to_ary have_src = src.to_ary.all? { |s| have_src?(rel_project_dir, s) } else have_src = have_src?(rel_project_dir, src) end if have_src create_nodes(rel_project_dir, target, src) end end end alias [] call private def have_src?(rel_project_dir, name) return true unless @rant.rec_save_resolve(name, self, rel_project_dir).empty? test(?e, @rant.abs_path(rel_project_dir, name)) end def create_nodes(rel_project_dir, target, deps) @rant.goto_project_dir rel_project_dir case nodes = @block[target, deps] when Array: nodes when Node: [nodes] else @rant.abort_at(@ch, "Block has to " + "return Node or array of Nodes.") end end end class FileHook < Hook private def have_src?(rel_project_dir, name) test(?e, @rant.abs_path(rel_project_dir, name)) or @rant.rec_save_resolve(name, self, rel_project_dir ).any? { |t| t.file_target? } end def create_nodes(rel_project_dir, target, deps) @rant.goto_project_dir rel_project_dir t = @rant.file(:__caller__ => @ch, target => deps, &@block) [t] end end end # class Rule class Action def self.rant_gen(rac, ch, args, &block) case args.size when 0: unless (rac[:tasks] || rac[:stop_after_load]) yield end when 1: rx = args.first unless rx.kind_of? Regexp rac.abort_at(ch, "Action: argument has " + "to be a regular expression.") end rac.resolve_hooks << self.new(rac, block, rx) nil else rac.abort_at(ch, "Action: too many arguments.") end end def initialize(rant, block, rx) @rant = rant @subdir = @rant.current_subdir @block = block @rx = rx end def call(target, rel_project_dir) if target =~ @rx @rant.resolve_hooks.delete(self) @rant.goto_project_dir @subdir @block.call @rant.resolve(target, rel_project_dir) end end alias [] call end end # module Generators end # module Rant rant-0.5.8/lib/rant/cs_compiler.rb0000644000175000017500000001747610527253231016423 0ustar xavierxavier require 'rant/init' module Rant # An object extending this module acts as an # interface to a C# Compiler. class CsCompiler LIB_SYSTEM_XML = "System.Xml.dll" LIB_SYSTEM_DRAWING = "System.Drawing.dll" LIB_SYSTEM_FORMS = "System.Windows.Forms.dll" class << self # Get the short name for the compiler referenced by this path. def cs_compiler_name(path) case path when /csc(\.exe)?$/i "csc" when /cscc(\.exe)?$/i "cscc" when /mcs(\.exe)?$/i "mcs" else nil end end # Search for a C# compiler in PATH and some # usual locations. def look_for_cs_compiler csc_bin = nil if Env.on_windows? csc_bin = "csc" if Env.find_bin "csc" unless csc_bin csc_bin = look_for_csc end end csc_bin = "cscc" if !csc_bin && Env.find_bin("cscc") csc_bin = "mcs" if !csc_bin && Env.find_bin("mcs") csc_bin end # Searches for csc in some usual directories. # Ment to be used on windows only! def look_for_csc # Is there a way to get a list of all available # drives? ("C".."Z").each { |drive| ["WINDOWS", "WINNT"].each { |win_dir| frame_dir = drive + ':\\' + win_dir + '\Microsoft.NET\Framework' next unless test(?d,frame_dir) csc_pathes = [] Dir.entries(frame_dir).each { |e| vdir = File.join(frame_dir, e) if test(?d, vdir) csc_path = File.join(vdir, "csc.exe") if test(?e,csc_path) csc_pathes << csc_path end end } next if csc_pathes.empty? return csc_pathes.sort.last } } nil rescue nil end end # Short name for compiler, such as "csc", "cscc" or "mcs". attr_reader :csc_name # Descriptive name for compiler. attr_reader :long_name # Compiler path, or cmd on PATH # Look also at the #csc and #csc= methods. Most times they do # what you need. attr_accessor :csc_bin # Debug flag. attr_accessor :debug # Target filename. attr_accessor :out # Libraries to link angainst (usually dlls). attr_accessor :libs # Preprocessor defines. attr_reader :defines # Other args, could be options. # Initialized to an empty array. attr_accessor :misc_args # Hash with compiler specific arguments. attr_accessor :specific_args # Sourcefiles. attr_accessor :sources # Resources to embedd in assembly. attr_accessor :resources # Library link pathes. attr_reader :lib_link_pathes # Entry point for executable. attr_accessor :entry # Optimize, defaults to true attr_accessor :optimize # Enable compiler warnings, defaults to true attr_accessor :warnings def initialize(compiler_name=nil) self.csc_name = (compiler_name || "cscc") @long_name = "C# Compiler" @defines = [] @libs = [] @sources = nil @misc_args = [] @specific_args = { "cscc" => [], "csc" => [], "mcs" => [], } @resources = [] @debug = false @out = "a.out" @lib_link_pathes = [] @entry = nil @optimize = true @warnings = true @csc_bin = nil end # Command to invoke compiler. def csc #puts "#@csc_bin, #@csc_name" @csc_bin || @csc_name end # Set this to command to invoke your compiler. Contrary to # +cc_bin+, this also tries to determine which interface to # use for this compiler. Finally it sets +cc_bin+. def csc=(cmd) @csc_bin = cmd.dup name = self.class.cs_compiler_name(@csc_bin) @csc_name = name if name #puts "csc=() setting csc_bin to " + @csc_bin end def csc_name= new_name unless ["cscc", "csc", "mcs"].include?(new_name) raise "Unsupported C# compiler `#{new_name}'" end @csc_name = new_name @long_name = case @csc_name when "cscc": "DotGNU C# compiler" when "csc": "MS Visual.NET C# compiler" when "mcs": "Mono C# compiler" end end # Shortcut for +specific_args+. def sargs @specific_args end # Generate compilation command for executable. def cmd_exe send @csc_name + "_cmd_exe" end def cscc_cmd_exe # This generates the compilation command # for cscc. cc_cmd = csc.dup #puts "creating exe command for #{cc_cmd}" cc_cmd << " -e#{entry}" if entry cc_cmd << cc_cmd_args cc_cmd end def csc_cmd_exe # This generates the compilation command # for csc. cc_cmd = csc.dup # Use target:winexe only if not debugging, # because this will suppress a background console window. cc_cmd << " /target:winexe" unless debug cc_cmd << " /main:#{entry}" if entry cc_cmd << cc_cmd_args cc_cmd end def mcs_cmd_exe # Generate compilation command for mcs. cc_cmd = csc.dup cc_cmd << " -target:exe" cc_cmd << " -main:#{entry}" if entry cc_cmd << cc_cmd_args cc_cmd end # Generate command for DLL. def cmd_dll send @csc_name + "_cmd_dll" end def cscc_cmd_dll cc_cmd = csc.dup cc_cmd << " -shared" cc_cmd << cc_cmd_args cc_cmd end def csc_cmd_dll cc_cmd = csc.dup cc_cmd << " /target:library" cc_cmd << cc_cmd_args cc_cmd end def mcs_cmd_dll cc_cmd = csc.dup cc_cmd << " -target:library" cc_cmd << cc_cmd_args cc_cmd end # Generate command for object file. def cmd_object send @csc_name + "_cmd_object" end def cscc_cmd_object cc_cmd = csc.dup cc_cmd << " -c" cc_cmd << cc_cmd_args cc_cmd end def csc_cmd_object cc_cmd = csc.dup cc_cmd << " /target:module" cc_cmd << cc_cmd_args cc_cmd end def mcs_cmd_object cc_cmd = csc.dup cc_cmd << " -target:module" cc_cmd << cc_cmd_args cc_cmd end def to_s csc + "\n" + "Interface: " + csc_name end private def cc_cmd_args send @csc_name + "_cmd_args" end def cscc_cmd_args cc_args = "" cc_args << " -o #{out}" if out cc_args << " -g -DDEBUG" if debug defines.each { |p| cc_args << " -D#{p}" } cc_args << " -Wall" if warnings cc_args << " -O2" if optimize lib_link_pathes.each { |p| cc_args << " -L #{Env.shell_path(p)}" } libs.each { |p| cc_args << " -l #{Env.shell_path(p)}" } cc_args << " " << misc_args.join(' ') if misc_args sargs = specific_args["cscc"] cc_args << " " << sargs.join(' ') if sargs resources.each { |p| cc_args << " -fresources=#{Env.shell_path(p)}" } cc_args << " " << Rant::Sys.sp(sources) if sources cc_args end def csc_cmd_args cc_args = "" cc_args << " /out:#{Env.shell_path(out)}" if out cc_args << " /debug /d:DEBUG" if debug defines.each { |p| cc_args << " /d:#{p}" } cc_args << " /optimize" if optimize # TODO: cc_args << " -Wall" if warnings lib_link_pathes.each { |p| #TODO: cc_args << " -L #{p}" } libs.each { |p| cc_args << " /r:#{Env.shell_path(p)}" } cc_args << " " << misc_args.join(' ') if misc_args sargs = specific_args["csc"] cc_args << " " << sargs.join(' ') if sargs resources.each { |p| cc_args << " /res:#{Env.shell_path(p)}" } cc_args << " " << Rant::Sys.sp(sources) if sources cc_args end def mcs_cmd_args cc_args = "" cc_args << " -out:#{Env.shell_path(out)}" if out cc_args << " -debug -d:DEBUG" if debug defines.each { |p| cc_args << " -d:#{p}" } cc_args << " -optimize" if optimize # Warning level for mcs: highest 4, default 2 cc_args << " -warn:4" if warnings lib_link_pathes.each { |p| cc_args << " -L #{Env.shell_path(p)}" } if libs && !libs.empty? cc_args << " -r:" + (libs.collect { |p| Env.shell_path(p) }).join(',') end cc_args << " " << misc_args.join(' ') if misc_args sargs = specific_args["mcs"] cc_args << " " << sargs.join(' ') if sargs resources.each { |p| cc_args << " -resource:#{Env.shell_path(p)}" } cc_args << " " << Rant::Sys.sp(sources) if sources cc_args end end # class CsCompiler end # module Rant rant-0.5.8/lib/rant/filelist.rb0000644000175000017500000000030110527253231015712 0ustar xavierxavier # filelist.rb - Define Rant::FileList class. # # Copyright (C) 2005 Stefan Lang require 'rant/init' require 'rant/import/filelist/core' require 'rant/import/filelist/std' rant-0.5.8/lib/rant/import.rb0000644000175000017500000003352310527253231015425 0ustar xavierxavier # import.rb - Library for the rant-import command. # # Copyright (C) 2005 Stefan Lang require 'getoptlong' require 'rant/rantlib' module Rant class RantImportDoneException < RantDoneException end class RantImportAbortException < RantAbortException end # This class is the implementation for the rant-import command. # Usage similar to RantApp class. class RantImport include Rant::Console LIB_DIR = File.expand_path(File.dirname(__FILE__)) OPTIONS = [ [ "--help", "-h", GetoptLong::NO_ARGUMENT, "Print this help and exit." ], [ "--version", "-V", GetoptLong::NO_ARGUMENT, "Print version of rant-import and exit." ], [ "-v", GetoptLong::NO_ARGUMENT, "Deprecated. Use -V or --version instead." ], [ "--quiet", "-q", GetoptLong::NO_ARGUMENT, "Operate quiet." ], [ "--plugins", "-p", GetoptLong::REQUIRED_ARGUMENT, "Include PLUGINS (comma separated list)." ], [ "--imports", "-i", GetoptLong::REQUIRED_ARGUMENT, "Include IMPORTS (comma separated list)." ], [ "--force", GetoptLong::NO_ARGUMENT, "Force overwriting of output file." ], [ "--with-comments", GetoptLong::NO_ARGUMENT, "Include comments from Rant sources." ], [ "--reduce-whitespace", "-r",GetoptLong::NO_ARGUMENT, "Remove as much whitespace from Rant sources as possible." ], [ "--zip", "-z", GetoptLong::NO_ARGUMENT, "Compress created script." ], [ "--auto", "-a", GetoptLong::NO_ARGUMENT, "Automatically try to determine imports and plugins.\n" + "Warning: loads Rantfile!" ], [ "--rantfile", "-f", GetoptLong::REQUIRED_ARGUMENT, "Load RANTFILE. This also sets --auto!\n" + "May be given multiple times." ], ] class << self def run(first_arg=nil, *other_args) other_args = other_args.flatten args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args) new(args).run end end # Arguments, usually those given on commandline. attr :args # Plugins to import. attr :plugins # Imports to import ;) attr :imports # Filename where the monolithic rant script goes to. attr :mono_fn # Skip comments? Defaults to true. attr_accessor :skip_comments # Remove whitespace from Rant sources? Defaults to false. attr_accessor :reduce_whitespace # Try automatic determination of imports and plugins? # Defaults to false. attr_accessor :auto def initialize(*args) @args = args.flatten @msg_prefix = "rant-import: " @plugins = [] @imports = [] @mono_fn = nil @force = false @rantapp = nil @included_plugins = [] @included_imports = [] # contains all filenames as given to +require+ @included_files = [] @skip_comments = true @reduce_whitespace = false @auto = false @arg_rantfiles = [] @quiet = false @zip = false end def run process_args if @auto rac_args = %w(--stop-after-load) + @arg_rantfiles.collect { |rf| "-f#{rf}" } rac_args << "-v" unless @quiet @rantapp = RantApp.new unless @rantapp.run(rac_args) == 0 abort("Auto-determination of required code failed.") end @imports.concat(@rantapp.imports) @plugins.concat(@rantapp.plugins.map { |p| p.name }) end if File.exist?(@mono_fn) && !@force abort("#{@mono_fn} exists. Rant won't overwrite this file.", "Add --force to override this restriction.") end script = < # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. EOH script << mono_rant_core script << mono_imports script << mono_plugins script << < # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # This script just loads and runs #@zip_fn. require 'zlib' dir = File.expand_path(File.dirname(__FILE__)) path = "\#{dir}/\#{File.basename(__FILE__)}.gz" script = nil begin Zlib::GzipReader.open(path) { |gz| script = gz.read } rescue Errno::ENOENT $stderr.print < e abort(e.message) ensure ARGV.replace(old_argv) end def done raise RantImportDoneException end def help puts "rant-import [OPTIONS] [-i IMPORT1,IMPORT2,...] [-p PLUGIN1,PLUGIN2...] MONO_RANT" puts puts " Write a monolithic rant script to MONO_RANT." puts puts "Options are:" print option_listing(OPTIONS) done end def msg(*args) super unless @quiet end def abort(*text) err_msg(*text) unless text.empty? raise RantImportAbortException end # Get a monolithic rant script (as string) containing only the # Rant core. def mono_rant_core # Starting point is rant/rantlib.rb. rantlib_f = File.join(LIB_DIR, "rantlib.rb") begin rantlib = File.read rantlib_f rescue abort("When trying to read `#{rantlib_f}': #$!", "This file should contains the core of rant, so import is impossible.", "Please check your rant installation!") end @included_files << "rant/rantlib" resolve_requires rantlib end def mono_imports rs = "" @imports.each { |name| next if @included_imports.include? name lib_name = "import/#{name}" lib_fn = "#{lib_name}.rb" rfn = "rant/#{lib_name}" path = get_lib_rant_path lib_fn unless path abort("No such import - #{name}") end @included_imports << name.dup unless @included_files.include? rfn @included_files << rfn msg "Including import `#{name}'", path rs << resolve_requires(File.read(path)) end } rs end def mono_plugins rs = "" @plugins.each { |name| lc_name = name.downcase next if @included_plugins.include? lc_name plugin_name = "plugin/#{lc_name}" plugin_fn = "#{plugin_name}.rb" rfn = "rant/#{plugin_name}" path = get_lib_rant_path plugin_fn unless File.exist? path abort("No such plugin - #{name}") end @included_plugins << lc_name unless @included_files.include? rfn @included_files << rfn msg "Including plugin `#{lc_name}'", path rs << resolve_requires(File.read(path)) end } rs end # +script+ is a string. This method resolves requires of rant/ # code by directly inserting the code. def resolve_requires script rs = "" in_ml_comment = false script.each { |line| if in_ml_comment if line =~ /^=end/ in_ml_comment = false end next if @skip_comments end # skip shebang line next if line =~ /^#! ?(\/|\\)?\w/ # uncomment line if +uncomment+ directive given if line =~ /^\s*#.*#\s?rant-import:\s?uncomment\s*$/ line.sub!(/#/, '') end # skip line if +remove+ directive given next if line =~ /#\s?rant-import:\s?remove\s*$/ # skip pure comment lines next if line =~ /^\s*#/ if @skip_comments if line =~ /^=begin\s/ in_ml_comment = true next if @skip_comments end name = nil lib_file = nil if line =~ /\s*(require|load)\s+('|")rant\/([\w\/]+)(\.rb)?('|")/ # Rant library code name = $3 elsif line =~ /\s*(require|load)\s+('|")rant\/(import\/[\w\/]+)(\.rb)?('|")/ # some "import" code name = $3 elsif line =~ /\s*(require|load)\s+('|")([^\2]+)\2[^r]*rant-import/ # a require which is explicitely labelled with rant-import lib_file = $3 end if name rfn = "rant/#{name}" next if @included_files.include? rfn path = get_lib_rant_path "#{name}.rb" msg "Including `#{name}'", path @included_files << rfn rs << resolve_requires(File.read(path)) elsif lib_file next if @included_files.include? lib_file path = get_lib_path "#{lib_file}.rb" msg "Including `#{lib_file}'", path @included_files << lib_file rs << resolve_requires(File.read(path)) else line.sub!(/^\s+/, '') if @reduce_whitespace rs << line end } rs end private def get_lib_rant_path(fn) path = File.join(LIB_DIR, fn) return path if File.exist?(path) $:.each { |lib_dir| path = File.join(lib_dir, "rant", fn) return path if File.exist?(path) } nil end def get_lib_path(fn) $:.each { |lib_dir| path = File.join(lib_dir, fn) return path if File.exist?(path) } nil end # Takes a script text as argument and returns the same script # but without unnecessary `end module XY' statements. # # Example input: # # 1 module Rant # 2 # more code # 3 end # module Rant # 4 # 5 # 6 module Rant # 7 # more code # # gives: # # 1 module Rant # 2 # more code # 5 # 7 # more code # # Note:: The comment with the module name in line 3 of the # input is very important. def filter_reopen_module(script) loop { script, done = filter_reopen_module_onepass(script) break(script) if done } end # Returns true as second return value if at no useless # statement was removed. def filter_reopen_module_onepass(script) lines = [] buffer = [] identifier = nil # class/module name keyword = nil # `class' or `module' done = true script.split(/\n/).each { |line| if identifier if line.strip.empty? buffer << line elsif line =~ /^\s*#{keyword}\s+#{identifier}\s*$/ # replace buffer with one empty line lines << "" buffer.clear identifier = keyword = nil done = false else lines.concat buffer buffer.clear identifier = keyword = nil redo end elsif line =~ /\s*end\s*#\s*(module|class)\s+(\w+)\s*$/ keyword = $1 identifier = $2 buffer << line else lines << line end } [lines.join("\n") << "\n", done] end end # class RantImport end # module Rant rant-0.5.8/lib/rant/init.rb0000644000175000017500000001316410527254516015064 0ustar xavierxavier # init.rb - Define constants and methods required by all Rant code. # # Copyright (C) 2005 Stefan Lang require 'rbconfig' unless Process::Status.method_defined?(:success?) # new in 1.8.2 class Process::Status def success?; exitstatus == 0; end end end unless Regexp.respond_to? :union # new in 1.8.1 def Regexp.union(*patterns) # let's hope it comes close to ruby-1.8.1 and upwards... return /(?!)/ if patterns.empty? Regexp.new(patterns.join("|")) end end if RUBY_VERSION < "1.8.2" class Array undef_method :flatten, :flatten! def flatten cp = self.dup cp.flatten! cp end def flatten! res = [] flattened = false self.each { |e| if e.respond_to? :to_ary res.concat(e.to_ary) flattened = true else res << e end } if flattened replace(res) flatten! self end end end end class String def _rant_sub_ext(ext, new_ext = nil) if new_ext self.sub(/#{Regexp.escape ext}$/, new_ext) else self.sub(/(\.[^.]*$)|$/, ".#{ext}") end end end module Rant VERSION = '0.5.8' @__rant_no_value__ = Object.new.freeze def self.__rant_no_value__ @__rant_no_value__ end module Env OS = ::Config::CONFIG['target'] RUBY = ::Config::CONFIG['ruby_install_name'] RUBY_BINDIR = ::Config::CONFIG['bindir'] RUBY_EXE = File.join(RUBY_BINDIR, RUBY + ::Config::CONFIG["EXEEXT"]) @@zip_bin = false @@tar_bin = false if OS =~ /mswin/i def on_windows?; true; end else def on_windows?; false; end end def have_zip? if @@zip_bin == false @@zip_bin = find_bin "zip" end !@@zip_bin.nil? end def have_tar? if @@tar_bin == false @@tar_bin = find_bin "tar" end !@@tar_bin.nil? end # Get an array with all pathes in the PATH # environment variable. def pathes # Windows doesn't care about case in environment variables, # but the ENV hash does! path = ENV[on_windows? ? "Path" : "PATH"] return [] unless path path.split(on_windows? ? ";" : ":") end # Searches for bin_name on path and returns # an absolute path if successfull or nil # if an executable called bin_name couldn't be found. def find_bin bin_name if on_windows? bin_name_exe = nil if bin_name !~ /\.[^\.]{1,3}$/i bin_name_exe = bin_name + ".exe" end pathes.each { |dir| file = File.join(dir, bin_name) return file if test(?f, file) if bin_name_exe file = File.join(dir, bin_name_exe) return file if test(?f, file) end } else pathes.each { |dir| file = File.join(dir, bin_name) return file if test(?x, file) } end nil end # Add quotes to a path and replace File::Separators if necessary. def shell_path path # TODO: check for more characters when deciding wheter to use # quotes. if on_windows? path = path.tr("/", "\\") if path.include? ' ' '"' + path + '"' else path end else if path.include? ' ' "'" + path + "'" else path end end end extend self end # module Env module Sys # Returns a string that can be used as a valid path argument # on the shell respecting portability issues. def sp(arg) if arg.respond_to? :to_ary arg.to_ary.map{ |e| sp e }.join(' ') else _escaped_path arg end end # Escape special shell characters (currently only spaces). # Flattens arrays and returns always a single string. def escape(arg) if arg.respond_to? :to_ary arg.to_ary.map{ |e| escape e }.join(' ') else _escaped arg end end if Env.on_windows? def _escaped_path(path) _escaped(path.to_s.tr("/", "\\")) end def _escaped(arg) sarg = arg.to_s return sarg unless sarg.include?(" ") sarg << "\\" if sarg[-1].chr == "\\" "\"#{sarg}\"" end def regular_filename(fn) fn.to_str.tr("\\", "/").gsub(%r{/{2,}}, "/") end else def _escaped_path(path) path.to_s.gsub(/(?=\s)/, "\\") end alias _escaped _escaped_path def regular_filename(fn) fn.to_str.gsub(%r{/{2,}}, "/") end end private :_escaped_path private :_escaped # Split a path in all elements. def split_all(path) names = regular_filename(path).split(%r{/}) names[0] = "/" if names[0] && names[0].empty? names end extend self end # module Sys end # module Rant rant-0.5.8/lib/rant/init.rb~0000644000175000017500000001316410527254516015262 0ustar xavierxavier # init.rb - Define constants and methods required by all Rant code. # # Copyright (C) 2005 Stefan Lang require 'rbconfig' unless Process::Status.method_defined?(:success?) # new in 1.8.2 class Process::Status def success?; exitstatus == 0; end end end unless Regexp.respond_to? :union # new in 1.8.1 def Regexp.union(*patterns) # let's hope it comes close to ruby-1.8.1 and upwards... return /(?!)/ if patterns.empty? Regexp.new(patterns.join("|")) end end if RUBY_VERSION < "1.8.2" class Array undef_method :flatten, :flatten! def flatten cp = self.dup cp.flatten! cp end def flatten! res = [] flattened = false self.each { |e| if e.respond_to? :to_ary res.concat(e.to_ary) flattened = true else res << e end } if flattened replace(res) flatten! self end end end end class String def _rant_sub_ext(ext, new_ext = nil) if new_ext self.sub(/#{Regexp.escape ext}$/, new_ext) else self.sub(/(\.[^.]*$)|$/, ".#{ext}") end end end module Rant VERSION = '0.5.7' @__rant_no_value__ = Object.new.freeze def self.__rant_no_value__ @__rant_no_value__ end module Env OS = ::Config::CONFIG['target'] RUBY = ::Config::CONFIG['ruby_install_name'] RUBY_BINDIR = ::Config::CONFIG['bindir'] RUBY_EXE = File.join(RUBY_BINDIR, RUBY + ::Config::CONFIG["EXEEXT"]) @@zip_bin = false @@tar_bin = false if OS =~ /mswin/i def on_windows?; true; end else def on_windows?; false; end end def have_zip? if @@zip_bin == false @@zip_bin = find_bin "zip" end !@@zip_bin.nil? end def have_tar? if @@tar_bin == false @@tar_bin = find_bin "tar" end !@@tar_bin.nil? end # Get an array with all pathes in the PATH # environment variable. def pathes # Windows doesn't care about case in environment variables, # but the ENV hash does! path = ENV[on_windows? ? "Path" : "PATH"] return [] unless path path.split(on_windows? ? ";" : ":") end # Searches for bin_name on path and returns # an absolute path if successfull or nil # if an executable called bin_name couldn't be found. def find_bin bin_name if on_windows? bin_name_exe = nil if bin_name !~ /\.[^\.]{1,3}$/i bin_name_exe = bin_name + ".exe" end pathes.each { |dir| file = File.join(dir, bin_name) return file if test(?f, file) if bin_name_exe file = File.join(dir, bin_name_exe) return file if test(?f, file) end } else pathes.each { |dir| file = File.join(dir, bin_name) return file if test(?x, file) } end nil end # Add quotes to a path and replace File::Separators if necessary. def shell_path path # TODO: check for more characters when deciding wheter to use # quotes. if on_windows? path = path.tr("/", "\\") if path.include? ' ' '"' + path + '"' else path end else if path.include? ' ' "'" + path + "'" else path end end end extend self end # module Env module Sys # Returns a string that can be used as a valid path argument # on the shell respecting portability issues. def sp(arg) if arg.respond_to? :to_ary arg.to_ary.map{ |e| sp e }.join(' ') else _escaped_path arg end end # Escape special shell characters (currently only spaces). # Flattens arrays and returns always a single string. def escape(arg) if arg.respond_to? :to_ary arg.to_ary.map{ |e| escape e }.join(' ') else _escaped arg end end if Env.on_windows? def _escaped_path(path) _escaped(path.to_s.tr("/", "\\")) end def _escaped(arg) sarg = arg.to_s return sarg unless sarg.include?(" ") sarg << "\\" if sarg[-1].chr == "\\" "\"#{sarg}\"" end def regular_filename(fn) fn.to_str.tr("\\", "/").gsub(%r{/{2,}}, "/") end else def _escaped_path(path) path.to_s.gsub(/(?=\s)/, "\\") end alias _escaped _escaped_path def regular_filename(fn) fn.to_str.gsub(%r{/{2,}}, "/") end end private :_escaped_path private :_escaped # Split a path in all elements. def split_all(path) names = regular_filename(path).split(%r{/}) names[0] = "/" if names[0] && names[0].empty? names end extend self end # module Sys end # module Rant rant-0.5.8/lib/rant/metautils.rb0000644000175000017500000000736210527253231016124 0ustar xavierxavier # metautils.rb - Meta programming utils for Rant internals. # # Copyright (C) 2005 Stefan Lang module Rant module MetaUtils # Creates three accessor methods: # obj.attr_name:: Return value of instance variable # @attr_name # obj.attr_name = val:: Set value instance variable # @attr_name to val # obj.attr_name val:: same as above def rant_attr attr_name attr_name = valid_attr_name attr_name attr_writer attr_name module_eval <<-EOD def #{attr_name} val=Rant.__rant_no_value__ if val.equal? Rant.__rant_no_value__ @#{attr_name} else @#{attr_name} = val end end EOD nil end # Creates three accessor methods: # obj.attr_name?:: Return value, true or false # obj.attr_name:: Set attribute to true # obj.no_attr_name:: Set attribute to false def rant_flag attr_name attr_name = valid_attr_name attr_name module_eval <<-EOD def #{attr_name}? @#{attr_name} end def #{attr_name} @#{attr_name} = true end def no_#{attr_name} @#{attr_name} = false end EOD end # Creates accessor methods like #rant_attr for the attribute # attr_name. Additionally, values are converted with to_str # before assignment to instance variables happens. def string_attr attr_name attr_name = valid_attr_name attr_name module_eval <<-EOD def #{attr_name}=(val) if val.respond_to? :to_str @#{attr_name} = val.to_str else raise ArgumentError, "string (#to_str) value required", caller end end def #{attr_name} val=Rant.__rant_no_value__ if val.equal? Rant.__rant_no_value__ @#{attr_name} else self.__send__(:#{attr_name}=, val) end end EOD nil end def redirect_accessor(receiver, *attributes) redirect_reader(receiver, *attributes) redirect_writer(receiver, *attributes) end # Create attribute reader methods that redirect to the entity # given as first argument (e.g. an instance variable name). def redirect_reader(receiver, *attributes) attributes.each { |attr_name| attr_name = valid_attr_name attr_name module_eval <<-EOD def #{attr_name}; #{receiver}.#{attr_name}; end EOD } nil end # Create attribute writer methods that redirect to the entity # given as first argument (e.g. an instance variable name). def redirect_writer(receiver, *attributes) attributes.each { |attr_name| attr_name = valid_attr_name attr_name module_eval <<-EOD def #{attr_name}=(val); #{receiver}.#{attr_name}= val; end EOD } nil end def redirect_message(receiver, *messages) messages.each { |message| module_eval <<-EOD def #{message}(*args, &blk) # the first ; on the next line is needed # because of rant-import ;#{receiver}.#{message}(*args, &blk) end EOD } nil end # attr_name is converted to a string with #to_s and has to # match /^\w+$/. Returns attr_name.to_s. def valid_attr_name attr_name attr_name = attr_name.to_s attr_name =~ /^\w+\??$/ or raise ArgumentError, "argument has to match /^\w+\??$/", caller attr_name end end # module MetaUtils end # module Rant rant-0.5.8/lib/rant/node.rb0000644000175000017500000001225010527253231015032 0ustar xavierxavier # node.rb - Base of Rant nodes. # # Copyright (C) 2005 Stefan Lang module Rant class TaskFail < StandardError def initialize(task, orig, msg) @task = task @orig = orig @msg = msg end def exception self end def task @task end def tname @task ? @task.name : nil end # the exception which caused the task to fail def orig @orig end def msg @msg end end class Rantfile attr_reader :tasks, :path attr_accessor :project_subdir def initialize(path) @path = path or raise ArgumentError, "path required" @tasks = [] @project_subdir = nil end alias to_s path alias to_str path end # class Rantfile # Any +object+ is considered a _task_ if # Rant::Node === object is true. # # Most important classes including this module are the Rant::Task # class and the Rant::FileTask class. module Node INVOKE_OPT = {}.freeze T0 = Time.at(0).freeze # Name of the task, this is always a string. attr_reader :name # A reference to the Rant compiler this task belongs to. attr_reader :rac # Description for this task. attr_accessor :description # The rantfile this task was defined in. # Should be a Rant::Rantfile instance. attr_accessor :rantfile # The linenumber in rantfile where this task was defined. attr_accessor :line_number # The directory in which this task was defined, relative to # the projects root directory. attr_accessor :project_subdir def initialize @description = nil @rantfile = nil @line_number = nil @run = false @project_subdir = "" # success has one of three values: # nil no invoke # false invoked, but fail # true invoked and run successfully @success = nil end def reference_name sd = rac.current_subdir case sd when "": full_name when project_subdir: name else "@#{full_name}".sub(/^@#{Regexp.escape sd}\//, '') end end alias to_s reference_name alias to_rant_target name # Basically project_subdir/name # # The Rant compiler (or application) references tasks by their # full_name. def full_name sd = project_subdir sd.empty? ? name : File.join(sd, name) end def ch {:file => rantfile.to_str, :ln => line_number} end # Change current working directory to the directory this task # was defined in. # # Important for subclasses: Call this method always before # invoking code from Rantfiles (e.g. task action blocks). def goto_task_home @rac.goto_project_dir project_subdir end def file_target? false end # Task was run and didn't fail. def done? @success end def needed? invoke(:needed? => true) end # True during invoke. Used to encounter circular dependencies. def run? @run end # +opt+ is a Hash and shouldn't be modified. # All objects implementing the Rant::Node protocol should # know about the following +opt+ values: # :needed?:: # Just check if this task is needed. Should do the same # as calling Node#needed? # :force:: # Run task action even if needed? is false. # Returns true if task action was run. def invoke(opt = INVOKE_OPT) return circular_dep if run? @run = true begin return !done? if opt[:needed?] # we don't need to check for opt[:force] here # since a plain Node is run anyway. self.run if !done? @success = true ensure @run = false end end # Cause task to fail. Usually called from inside the block # given to +act+. def fail msg = nil, orig = nil raise TaskFail.new(self, orig, msg) end # Change pwd to task home directory and yield for each created # file/directory. # # Override in subclasses if your task instances create files. def each_target end def has_actions? defined? @block and @block end def dry_run text = "Executing #{name.dump}" text << " [NOOP]" unless has_actions? @rac.cmd_msg text action_descs.each { |ad| @rac.cmd_print " - " @rac.cmd_msg ad.sub(/\n$/, '').gsub(/\n/, "\n ") } end private def run goto_task_home return if @rac.running_task(self) return unless has_actions? @receiver.pre_run(self) if defined? @receiver and @receiver @block.arity == 0 ? @block.call : @block[self] if @block end def action_descs descs = [] if defined? @receiver and @receiver descs.concat(@receiver.pre_action_descs) end @block ? descs << action_block_desc : descs end def action_block_desc @block.inspect =~ /^#$/i fn, ln = $1, $2 "Ruby Proc at #{fn.sub(/^#{Regexp.escape @rac.rootdir}\//, '')}:#{ln}" end def circular_dep rac.warn_msg "Circular dependency on task `#{full_name}'." false end end # module Node end # module Rant rant-0.5.8/lib/rant/plugin_methods.rb0000644000175000017500000000224210527253231017126 0ustar xavierxavier require 'rant/rantlib' # This module defines all instance methods required for an Rant # plugin. Additionally, each plugin class has to define the class # method +plugin_create+. # # Include this module in your plugin class to ensure your plugin won't # break when Rant requires new methods. module Rant::PluginMethods # The type of your plugin as string. def rant_plugin_type "rant plugin" end # Please override this method. This is used as a name for your # plugin instance. def rant_plugin_name "rant plugin object" end # This is used for verification. Usually you don't want to change # this for your plugin :-) def rant_plugin? true end # Called immediately after registration. def rant_plugin_init end # Called before rant runs the first task. def rant_start end # Called when rant *successfully* processed all required tasks. def rant_done end # You should "shut down" your plugin as response to this method. def rant_plugin_stop end # Called immediately before the rant application return control to # the caller. def rant_quit end end # module Rant::PluginMethods rant-0.5.8/lib/rant/progress.rb0000644000175000017500000000152610527253231015755 0ustar xavierxavier # progress.rb - Simple progress bar features for Rant. # # Copyright (C) 2005 Stefan Lang module Rant class ProgressCountdown attr_reader :total, :current def initialize(total, rant=nil) @total = total @step = @total / 10 @fraction = 10 @rant = rant @current = 0 end def inc @current += 1 if @step > 0 and @current % @step == 0 @fraction -= 1 print_progress "#@fraction " if @fraction >= 0 end end private def print_progress(text) if @rant @rant.cmd_print(text) else print text $stdout.flush end end end # class ProgressCountdown end # module Rant rant-0.5.8/lib/rant/rantlib.rb0000644000175000017500000011435210527253231015546 0ustar xavierxavier # rantlib.rb - The core of Rant. # # Copyright (C) 2005 Stefan Lang # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. require 'getoptlong' require 'rant/init' require 'rant/rantvar' require 'rant/rantsys' require 'rant/node' require 'rant/import/nodes/default' # could be optimized away require 'rant/coregen' # There is one problem with executing Rantfiles in a special context: # In the top-level execution environment, there are some methods # available which are not available to all objects. One example is the # +include+ method. # # To (at least partially) solve this problem, we capture the `main' # object here and delegate methods from RantContext#method_missing to # this object. Rant::MAIN_OBJECT = self class String alias sub_ext _rant_sub_ext def to_rant_target self end end # This module and its methods don't belong to Rant's public API. # For (Rant) internal usage only! module Rant::Lib # Parses one string (elem) as it occurs in the array # which is returned by caller. # E.g.: # p parse_caller_elem "/usr/local/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'" # prints: # {:ln=>52, :file=>"/usr/local/lib/ruby/1.8/irb/workspace.rb"} # # Note: This method splits on the pattern :(\d+)(:|$), # assuming anything before is the filename. def parse_caller_elem(elem) return { :file => "", :ln => 0 } unless elem if elem =~ /^(.+):(\d+)(?::|$)/ { :file => $1, :ln => $2.to_i } else # should never occur $stderr.puts "parse_caller_elem: #{elem.inspect}" { :file => elem, :ln => 0 } end #parts = elem.split(":") #{ :file => parts[0], :ln => parts[1].to_i } end module_function :parse_caller_elem end # module Lib module Rant::Console RANT_PREFIX = "rant: " ERROR_PREFIX = "[ERROR] " WARN_PREFIX = "[WARNING] " def msg_prefix if defined? @msg_prefix and @msg_prefix @msg_prefix else RANT_PREFIX end end def msg(*text) pre = msg_prefix $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}" end def vmsg(importance, *text) msg(*text) if verbose >= importance end def err_msg(*text) pre = msg_prefix + ERROR_PREFIX $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}" end def warn_msg(*text) pre = msg_prefix + WARN_PREFIX $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}" end def ask_yes_no text $stderr.print msg_prefix + text + " [y|n] " case $stdin.readline when /y|yes/i: true when /n|no/i: false else $stderr.puts(' ' * msg_prefix.length + "Please answer with `yes' or `no'") ask_yes_no text end end def prompt text $stderr.print msg_prefix + text input = $stdin.readline input ? input.chomp : input end def option_listing opts rs = "" opts.each { |lopt, *opt_a| if opt_a.size == 2 # no short option mode, desc = opt_a else sopt, mode, desc = opt_a end next unless desc # "private" option optstr = "" arg = nil if mode != GetoptLong::NO_ARGUMENT if desc =~ /(\b[A-Z_]{2,}\b)/ arg = $1 end end if lopt optstr << lopt if arg optstr << " " << arg end optstr = optstr.ljust(30) end if sopt optstr << " " unless optstr.empty? optstr << sopt if arg optstr << " " << arg end end rs << " #{optstr}\n" rs << " #{desc.split("\n").join("\n ")}\n" } rs end extend self end # module Rant::Console # The methods in this module are the public interface to Rant that can # be used in Rantfiles. module RantContext include Rant::Generators Env = Rant::Env FileList = Rant::FileList # Define a basic task. def task(targ, &block) rant.task(targ, &block) end # Define a file task. def file(targ, &block) rant.file(targ, &block) end # Add code and/or prerequisites to existing task. def enhance(targ, &block) rant.enhance(targ, &block) end def desc(*args) rant.desc(*args) end def gen(*args, &block) rant.gen(*args, &block) end def import(*args, &block) rant.import(*args, &block) end def plugin(*args, &block) rant.plugin(*args, &block) end # Look in the subdirectories, given by args, # for rantfiles. def subdirs(*args) rant.subdirs(*args) end def source(opt, rantfile = nil) rant.source(opt, rantfile) end def sys(*args, &block) rant.sys(*args, &block) end def var(*args, &block) rant.var(*args, &block) end def make(*args, &block) rant.make(*args, &block) end =begin # +rac+ stands for "rant compiler" def rac ch = Rant::Lib.parse_caller_elem caller[0] rant.warn_msg(rant.pos_text(ch[:file], ch[:ln]), "Method `rac' is deprecated. Use `rant' instead.") rant end =end end # module RantContext class RantAppContext include RantContext def initialize(app) @__rant__ = app end def rant @__rant__ end def method_missing(sym, *args) # See the documentation for Rant::MAIN_OBJECT why we're doing # this... # Note also that the +send+ method also invokes private # methods, this is very important for our intent. Rant::MAIN_OBJECT.send(sym, *args) rescue NoMethodError raise NameError, "NameError: undefined local " + "variable or method `#{sym}' for main:Object", caller end end module Rant @__rant__ = nil class << self # Run a new rant application in the current working directory. # This has the same effect as running +rant+ from the # commandline. You can give arguments as you would give them # on the commandline. If no argument is given, ARGV will be # used. # # This method returns 0 if the rant application was # successfull and 1 on failure. So if you need your own rant # startscript, it could look like: # # exit Rant.run # # This runs rant in the current directory, using the arguments # given to your script and the exit code as suggested by the # rant application. # # Or if you want rant to always be quiet with this script, # use: # # exit Rant.run("--quiet", ARGV) # # Of course, you can invoke rant directly at the bottom of # your rantfile, so you can run it directly with ruby. def run(first_arg=nil, *other_args) other_args = other_args.flatten args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args) if rant && !rant.run? rant.run(args.flatten) else @__rant__ = Rant::RantApp.new rant.run(args) end end def rant @__rant__ end end end # module Rant class Rant::RantApp include Rant::Console class AutoLoadNodeFactory def initialize(rant) @rant = rant end def method_missing(sym, *args, &block) @rant.import "nodes/default" @rant.node_factory.send(sym, *args, &block) end end # Important: We try to synchronize all tasks referenced indirectly # by @rantfiles with the task hash @tasks. The task hash is # intended for fast task lookup per task name. # # All tasks are registered to the system by the +prepare_task+ # method. # The RantApp class has no own state. OPTIONS = [ [ "--help", "-h", GetoptLong::NO_ARGUMENT, "Print this help and exit." ], [ "--version", "-V", GetoptLong::NO_ARGUMENT, "Print version of Rant and exit." ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT, "Print more messages to stderr." ], [ "--quiet", "-q", GetoptLong::NO_ARGUMENT, "Don't print commands." ], [ "--err-commands", GetoptLong::NO_ARGUMENT, "Print failed commands and their exit status." ], [ "--directory","-C", GetoptLong::REQUIRED_ARGUMENT, "Run rant in DIRECTORY." ], [ "--cd-parent","-c", GetoptLong::NO_ARGUMENT, "Run rant in parent directory with Rantfile." ], [ "--look-up", "-u", GetoptLong::NO_ARGUMENT, "Look in parent directories for root Rantfile." ], [ "--rantfile", "-f", GetoptLong::REQUIRED_ARGUMENT, "Process RANTFILE instead of standard rantfiles.\n" + "Multiple files may be specified with this option." ], [ "--force-run","-a", GetoptLong::REQUIRED_ARGUMENT, "Force rebuild of TARGET and all dependencies." ], [ "--dry-run", "-n", GetoptLong::NO_ARGUMENT, "Print info instead of actually executing actions." ], [ "--tasks", "-T", GetoptLong::NO_ARGUMENT, "Show a list of all described tasks and exit." ], # "private" options intended for debugging, testing and # internal use. A private option is distuingished from others # by having +nil+ as description! [ "--import", "-i", GetoptLong::REQUIRED_ARGUMENT, nil ], [ "--stop-after-load", GetoptLong::NO_ARGUMENT, nil ], # Print caller to $stderr on abort. [ "--trace-abort", GetoptLong::NO_ARGUMENT, nil ], ] # Reference project's root directory in task names by preceding # them with this character. ROOT_DIR_ID = "@" ESCAPE_ID = "\\" # Arguments, usually those given on commandline. attr_reader :args # A list of all Rantfiles used by this app. attr_reader :rantfiles # A list of target names to be forced (run even # if not required). Each of these targets will be removed # from this list after the first run. # # Forced targets will be run before other targets. attr_reader :force_targets # A list of all registered plugins. attr_reader :plugins # The context in which Rantfiles are loaded. RantContext methods # may be called through this object (e.g. from plugins). attr_reader :context alias cx context # A hash with all tasks. For fast task lookup use this hash with # the taskname as key. # # See also: #resolve, #make attr_reader :tasks # A list of all imports (code loaded with +import+). attr_reader :imports # Current subdirectory relative to project's root directory # (#rootdir). attr_reader :current_subdir # List of proc objects used to automatically create required # tasks. (Especially used for Rules.) # # Note: Might change before 1.0 attr_reader :resolve_hooks # Root directory of project. Will be initialized to working # directory in #initialize. This is always an absolute path # beginning with a / and not ending in a slash (unless # rootdir is /). attr_reader :rootdir attr_accessor :node_factory def initialize @args = [] # Rantfiles will be loaded in the context of this object. @context = RantAppContext.new(self) @sys = ::Rant::SysObject.new(self) @rantfiles = [] @tasks = {} @opts = { :verbose => 0, :quiet => false, } @rootdir = Dir.pwd # root directory of project @arg_rantfiles = [] # rantfiles given in args @arg_targets = [] # targets given in args @force_targets = [] # targets given with -a option @run = false # run method was called at least once @done = false # run method was successful @plugins = [] @var = Rant::RantVar::Space.new @var.query :ignore, :AutoList, [] @imports = [] @task_desc = nil @last_build_subdir = "" @current_subdir = "" @resolve_hooks = [] @node_factory = AutoLoadNodeFactory.new(self) end def [](opt) @opts[opt] end def []=(opt, val) @opts[opt] = val end ### support for subdirectories ################################### def expand_path(subdir, path) case path when nil: subdir.dup when "": subdir.dup when /^@/: path.sub(/^@/, '') else path = path.sub(/^\\(?=@)/, '') if subdir.empty? # we are in project's root directory path else File.join(subdir, path) end end end def resolve_root_ref(path) return File.join(@rootdir, path[1..-1]) if path =~ /^@/ path.sub(/^\\(?=@)/, '') end # Returns an absolute path. If path resolves to a directory this # method ensures that the returned absolute path doesn't end in a # slash. def project_to_fs_path(path) sub = expand_path(@current_subdir, path) sub.empty? ? @rootdir : File.join(@rootdir, sub) end def abs_path(subdir, fn) return fn if Rant::Sys.absolute_path?(fn) path = File.join(@rootdir, subdir, fn) path.gsub!(%r{/+}, "/") path.sub!(%r{/$}, "") if path.length > 1 path end def goto(dir) goto_project_dir(expand_path(@current_subdir, dir)) end # +dir+ is a path relative to +rootdir+. It has to be a "clean" # path string, i.e. it mustn't start with ./, contain any # .. parent reference and it mustn't have a trailing # slash. # # To go to the root directory, dir has to be an empty string, # which is the default value. def goto_project_dir(dir='') @current_subdir = dir abs_path = @current_subdir.empty? ? @rootdir : File.join(@rootdir, @current_subdir) unless Dir.pwd == abs_path Dir.chdir abs_path vmsg 1, "in #{abs_path}" end end ################################################################## def run? @run end def done? @done end # Run this Rant application with the given arguments. The process # working directory after this method returns, will be the same as # before invocation. # # Returns 0 on success and 1 on failure. def run(*args) @run = true @args.concat(args.flatten) # remind pwd orig_pwd = @rootdir = Dir.pwd # Process commandline. process_args Dir.chdir(@rootdir) rescue abort $!.message # read rantfiles, might change @rootdir and Dir.pwd load_rantfiles raise Rant::RantDoneException if @opts[:stop_after_load] # Notify plugins before running tasks @plugins.each { |plugin| plugin.rant_start } if @opts[:tasks] show_descriptions raise Rant::RantDoneException end # run tasks run_tasks raise Rant::RantDoneException rescue Rant::RantDoneException @done = true # Notify plugins @plugins.each { |plugin| plugin.rant_done } return 0 rescue Rant::RantAbortException $stderr.puts "rant aborted!" return 1 rescue Exception => e ch = get_ch_from_backtrace(e.backtrace) if ch && !@opts[:trace_abort] err_msg(pos_text(ch[:file], ch[:ln]), e.message) else err_msg e.message, e.backtrace[0..4] end $stderr.puts "rant aborted!" return 1 ensure # TODO: exception handling! Dir.chdir @rootdir if test ?d, @rootdir hooks = var._get("__at_return__") hooks.each { |hook| hook.call } if hooks @plugins.each { |plugin| plugin.rant_plugin_stop } @plugins.each { |plugin| plugin.rant_quit } # restore pwd Dir.chdir orig_pwd end ###### methods accessible through RantContext #################### def desc(*args) if args.empty? || (args.size == 1 && args.first.nil?) @task_desc = nil else @task_desc = args.join("\n") end end def task(targ, &block) prepare_task(targ, block) { |name,pre,blk| @node_factory.new_task(self, name, pre, blk) } end def file(targ, &block) prepare_task(targ, block) { |name,pre,blk| @node_factory.new_file(self, name, pre, blk) } end def gen(*args, &block) # retrieve caller info ch = Rant::Lib::parse_caller_elem(caller[1]) # validate args generator = args.shift unless generator.respond_to? :rant_gen abort_at(ch, "gen: First argument has to be a task-generator.") end # ask generator to produce a task for this application generator.rant_gen(self, ch, args, &block) end # Currently ignores block. def import(*args, &block) ch = Rant::Lib::parse_caller_elem(caller[1]) if block warn_msg pos_text(ch[:file], ch[:ln]), "import: ignoring block" end args.flatten.each { |arg| unless String === arg abort_at(ch, "import: only strings allowed as arguments") end unless @imports.include? arg unless Rant::CODE_IMPORTS.include? arg begin vmsg 2, "import #{arg}" require "rant/import/#{arg}" rescue LoadError => e abort_at(ch, "No such import - #{arg}") end Rant::CODE_IMPORTS << arg.dup end init_msg = "init_import_#{arg.gsub(/[^\w]/, '__')}" Rant.send init_msg, self if Rant.respond_to? init_msg @imports << arg.dup end } end def plugin(*args, &block) # retrieve caller info clr = caller[1] ch = Rant::Lib::parse_caller_elem(clr) name = nil pre = [] ln = ch[:ln] || 0 file = ch[:file] pl_name = args.shift pl_name = pl_name.to_str if pl_name.respond_to? :to_str pl_name = pl_name.to_s if pl_name.is_a? Symbol unless pl_name.is_a? String abort(pos_text(file, ln), "Plugin name has to be a string or symbol.") end lc_pl_name = pl_name.downcase import_name = "plugin/#{lc_pl_name}" unless Rant::CODE_IMPORTS.include? import_name begin require "rant/plugin/#{lc_pl_name}" Rant::CODE_IMPORTS << import_name rescue LoadError abort(pos_text(file, ln), "no such plugin library -- #{lc_pl_name}") end end pl_class = nil begin pl_class = ::Rant::Plugin.const_get(pl_name) rescue NameError, ArgumentError abort(pos_text(file, ln), "no such plugin -- #{pl_name}") end plugin = pl_class.rant_plugin_new(self, ch, *args, &block) # TODO: check for rant_plugin? @plugins << plugin vmsg 2, "Plugin `#{plugin.rant_plugin_name}' registered." plugin.rant_plugin_init # return plugin instance plugin end # Add block and prerequisites to the task specified by the # name given as only key in targ. # If there is no task with the given name, generate a warning # and a new file task. def enhance(targ, &block) prepare_task(targ, block) { |name,pre,blk| t = resolve(name).last if t unless t.respond_to? :enhance abort("Can't enhance task `#{name}'") end t.enhance(pre, &blk) # Important: return from method, don't break to # prepare_task which would add task t again return t end warn_msg "enhance \"#{name}\": no such task", "Generating a new file task with the given name." @node_factory.new_file(self, name, pre, blk) } end # Returns the value of the last expression executed in +rantfile+. def source(opt, rantfile = nil) unless rantfile rantfile = opt opt = nil end make_rf = opt != :n && opt != :now rf, is_new = rantfile_for_path(rantfile) return false unless is_new make rantfile if make_rf unless File.exist? rf.path abort("source: No such file -- #{rantfile}") end load_file rf end # Search the given directories for Rantfiles. def subdirs(*args) args.flatten! ch = Rant::Lib::parse_caller_elem(caller[1]) args.each { |arg| if arg.respond_to? :to_str arg = arg.to_str else abort_at(ch, "subdirs: arguments must be strings") end loaded = false prev_subdir = @current_subdir begin #puts "* subdir *", # " rootdir: #{rootdir}", # " current subdir: #@current_subdir", # " pwd: #{Dir.pwd}", # " arg: #{arg}" goto arg if test(?f, Rant::SUB_RANTFILE) path = Rant::SUB_RANTFILE else path = rantfile_in_dir end if path if defined? @initial_subdir and @initial_subdir == @current_subdir rf, is_new = rantfile_for_path(path, false) @rantfiles.unshift rf if is_new else rf, is_new = rantfile_for_path(path) end load_file rf if is_new elsif !@opts[:no_warn_subdir] warn_msg(pos_text(ch[:file], ch[:ln]), "subdirs: No Rantfile in subdir `#{arg}'.") end ensure #puts " going back to project dir: #{prev_subdir}" goto_project_dir prev_subdir end } rescue SystemCallError => e abort_at(ch, "subdirs: " + e.message) end def sys(*args, &block) args.empty? ? @sys : @sys.sh(*args, &block) end # The [] and []= operators may be used to set/get values from this # object (like a hash). It is intended to let the different # modules, plugins and tasks to communicate with each other. def var(*args, &block) args.empty? ? @var : @var.query(*args, &block) end ################################################################## # Pop (remove and return) current pending task description. def pop_desc td = @task_desc @task_desc = nil td end # Prints msg as error message and raises an RantAbortException. def abort(*msg) err_msg(msg) unless msg.empty? $stderr.puts caller if @opts[:trace_abort] raise Rant::RantAbortException end def abort_at(ch, *msg) err_msg(pos_text(ch[:file], ch[:ln]), msg) $stderr.puts caller if @opts[:trace_abort] raise Rant::RantAbortException end def show_help puts "rant [-f Rantfile] [Options] [targets]" puts puts "Options are:" print option_listing(OPTIONS) end def show_descriptions tlist = select_tasks { |t| t.description } # +target_list+ aborts if no task defined, so we can be sure # that +default+ is not nil def_target = target_list.first if tlist.empty? puts "rant # => " + list_task_names( resolve(def_target)).join(', ') msg "No described tasks." return end prefix = "rant " infix = " # " name_length = (tlist.map{ |t| t.to_s.length } << 7).max cmd_length = prefix.length + name_length unless tlist.first.to_s == def_target defaults = list_task_names( resolve(def_target)).join(', ') puts "#{prefix}#{' ' * name_length}#{infix}=> #{defaults}" end tlist.each { |t| print(prefix + t.to_s.ljust(name_length) + infix) dt = t.description.sub(/\s+$/, "") puts dt.gsub(/\n/, "\n" + ' ' * cmd_length + infix + " ") } true end def list_task_names(*tasks) rsl = [] tasks.flatten.each { |t| if t.respond_to?(:has_actions?) && t.has_actions? rsl << t elsif t.respond_to? :prerequisites if t.prerequisites.empty? rsl << t else t.prerequisites.each { |pre| rsl.concat(list_task_names( resolve(pre, t.project_subdir))) } end else rsl << t end } rsl end private :list_task_names # This is actually an integer indicating the verbosity level. # Usual values range from 0 to 3. def verbose @opts[:verbose] end def quiet? @opts[:quiet] end def pos_text(file, ln) t = "in file `#{file}'" t << ", line #{ln}" if ln && ln > 0 t << ": " end # Print a command message as would be done from a call to a # sys method. def cmd_msg(cmd) puts cmd unless quiet? end def cmd_print(text) print text unless quiet? $stdout.flush end # All targets given on commandline, including those given # with the -a option. The list will be in processing order. def cmd_targets @force_targets + @arg_targets end def running_task(task) if @current_subdir != @last_build_subdir cmd_msg "(in #{@current_subdir.empty? ? @rootdir : @current_subdir})" @last_build_subdir = @current_subdir end # TODO: model feels sick... this functionality should # be implemented in Node if @opts[:dry_run] task.dry_run true end end private def have_any_task? !@tasks.empty? end def target_list if !have_any_task? && @resolve_hooks.empty? abort("No tasks defined for this rant application!") end # Target selection strategy: # Run tasks specified on commandline, if not given: # run default task, if not given: # run first defined task. target_list = @force_targets + @arg_targets # The target list is a list of strings, not node objects! if target_list.empty? def_tasks = resolve "default" unless def_tasks.empty? target_list << "default" else @rantfiles.each { |f| first = f.tasks.first if first target_list << first.reference_name break end } end end target_list end # If this method returns (i.e. no exception was risen), # current_subdir is the same as before invocation. def run_tasks # Now, run all specified tasks in all rantfiles, # rantfiles in reverse order. target_list.each { |target| # build ensures that current_subdir is the same before # and after invocation if build(target) == 0 abort("Don't know how to make `#{target}'.") end } end def make(target, *args, &block) ch = nil if target.respond_to? :to_hash targ = target.to_hash ch = Rant::Lib.parse_caller_elem(caller[1]) abort_at(ch, "make: too many arguments") unless args.empty? tn = nil prepare_task(targ, block, ch) { |name,pre,blk| tn = name @node_factory.new_file(self, name, pre, blk) } build(tn) elsif target.respond_to? :to_rant_target rt = target.to_rant_target opt = args.shift unless args.empty? ch ||= Rant::Lib.parse_caller_elem(caller[1]) abort_at(ch, "make: too many arguments") end if block # create a file task ch ||= Rant::Lib.parse_caller_elem(caller[1]) prepare_task(rt, block, ch) { |name,pre,blk| @node_factory.new_file(self, name, pre, blk) } build(rt) else build(rt, opt||{}) end elsif target.respond_to? :rant_gen ch = Rant::Lib.parse_caller_elem(caller[1]) rv = target.rant_gen(self, ch, args, &block) unless rv.respond_to? :to_rant_target abort_at(ch, "make: invalid generator return value") end build(rv.to_rant_target) rv else ch = Rant::Lib.parse_caller_elem(caller[1]) abort_at(ch, "make: generator or target as first argument required.") end end public :make # Invoke all tasks necessary to build +target+. Returns the number # of tasks invoked. def build(target, opt = {}) opt[:force] = true if @force_targets.delete(target) # Currently either the whole application has to by run in # dry-run mode or nothing. opt[:dry_run] = @opts[:dry_run] matching_tasks = 0 old_subdir = @current_subdir old_pwd = Dir.pwd resolve(target).each { |t| unless opt[:type] == :file && !t.file_target? matching_tasks += 1 begin t.invoke(opt) rescue Rant::TaskFail => e err_task_fail(e) abort end end } @current_subdir = old_subdir Dir.chdir old_pwd matching_tasks end public :build # Currently always returns an array (which might actually be an # empty array, but never nil). def resolve(task_name, rel_project_dir = @current_subdir) # alternative implementation: # rec_save_resolve(task_name, nil, rel_project_dir) s = @tasks[expand_path(rel_project_dir, task_name)] case s when nil @resolve_hooks.each { |s| # Note: will probably change to get more params s = s[task_name, rel_project_dir] #if s # puts s.size # t = s.first # puts t.full_name # puts t.name # puts t.deps #end return s if s } [] when Rant::Node: [s] else # assuming list of tasks s end end public :resolve def rec_save_resolve(task_name, excl_hook, rel_project_dir = @current_subdir) s = @tasks[expand_path(rel_project_dir, task_name)] case s when nil @resolve_hooks.each { |s| next if s == excl_hook s = s[task_name, rel_project_dir] return s if s } [] when Rant::Node: [s] else s end end public :rec_save_resolve # This hook will be invoked when no matching task is found for a # target. It may create one or more tasks for the target, which is # given as argument, on the fly and return an array of the created # tasks or nil. def at_resolve(&block) @resolve_hooks << block if block end public :at_resolve # block will be called before this rant returns from #run # pwd will be the projects root directory def at_return(&block) hooks = var._get("__at_return__") if hooks hooks << block else var._set("__at_return__", [block]) end end public :at_return # Returns a list with all tasks for which yield # returns true. def select_tasks selection = [] @rantfiles.each { |rf| rf.tasks.each { |t| selection << t if yield t } } selection end public :select_tasks def load_rantfiles # Take care: When rant isn't invoked from commandline, # some "rant code" could already have run! # We run the default Rantfiles only if no tasks where # already defined and no Rantfile was given in args. unless @arg_rantfiles.empty? @arg_rantfiles.each { |fn| if test(?f, fn) rf, is_new = rantfile_for_path(fn) load_file rf if is_new else abort "No such file -- #{fn}" end } return end return if have_any_task? # look for standard Rantfile in working directory fn = rantfile_in_dir if @opts[:cd_parent] # search for Rantfile in parent directories old_root = @rootdir until fn or @rootdir == "/" @rootdir = File.dirname(@rootdir) fn = rantfile_in_dir(@rootdir) end if @rootdir != old_root and fn Dir.chdir @rootdir cmd_msg "(in #@rootdir)" end end if fn rf, is_new = rantfile_for_path(fn) load_file rf if is_new return end have_sub_rantfile = test(?f, Rant::SUB_RANTFILE) if have_sub_rantfile || @opts[:look_up] # search for "root" Rantfile in parent directories, treat # current working directory as project subdirectory cur_dir = Dir.pwd until cur_dir == "/" cur_dir = File.dirname(cur_dir) Dir.chdir cur_dir fn = rantfile_in_dir if fn @initial_subdir = @rootdir.sub( /^#{Regexp.escape cur_dir}\//, '') # adjust rootdir @rootdir = cur_dir cmd_msg "(root is #@rootdir, in #@initial_subdir)" @last_build_subdir = @initial_subdir rf, is_new = rantfile_for_path(fn) load_file rf if is_new goto_project_dir @initial_subdir # ensure to read sub.rant in initial subdir even # if it wasn't mentioned with +subdirs+. if have_sub_rantfile rf, is_new = rantfile_for_path( Rant::SUB_RANTFILE, false) if is_new @rantfiles.unshift rf load_file rf end end break end end end if @rantfiles.empty? abort("No Rantfile found, looking for:", Rant::RANTFILES.join(", ")) end end # Returns the value of the last expression executed in +rantfile+. # +rantfile+ has to be an Rant::Rantfile instance. def load_file(rantfile) vmsg 1, "source #{rantfile}" @context.instance_eval(File.read(rantfile), rantfile) end private :load_file # Get path to Rantfile in +dir+ or nil if dir doesn't contain an # Rantfile. # # If dir is nil, look in current directory. def rantfile_in_dir(dir=nil) ::Rant::RANTFILES.each { |rfn| path = dir ? File.join(dir, rfn) : rfn return path if test ?f, path } nil end def process_args # WARNING: we currently have to fool getoptlong, # by temporary changing ARGV! # This could cause problems (e.g. multithreading). old_argv = ARGV.dup ARGV.replace(@args.dup) cmd_opts = GetoptLong.new(*OPTIONS.collect { |lst| lst[0..-2] }) cmd_opts.quiet = true cmd_opts.each { |opt, value| case opt when "--verbose": @opts[:verbose] += 1 when "--version" puts "rant #{Rant::VERSION}" raise Rant::RantDoneException when "--help" show_help raise Rant::RantDoneException when "--directory" @rootdir = File.expand_path(value) when "--rantfile" @arg_rantfiles << value when "--force-run" @force_targets << value when "--import" import value else # simple switch @opts[opt.sub(/^--/, '').tr('-', "_").to_sym] = true end } rescue GetoptLong::Error => e abort(e.message) ensure rem_args = ARGV.dup ARGV.replace(old_argv) rem_args.each { |ra| if ra =~ /(^[^=]+)=([^=]+)$/ vmsg 2, "var: #$1=#$2" @var[$1] = $2 else @arg_targets << ra end } end # Every task has to be registered with this method. def prepare_task(targ, block, clr = caller[2]) #STDERR.puts "prepare task (#@current_subdir):\n #{targ.inspect}" # Allow override of caller, useful for plugins and libraries # that define tasks. if targ.is_a? Hash targ.reject! { |k, v| clr = v if k == :__caller__ } end ch = Hash === clr ? clr : Rant::Lib::parse_caller_elem(clr) name, pre = normalize_task_arg(targ, ch) file, is_new = rantfile_for_path(ch[:file]) nt = yield(name, pre, block) nt.rantfile = file #nt.project_subdir = file.project_subdir nt.project_subdir = @current_subdir nt.line_number = ch[:ln] nt.description = @task_desc @task_desc = nil file.tasks << nt hash_task nt nt end public :prepare_task def hash_task(task) n = task.full_name #STDERR.puts "hash_task: `#{n}'" et = @tasks[n] case et when nil @tasks[n] = task when Rant::Node mt = [et, task] @tasks[n] = mt else # assuming list of tasks et << task end end # Tries to extract task name and prerequisites from the typical # argument to the +task+ command. +targ+ should be one of String, # Symbol or Hash. ch is the caller (hash with the elements :file # and :ln) and is used for error reporting and debugging. # # Returns two values, the first is a string which is the task name # and the second is an array with the prerequisites. def normalize_task_arg(targ, ch) name = nil pre = [] # process and validate targ if targ.is_a? Hash if targ.empty? abort_at(ch, "Empty hash as task argument, " + "task name required.") end if targ.size > 1 abort_at(ch, "Too many hash elements, " + "should only be one.") end targ.each_pair { |k,v| name = normalize_task_name(k, ch) pre = v } unless ::Rant::FileList === pre if pre.respond_to? :to_ary pre = pre.to_ary.dup pre.map! { |elem| normalize_task_name(elem, ch) } else pre = [normalize_task_name(pre, ch)] end end else name = normalize_task_name(targ, ch) end [name, pre] end public :normalize_task_arg # Tries to make a task name out of arg and returns # the valid task name. If not possible, calls abort # with an appropriate error message using file and ln. def normalize_task_name(arg, ch) return arg if arg.is_a? String if Symbol === arg arg.to_s elsif arg.respond_to? :to_str arg.to_str else abort_at(ch, "Task name has to be a string or symbol.") end end # Returns a Rant::Rantfile object as first value # and a boolean value as second. If the second is true, # the rantfile was created and added, otherwise the rantfile # already existed. def rantfile_for_path(path, register=true) # all rantfiles have an absolute path as path attribute abs_path = File.expand_path(path) file = @rantfiles.find { |rf| rf.path == abs_path } if file [file, false] else # create new Rantfile object file = Rant::Rantfile.new abs_path file.project_subdir = @current_subdir @rantfiles << file if register [file, true] end end # Returns the usual hash with :file and :ln as keys for the first # element in backtrace which comes from an Rantfile, or nil if no # Rantfile is involved. # # Note that this method is very time consuming! def get_ch_from_backtrace(backtrace) backtrace.each { |clr| ch = ::Rant::Lib.parse_caller_elem(clr) if ::Rant::Env.on_windows? return ch if @rantfiles.any? { |rf| # sigh... a bit hackish: replace any backslash # with a slash and remove any leading drive (e.g. # C:) from the path rf.path.tr("\\", "/").sub(/^\w\:/, '') == ch[:file].tr("\\", "/").sub(/^\w\:/, '') } else return ch if @rantfiles.any? { |rf| rf.path == ch[:file] } end } nil end def err_task_fail(e) msg = [] t_msg = ["Task `#{e.tname}' fail."] orig = e loop { orig = orig.orig; break unless Rant::TaskFail === orig } if orig && orig != e && !(Rant::RantAbortException === orig) ch = get_ch_from_backtrace(orig.backtrace) msg << pos_text(ch[:file], ch[:ln]) if ch unless Rant::CommandError === orig && !@opts[:err_commands] msg << orig.message msg << orig.backtrace[0..4] unless ch end end if e.msg && !e.msg.empty? ch = get_ch_from_backtrace(e.backtrace) t_msg.unshift(e.msg) t_msg.unshift(pos_text(ch[:file], ch[:ln])) if ch end err_msg msg unless msg.empty? err_msg t_msg end end # class Rant::RantApp # this line prevents ruby 1.8.3 from segfaulting rant-0.5.8/lib/rant/rantsys.rb0000644000175000017500000002205410527253231015613 0ustar xavierxavier # rantsys.rb - Support for the +sys+ method/object. # # Copyright (C) 2005 Stefan Lang require 'fileutils' require 'rant/import/filelist/core' # Fix FileUtils::Verbose visibility issue if RUBY_VERSION == "1.8.3" module FileUtils METHODS = singleton_methods - %w(private_module_function commands options have_option? options_of collect_method) module Verbose class << self public(*::FileUtils::METHODS) end public(*::FileUtils::METHODS) end end end if RUBY_VERSION < "1.8.1" module FileUtils undef_method :fu_list def fu_list(arg) arg.respond_to?(:to_ary) ? arg.to_ary : [arg] end end end module Rant class RacFileList < FileList attr_reader :subdir attr_reader :basedir def initialize(rac, store = []) super(store) @rac = rac @subdir = @rac.current_subdir @basedir = Dir.pwd @ignore_hash = nil @add_ignore_args = [] update_ignore_rx end def dup c = super c.instance_variable_set( :@add_ignore_args, @add_ignore_args.dup) c end def copy c = super c.instance_variable_set( :@add_ignore_args, @add_ignore_args.map { |e| e.dup }) c end alias filelist_ignore ignore def ignore(*patterns) @add_ignore_args.concat patterns self end def ignore_rx update_ignore_rx @ignore_rx end alias filelist_resolve resolve def resolve Sys.cd(@basedir) { filelist_resolve } end def each_cd(&block) old_pwd = Dir.pwd Sys.cd(@basedir) filelist_resolve if @pending @items.each(&block) ensure Sys.cd(old_pwd) end private def update_ignore_rx ri = @rac.var[:ignore] ri = ri ? (ri + @add_ignore_args) : @add_ignore_args rh = ri.hash unless rh == @ignore_hash @ignore_rx = nil filelist_ignore(*ri) @ignore_hash = rh end end end # class RacFileList class MultiFileList attr_reader :cur_list def initialize(rac) @rac = rac @cur_list = RacFileList.new(@rac) @lists = [@cur_list] end def each_entry(&block) @lists.each { |list| list.each_cd(&block) } end def add(filelist) # TODO: validate filelist @cur_list = filelist @lists << filelist self end def method_missing(sym, *args, &block) if @cur_list && @cur_list.respond_to?(sym) if @cur_list.subdir == @rac.current_subdir @cur_list.send(sym, *args, &block) else add(RacFileList.new(@rac)) @cur_list.send(sym, *args, &block) end else super end end end # class MultiFileList class CommandError < StandardError attr_reader :cmd attr_reader :status def initialize(cmd, status=nil, msg=nil) @msg = msg @cmd = cmd @status = status end def message if !@msg && cmd if status "Command failed with status #{status.exitstatus}:\n" + "[#{cmd}]" else "Command failed:\n[#{cmd}]" end else @msg end end end module Sys include ::FileUtils::Verbose # We include the verbose version of FileUtils # and override the fu_output_message to control # messages. # Set symlink support flag to true and try the first # time (in safe_ln) if symlinks are supported and if # not, reset this flag. @symlink_supported = true class << self attr_accessor :symlink_supported end # We override the output method of the FileUtils module to # allow the Rant application to control output. def fu_output_message(msg) #:nodoc: # direct calls through Rant::Sys are silent end private :fu_output_message def fu_each_src_dest(src, *rest) src = src.to_ary if src.respond_to? :to_ary super(src, *rest) end private :fu_each_src_dest # Run an external command. When given one argument, this is # subject to shell interpretation. Otherwise the first # argument is the program to run, following arguments are # given as arguments to the program. # # Note: This method is called on +sys + # invocation in an Rantfile. def sh(*cmd_args, &block) cmd_args.flatten! cmd = cmd_args.join(" ") fu_output_message cmd success = system(*cmd_args) if block_given? block[$?] elsif !success raise CommandError.new(cmd, $?) end end # Run a new Ruby interpreter with the given arguments: # sys.ruby "install.rb" def ruby(*args, &block) if args.empty? # The empty string argument ensures that +system+ # doesn't start a subshell but invokes ruby directly. # The empty string argument is ignored by ruby. sh(Env::RUBY_EXE, '', &block) else sh(args.unshift(Env::RUBY_EXE), &block) end end # Returns the value of +block+ if a block is given, a true # value otherwise. def cd(dir, &block) fu_output_message "cd #{dir}" orig_pwd = Dir.pwd Dir.chdir dir if block begin block.arity == 0 ? block.call : block.call(Dir.pwd) ensure fu_output_message "cd -" Dir.chdir orig_pwd end else self end end # If supported, make a hardlink, otherwise # fall back to copying. def safe_ln(src, dest) dest = dest.to_str src = src.respond_to?(:to_ary) ? src.to_ary : src.to_str unless Sys.symlink_supported cp(src, dest) else begin ln(src, dest) rescue Exception # SystemCallError # Errno::EOPNOTSUPP Sys.symlink_supported = false cp(src, dest) end end end def ln_f(src, dest) ln(src, dest, :force => true) end def split_path(str) str.split(Env.on_windows? ? ";" : ":") end if Env.on_windows? def root_dir?(path) path == "/" || path == "\\" || path =~ %r{\A[a-zA-Z]+:(\\|/)\Z} # how many drive letters are really allowed on # windows? end def absolute_path?(path) path =~ %r{\A([a-zA-Z]+:)?(/|\\)} end else def root_dir?(path) path == "/" end def absolute_path?(path) path =~ %r{\A/} end end extend self if RUBY_VERSION >= "1.8.4" # needed by 1.9.0, too class << self public(*::FileUtils::METHODS) end public(*::FileUtils::METHODS) end end # module Sys # An instance of this class is returned from the +sys+ method in # Rantfiles (when called without arguments). # sys.rm_rf "tmp" # In this (Rantfile) example, the +rm_rf+ message is sent to an # instance of this class. class SysObject include Sys def initialize(rant) @rant = rant or raise ArgumentError, "rant application required" end # Preferred over directly modifying var[:ignore]. var[:ignore] # might go in future. def ignore(*patterns) @rant.var[:ignore].concat(patterns) nil end # sys.filelist(arg):: # corresponds to Rant::FileList(arg) # sys.filelist:: # corresponds to Rant::FileList.new def filelist(arg = Rant.__rant_no_value__) if Rant.__rant_no_value__.equal?(arg) RacFileList.new(@rant) elsif arg.respond_to?(:to_rant_filelist) arg.to_rant_filelist elsif arg.respond_to?(:to_ary) RacFileList.new(@rant, arg.to_ary) else raise TypeError, "cannot convert #{arg.class} into Rant::FileList" end end # corresponds to Rant::FileList[*patterns]. def [](*patterns) RacFileList.new(@rant).hide_dotfiles.include(*patterns) end # corresponds to Rant::FileList.glob(*patterns, # &block). def glob(*patterns, &block) fl = RacFileList.new(@rant).hide_dotfiles.include(*patterns) fl.ignore(".", "..") if block_given? then yield fl else fl end end # corresponds to Rant::FileList.glob_all(*patterns, # &block). def glob_all(*patterns, &block) fl = RacFileList.new(@rant).include(*patterns) fl.ignore(".", "..") # use case: "*.*" as pattern if block_given? then yield fl else fl end end def expand_path(path) File.expand_path(@rant.project_to_fs_path(path)) end private # Delegates FileUtils messages to +rant+. def fu_output_message(cmd) @rant.cmd_msg cmd end end end # module Rant # this line prevents ruby 1.8.3 from crashing with: [BUG] unknown node type 0 rant-0.5.8/lib/rant/rantvar.rb0000644000175000017500000001545010527253231015567 0ustar xavierxavier # rantvar.rb - Support for the +var+ method in Rantfiles. # # Copyright (C) 2005 Stefan Lang # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # # This file provides support for the +var+ attribute of the Rant # application (Rant::RantApp#var). # Most constants (classes, modules etc.) of Rant live in this module, # thus it acts as a namespace. # # If you're looking for general info about Rant, read the # README[link:files/README.html]. module Rant # Those are the filenames for rantfiles. # Case matters! ROOT_RANTFILE = "root.rant" SUB_RANTFILE = "sub.rant" RANTFILES = [ "Rantfile", "rantfile", ROOT_RANTFILE ] # Names of plugins and imports for which code was loaded. # Files that where loaded with the `import' commant are directly # added; files loaded with the `plugin' command are prefixed with # "plugin/". CODE_IMPORTS = [] class RantAbortException < StandardError end class RantDoneException < StandardError end class Error < StandardError end # This module is a namespace for generator classes. module Generators end module RantVar class Error < Rant::Error end class ConstraintError < Error attr_reader :constraint, :val def initialize(constraint, val, msg = nil) #super(msg) @msg = msg @constraint = constraint @val = val end def message # TODO: handle @msg val_desc = @val.inspect val_desc[7..-1] = "..." if val_desc.length > 10 "#{val_desc} doesn't match constraint: #@constraint" end end class NotAConstraintFactoryError < Error attr_reader :obj def initialize(obj, msg = nil) @msg = msg @obj = obj end def message # TODO: handle @msg obj_desc = @obj.inspect obj_desc[7..-1] = "..." if obj_desc.length > 10 "#{obj_desc} is not a valid constraint factory" end end class InvalidVidError < Error def initialize(vid, msg = nil) @msg = msg @vid = vid end def message # TODO: handle @msg vid_desc = @vid.inspect vid_desc[7..-1] = "..." if vid_desc.length > 10 "#{vid_desc} is not a valid var identifier" end end class InvalidConstraintError < Error end class QueryError < Error end class Space @@env_ref = Object.new def initialize # holds all values @store = {} # holds constraints for values in @store @constraints = {} # set by default query end def query(*args, &block) # currently ignoring block case args.size when 0 raise QueryError, "no arguments", caller when 1 arg = args.first if Hash === arg if arg.size == 1 arg.each { |k,v| self[k] = v if self[k].nil? } self else init_all arg end else self[arg] end when 2, 3 vid, cf, val = *args constrain vid, get_factory(cf).rant_constraint self[vid] = val if val else raise QueryError, "too many arguments" end end def restrict vid, ct, *ct_args if vid.respond_to? :to_ary vid.to_ary.each { |v| restrict(v, ct, *ct_args) } else constrain vid, get_factory(ct).rant_constraint(*ct_args) end self end def get_factory id if String === id || Symbol === id id = Constraints.const_get(id) rescue nil end unless id.respond_to? :rant_constraint raise NotAConstraintFactoryError.new(id), caller end id end private :get_factory # Get var with name +vid+. def [](vid) vid = RantVar.valid_vid vid val = @store[vid] val.equal?(@@env_ref) ? ENV[vid] : val end # Set var with name +vid+ to val. Throws a ConstraintError # if +val+ doesn't match the constraint on +vid+ (if a # constraint is registered for +vid+). def []=(vid, val) vid = RantVar.valid_vid(vid) c = @constraints[vid] if @store[vid] == @@env_ref ENV[vid] = c ? c.filter(val) : val else @store[vid] = c ? c.filter(val) : val end end # Use ENV instead of internal store for given vars. # Probably useful for vars like CC, CFLAGS, etc. def env(*vars) vars.flatten.each { |var| vid = RantVar.valid_vid(var) cur_val = @store[vid] next if cur_val == @@env_ref ENV[vid] = cur_val unless cur_val.nil? @store[vid] = @@env_ref } nil end def set_all hash unless Hash === hash raise QueryError, "set_all argument has to be a hash" end hash.each_pair { |k, v| self[k] = v } end def init_all hash unless Hash === hash raise QueryError, "init_all argument has to be a hash" end hash.each_pair { |k, v| self[k] = v if self[k].nil? } end # Add +constraint+ for var with id +vid+. def constrain vid, constraint vid = RantVar.valid_vid(vid) unless RantVar.valid_constraint? constraint raise InvalidConstraintError, constraint end @constraints[vid] = constraint if @store.member? vid begin val = @store[vid] @store[vid] = constraint.filter(@store[vid]) rescue @store[vid] = constraint.default raise ConstraintError.new(constraint, val) end else @store[vid] = constraint.default end end def has_var?(vid) !self[vid].nil? end def _set(vid, val) #:nodoc: @store[vid] = val end def _get(vid) #:nodoc: @store[vid] end def _init(vid, val) #:nodoc: @store[vid] ||= val end end # class Space module Constraint def matches? val filter val true rescue return false end end # A +vid+ has to be a String to be valid. def valid_vid(obj) case obj when String: obj when Symbol: obj.to_s else if obj.respond_to? :to_str obj.to_str else raise InvalidVidError.new(obj) end end end # A constraint has to respond to the following methods: # [filter(val)] # Filter _val_ or throw ConstraintError if _val_ doesn't # match constraint. # [matches?(val)] # Return true if _val_ matches constraint. def valid_constraint?(obj) # TODO: check for arity obj.respond_to?(:filter) && obj.respond_to?(:matches?) && obj.respond_to?(:default) end module_function :valid_constraint?, :valid_vid module Constraints class AutoList include Constraint class << self alias rant_constraint new end def filter(val) if val.respond_to? :to_ary val.to_ary elsif val.nil? raise ConstraintError.new(self, val) else [val] end end def default [] end def to_s "list or single, non-nil value" end end end # module Constraints end # module RantVar end # module Rant rant-0.5.8/lib/rant/tempfile.rb0000644000175000017500000000041410527253231015711 0ustar xavierxavier # tempfile.rb # # Copyright (C) 2005 Stefan Lang require 'tempfile' if Tempfile.superclass == SimpleDelegator require 'rant/archive/rubyzip/tempfile_bugfixed' Rant::Tempfile = Rant::BugFix::Tempfile else Rant::Tempfile = Tempfile end rant-0.5.8/lib/rant.rb0000644000175000017500000000340010527253231014102 0ustar xavierxavier#!/usr/bin/env ruby require 'rant/rantlib' module RantContext # Needed for irb, which defines its own +source+ method. def source_rf(*args, &block) rac.source(*args, &block) end end module Rant class FileList def inspect # what's the right encoding for object_id ? s = "#<#{self.class}:0x#{"%x" % object_id} " s << "#{@actions.size} actions, #{@items.size} entries" if @ignore_rx is = @ignore_rx.inspect.gsub(/\n|\t/, ' ') s << ", ignore#{is.squeeze ' '}" end if @glob_flags != 0 s << ", flags:#@glob_flags" end s << ">" end end module Node def inspect s = "#<#{self.class}:0x#{"%x" % object_id} " s << "task_id:#{full_name}, action:#{inspect_action}" s << ", deps:#{inspect_deps}" s << ">" end private def inspect_action (defined? @block) ? @block.inspect : "nil" end def inspect_deps if respond_to? :deps dl = deps s = dl.size.to_s dls = dl.join(",") dls[12..dls.length] = "..." if dls.length > 12 s << "[#{dls}]" else "0" end end end class RantApp def inspect s = "#<#{self.class}:0x#{"%x" % object_id} " if current_subdir && !current_subdir.empty? s << "subdir:#{current_subdir}, " end s << "tasks:#{tasks.size}" s << ">" end end end def rant Rant.rant end Rant.instance_variable_set(:@__rant__, Rant::RantApp.new) include RantContext rant-0.5.8/misc/0000755000175000017500000000000010527254520013003 5ustar xavierxavierrant-0.5.8/misc/TODO0000644000175000017500000001233410527253221013473 0ustar xavierxavier = TODO == Convinient symlink method Implement a Rant::Sys.symlink (or similar name) method that converts destination filenames to absolute pathes before actual linking. == More tests for --dry-run == Behaviour of `gen Rule' without block Currently dies with undefined method `arity' for nil:NilClass. Done (0.4.9), printing an appropriate error message. == Command: Hash interpolation Decide about behaviour of hash interpolation in Command tasks. == Don't use +invoke+ result of dependency to check if update is necessary It doesn't make sense since: 1. The node (prerequisite) could have already been invoked by another node as prerequisite. For the first node, the prerequisite would return true, all following false. 2. The node (prerequisite) *can* *not* know if the invoking node is up to date compared to itself. == Check +enhance+ for SourceNodes Rant fails (infinite recursion) for enhance on SourceNodes. Update: Infinite recursion occurs only when a require "rant" was done. Else an appropriate message is printed. Can be considered OK. == Implement Rant::FileList#== == Update documentation with regards to resolve hooks. The argument list for resolve hooks has changed in version 0.4.5. A hook has to accept two arguments now, the first is the target name and the second is the project directory the resolve action is relative to. Previously, a resolve hook got only the target name as argument. == Eventually replace # with another symbol. The # symbol as reference to the project's root directory should eventuelly get replaced with a better fitting symbol. Many shells interpret it as comment start and thus escaping/quoting is needed if #some_target arguments are used on the commandline. This could get a common source of annoyance/bugs. Favorite candidate is currently @. Done (0.4.5), using @. == Improve SourceNode types Currently a SourceNode can only have other SourceNodes or files as prerequisites. But if e.g., a header file should be autogenerated, this isn't sufficient anymore. Done (0.4.5). --- Update Done (0.4.3) for SignedSourceNodes. == Deprecate plugin API ... and provide alternatives for the Configure and CSharp plugins. == FileList FileList objects need a method which adds a file which won't be removed by any pattern (also not affected by var[:ignore]). --- Done (0.4.3). Use FileList#<< to append a single entry which won't be removed by exclude/ignore patterns. == Semantics of directories as prerequisites of file creating nodes. Currently there are two so called "file creating" nodes: FileTask and SignedFile. The following treatments of a directory as prerequisite are possible: 1. Current behaviour: FileTask:: If a directory node exists, invoke it and compare if the modification time of the directory is newer than the target file. SignedFile:: If a directory node exists, invoke it. If the invocation return true, rebuild target file. If no directory node exists, create a signature of the directory by reading its entries and check this signature against the signature of the last time the target file was built. 2. Just check if a directory node exists and invoke it. Trigger target rebuild if +invoke+ returns true. If no node exists do nothing special, the directory doesn't trigger a target file rebuild. I'm currently in favour of #2. --- *Update* Current behaviour: FileTask:: If a directory node exists, invoke it and compare if the modification time of the directory is newer than the target file. If no node exists, just compare the modification time. SignedFile:: If a directory node exists, invoke it and trigger a rebuild if +invoke+ returns true. Anyway create a signature of the path, which results in a rebuild if the directory path changes once. This could be considered an inconsistency since path/name changes of other prerequisites don't trigger a rebuild. == Don't use testrb as testrunner for RubyTest. == Solve archiving dependency problem Because of timed depdencies, the archiving tasks don't recognize if the only change is that a file was removed. The only exception is a primitive +Archive+ task whith manifest synchronization. The problem can be solved by writing a list of files parallel to archive creation or to write a signature of the filelist. == Dependency alternation Allow at least two tasks as one dependency where the first available will actually used to satisfy the dependency, pseudo code: task :a => :b | :c Rant should first try :b and if the task :b exists and succeds :c won't be considered, if task :b doesn't exist, rant should try to make :c. == GET RID OF CLASS Rant::Path Done (0.3.7). == Packaging Try minitar if tar is not available. Done (0.3.9). == C# plugin Define the method +assembly+ for building a file with the C# compiler? Done. == Predefined tasks Add a 'distclean' task that removes all files and directories generatet by any file task. Done (AutoClean generator). == Java plugin At least for the 1.0.0 release. #-- # vim:tw=70 rant-0.5.8/misc/devel-notes0000644000175000017500000000753310527253221015160 0ustar xavierxavier == Rule recursion Since Rant 0.4.9, rules created with gen Rule no longer recurse. This means, that when looking for the Rule's source(s), this specific rule itself is ignored. This prevents infinite recursion. Consider: gen Rule, "o" => ".foo" do something end Say we call make "bo". Rant pre 0.4.9 behaviour: The rule matches anything ending in "o". Since "bo" ends in "o", the rule matches and searches for the source "b.foo". No task is defined for "b.foo", but again our only rule matches and thus searches for the source "b.fo.foo". Now we have our infinite recursion. Rant 0.4.9 and later behaviour: The only rule matches and searches for the source "b.foo". When a rule searches for a source, the rule itself is excluded. No infinite recursion. == Rant::Env.find_bin... ... should honour PATHEXT on windows. == Task alias Since +alias+ is a Ruby keyword, we could use +nick+ to create task alises. Other choices: short, cut, name, label, syn, synonym, ident, term, shortcut, abbreviation, link == Here docs Don't use here documents in the Rant sources, imports and plugins. They'll get messed up by the rant-import command. == More notes on rant-import rant-import removes all lines matching /^\s*#/ per default. Remember that, I was already bitten by that one in multiline regex! (FileList#mk_all_rx) == Running RDoc programmatically require 'rdoc/rdoc' rdoc = RDoc::RDoc.new begin rdoc.document(%w(like args from commandline)) rescue RDoc::RDocError $stderr.puts "Error when running rdoc: " + $!.message end == Changing the behaviour of Rant from Rantfiles Perhaps a method named like `behave' would be appropriate to to set options of the Rant application. == Subdirectories Allow the use of `#' at the start of a pathname to tell rant that it should be interpreted relative to the project's root directory. == Directory structure of +lib/+ +lib/+ contains only two entries: [rant.rb] Used to require rantlib and include +Rant+ for small build scripts. [rant/] All files matching /^rant\w+\.rb$/ are considered as Rant core. Others contain utilities, helper classes, Generators and similar. [rant/plugin] Each file ending in .rb in this directory is considered to load support for one Rant plugin. == Performance With version 0.2.6, the big performance problem with task lookup (caused very slow dependency resolving) is fixed. The next performance improvement should target Rantfile reading, especially task creation. == RubyPackage generator Currently, the package task(s) generated by RubyPackage doesn't recognize (and repackage) when files/directories are removed (from the sources). == Unit tests The unit tests contain many assertions of the form assert_equal(Rant.run(...), 0) which are wrong, because the first argument should be the expected result! == FileList < Array problems There arise some severe problems. E.g. the Array#flatten method: [FileList["*.rb"]].flatten The +flatten+ method doesn't call *any* method of our FileList but it recognizes that it is an Array and copies the elements directly. So there is *no* way to resolve the glob pattern and there will never any Ruby file be selected! == Subdirectories Rant would have to check and eventually +cd+ before doing one of the following: * Loading an Rantfile (even through +source+). * Worker#invoke * Worker#needed? * RantApp#run before returning. == RantContext#rantapp rename Rename +rantapp+ to +rac+. Done. == Circular dependencies Before Rant detected circular dependencies, we silently removed a task dependency on itself. Should we remove this mechanism now? == Testing Files/directories created during tests match the glob *.t* so don't give names matching this pattern to regular files, as they would soon be deleted! == Compiling C make uses the env. variables CC and CFLAGS. # vim:tw=70: rant-0.5.8/misc/mt.rb0000644000175000017500000000002110527253221013736 0ustar xavierxavier module Task end rant-0.5.8/misc/rantmethods.rb0000644000175000017500000000311210527253221015652 0ustar xavierxavier # Run this script with `rant -f' to get a list of methodnames which # allow an Rantfile to communicate with Rant. desc "Print all methods which allow to communicate with rant." task :rant_methods do ml = methods om = Object.instance_methods ml = ml.select { |m| not om.include?(m) }.sort puts ml puts "*** total: #{ml.size} methods ***" end desc "Print constants introduced by Rant." task :constants do puts((self.class.constants - Object.constants).sort) end desc "Print all attribute writers of a Gem::Specification." task :gem_attrs do require 'rubygems' ml = [] Gem::Specification.new do |s| ml = s.methods end ml = ml.select { |m| m =~ /\w=$/ }.sort puts ml puts "*** total: #{ml.size} methods ***" end file "bench-rant" do |t| c = 2000 if var["TC"] c = Integer(var["TC"]) end File.open(t.name, "w") { |f| f.puts "$tc_run = 0" c.times { |i| f << <<-EOT task "#{i}" => "#{i+1}" do $tc_run += 1 end EOT } f << <<-EOT task "#{c}" do $tc_run += 1 end at_exit { puts $tc_run.to_s + " tasks run" } EOT } end file "bench-depsearch" do |t| c = 2000 if var["TC"] c = Integer var["TC"] end File.open(t.name, "w") { |f| f.puts "$tc_run = 0" all = [] c.times { |i| all << i.to_s f << <<-EOT task "#{i}" => "#{c}" do print "*" $tc_run += 1 end EOT } f << <<-EOT task :all => %w(#{all.join(" ")}) task "#{c}" do print "+" $tc_run += 1 end at_exit { puts puts $tc_run.to_s + " tasks run" } EOT } end rant-0.5.8/misc/t.rb0000644000175000017500000000035710527253221013575 0ustar xavierxavier =begin Run this with `rant -f t.rb', it should print the following 3 lines: Task Rant::Task a =end require 'rubygems' #require 'rake' require 'mt' gen Task, "a" do |t| t.needed { true } t.act { puts t.name } end p ::Task p Task rant-0.5.8/test/0000755000175000017500000000000010527254520013027 5ustar xavierxavierrant-0.5.8/test/c/0000755000175000017500000000000010527254520013251 5ustar xavierxavierrant-0.5.8/test/c/source.c0000644000175000017500000000042410527253217014717 0ustar xavierxavier #include #include "util.h" /* * some comment */ #include "mylib.h" /* #include "xy" */ // # include #include "custom" // custom header # // include "abc" #include \ "custom2.h" #include /* one line block comment */ #include "_foo.h" rant-0.5.8/test/c/test_parse_includes.rb0000644000175000017500000000152410527253217017641 0ustar xavierxavier require 'tutil' require 'rant/c/include' $testCDir ||= File.expand_path(File.dirname(__FILE__)) class TestCParseIncludes < Test::Unit::TestCase C = Rant::C def setup Dir.chdir($testCDir) end def test_parse_source src = File.read "source.c" sc, lc = C::Include.parse_includes(src) assert_equal(%w(stdio.h file.h std), sc) assert_equal( %w(util.h mylib.h custom custom2.h _foo.h), lc) end def test_parse_empty sc, lc = C::Include.parse_includes("") assert(sc.empty?) assert(lc.empty?) end def test_parse_nil assert_raises(ArgumentError) { C::Include.parse_includes(nil) } end def test_accepts_to_str obj = Object.new def obj.to_str "//" end lc, sc = nil, nil assert_nothing_raised { sc, lc = C::Include.parse_includes(obj) } assert(sc.empty?) assert(lc.empty?) end end rant-0.5.8/test/deprecated/0000755000175000017500000000000010527254520015127 5ustar xavierxavierrant-0.5.8/test/deprecated/README0000644000175000017500000000040310527253220016000 0ustar xavierxavierThis directory contains tests for deprecated features. The test file names have the form test_digit_digit_digit.rb, where the digits represent the Rant release version with which the features tested in the file (and thus the testfile itself) will disappear. rant-0.5.8/test/deprecated/test_0_6_0.rb0000644000175000017500000000121210527253220017306 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import' $test_deprecated_dir ||= File.expand_path(File.dirname(__FILE__)) class TestDeprecated_0_6_0 < Test::Unit::TestCase include Rant::TestUtil def setup Dir.chdir $test_deprecated_dir end def test_rant_import_option_v out, err = capture_std do assert_equal(0, Rant::RantImport.new("-v").run) end if Rant::VERSION > "0.4.8" assert_match(/-v.*\bdeprecated\b.*-V.*--version\b/m, err) else assert err.empty? end assert_match(/rant-import\s#{Regexp.escape Rant::VERSION}/, out) end end rant-0.5.8/test/dryrun/0000755000175000017500000000000010527254520014352 5ustar xavierxavierrant-0.5.8/test/dryrun/Rantfile0000644000175000017500000000031410527253217016041 0ustar xavierxavier import "command" task :install => "foo.t" do puts "installing foo" end gen Command, "foo.t", "foo.c", "$[sh_echo] $(>) $(<) > $(>)" @sh_echo = "#{sys.sp Env::RUBY_EXE} -e \"puts ARGV.join(' ')\"" rant-0.5.8/test/dryrun/foo.c0000644000175000017500000000013710527253217015304 0ustar xavierxavier #include int main(int argc, char** argv) { printf("Hello!\n"); return 0; } rant-0.5.8/test/dryrun/test_dryrun.rb0000644000175000017500000000173210527253217017266 0ustar xavierxavier require 'test/unit' require 'tutil' # Ensure we run in testproject directory. $testDryRunDir ||= File.expand_path(File.dirname(__FILE__)) class TestDryRun < Test::Unit::TestCase include Rant::TestUtil def setup Dir.chdir($testDryRunDir) end def teardown end def test_default cmd = "#{Rant::Sys.sp Rant::Env::RUBY_EXE} -e \"puts ARGV.join(' ')\" foo.t foo.c > foo.t" out, err = assert_rant "-n" assert err.empty? assert !test(?e, "foo.t") assert out !~ /installing foo/ lines = out.split(/\n/) assert_match(/Executing.*\bfoo\.t\b/i, lines[0]) assert_match(/\s+-\s+SHELL\b/i, lines[1]) assert_match(/\s+#{Regexp.escape cmd}/, lines[2]) assert_match(/Executing.*\binstall\b/i, lines[3]) assert_match(/\s+-\s+Ruby Proc\b.*\bRantfile\b.*\b4\b/i, lines[4]) out2, err2 = assert_rant "--dry-run" assert_equal out, out2 assert_equal err, err2 end end rant-0.5.8/test/import/0000755000175000017500000000000010527254520014341 5ustar xavierxavierrant-0.5.8/test/import/c/0000755000175000017500000000000010527254520014563 5ustar xavierxavierrant-0.5.8/test/import/c/dependencies/0000755000175000017500000000000010527254520017211 5ustar xavierxavierrant-0.5.8/test/import/c/dependencies/include/0000755000175000017500000000000010527254520020634 5ustar xavierxavierrant-0.5.8/test/import/c/dependencies/include/sub/0000755000175000017500000000000010527254520021425 5ustar xavierxavierrant-0.5.8/test/import/c/dependencies/include/sub/sub.h0000644000175000017500000000013010527253220022355 0ustar xavierxavier#ifndef SUB_SUB_H #define SUB_SUB_H #include "bar.h" #define SUB_STR "substr" #endif rant-0.5.8/test/import/c/dependencies/include/foo.h0000644000175000017500000000000010527253220021552 0ustar xavierxavierrant-0.5.8/test/import/c/dependencies/include/with space.h0000644000175000017500000000010510527253220023024 0ustar xavierxavier #ifndef WITH_SPACE_H #define WITH_SPACE_H #include "foo.h" #endif rant-0.5.8/test/import/c/dependencies/src/0000755000175000017500000000000010527254520020000 5ustar xavierxavierrant-0.5.8/test/import/c/dependencies/src/abc0000644000175000017500000000005610527253220020445 0ustar xavierxavier float abc(float param) { return param; } rant-0.5.8/test/import/c/dependencies/src/abc.c0000644000175000017500000000005210527253220020662 0ustar xavierxavier int abc(int param) { return param; } rant-0.5.8/test/import/c/dependencies/src/bar.c0000644000175000017500000000021110527253220020676 0ustar xavierxavier #include #include "abc" #include "with space.h" void greet() { abc(0); abc(0.0); printf("greetings from bar\n"); } rant-0.5.8/test/import/c/dependencies/Rantfile0000644000175000017500000000113510527253220020674 0ustar xavierxavier import "var/numbers", "c/dependencies", "autoclean" var :deps, 1..3 file "a.t" => sys["**/*.{c,cpp}"] do |t| sys.touch t.name end file "hello.t" => "hello.c" do |t| sys.touch t.name end file "bar.t" => "src/bar.c" do |t| sys.touch t.name end desc "Create C source dependency file." gen C::Dependencies gen C::Dependencies, "deps2.t", :search => [".", "include"] gen C::Dependencies, "deps3.t", :search => [".", "include", "src"] gen Action do source case var[:deps] when 2: "deps2.t" when 3: "deps3.t" else "c_dependencies" end end gen AutoClean # vim:ft=ruby rant-0.5.8/test/import/c/dependencies/bar.h0000644000175000017500000000002210527253220020114 0ustar xavierxavier #include "foo.h" rant-0.5.8/test/import/c/dependencies/foo.h0000644000175000017500000000004510527253220020140 0ustar xavierxavier #ifndef FOO_H #define FOO_H #endif rant-0.5.8/test/import/c/dependencies/hello.c0000644000175000017500000000012110527253220020446 0ustar xavierxavier#include "foo.h" #include int main() { printf("Hello, world!"); } rant-0.5.8/test/import/c/dependencies/test_c_dependencies.rb0000644000175000017500000000574010527253220023527 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportCDepDir ||= File.expand_path(File.dirname(__FILE__)) class TestImportCDependencies < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir $testImportCDepDir @manifest = %w( test_c_dependencies.rb Rantfile hello.c foo.h bar.h include include/foo.h include/sub include/sub/sub.h src src/abc src/abc.c src/bar.c ) @manifest << "include/with space.h" end def teardown Dir.chdir $testImportCDepDir FileUtils.rm_f "c_dependencies" FileUtils.rm_rf Dir["*.t"] @manifest.each { |f| assert(test(?e, f), "#{f} missing") } end def test_hello_c assert_rant("hello.t") assert(test(?f, "hello.t")) assert(test(?f, "c_dependencies")) out, err = assert_rant("hello.t") assert(out.strip.empty?) assert(err.strip.empty?) timeout FileUtils.touch "foo.h" old_mtime = File.mtime "hello.t" assert_rant("hello.t") assert(File.mtime("hello.t") > old_mtime) old_mtime = File.mtime("hello.t") timeout out, err = assert_rant("hello.t") assert(out.strip.empty?) assert(err.strip.empty?) FileUtils.rm "c_dependencies" out, err = assert_rant("hello.t") assert(!out.strip.empty?) assert(err.strip.empty?) assert_equal(old_mtime, File.mtime("hello.t")) end def test_bar_c assert_rant("deps=2", "bar.t") assert(test(?f, "bar.t")) assert(test(?f, "deps2.t")) cdeps_mtime = File.mtime "deps2.t" FileUtils.rm "bar.t" assert_rant("deps=2", "bar.t") assert(test(?f, "bar.t")) assert_equal(cdeps_mtime, File.mtime("deps2.t")) old_mtime = File.mtime "bar.t" timeout FileUtils.touch "src/abc.c" assert_rant("deps=2", "bar.t") assert_equal(old_mtime, File.mtime("bar.t")) timeout FileUtils.touch "include/with space.h" assert_rant("deps=2", "bar.t") assert(File.mtime("bar.t") > old_mtime) end def test_bar_c_deps3 assert_rant("deps=3", "bar.t") assert(test(?f, "bar.t")) assert(test(?f, "deps3.t")) old_mtime = File.mtime("bar.t") timeout FileUtils.touch "src/abc" assert_rant("deps=3", "bar.t") assert(File.mtime("bar.t") > old_mtime) old_mtime = File.mtime "bar.t" timeout FileUtils.touch "src/abc.c" assert_rant("deps=3", "bar.t") assert(File.mtime("bar.t") > old_mtime) old_mtime = File.mtime "bar.t" timeout FileUtils.touch "foo.h" assert_rant("deps=3", "bar.t") assert(File.mtime("bar.t") > old_mtime) assert_rant("autoclean") %w(a.t hello.t bar.t c_dependencies deps2.t deps3.t).each { |f| assert(!test(?e, f), "#{f} should get unlinked by AutoClean") } end def test_rant_import_hello_c run_import("-q", "--auto", "ant.t") assert_exit assert(test(?f, "ant.t")) run_ruby("ant.t", "hello.t") assert_exit assert(test(?f, "hello.t")) assert(test(?f, "c_dependencies")) out = run_ruby("ant.t", "hello.t") assert(out.strip.empty?) open "ant.t" do |f| requires = extract_requires(f) requires.each { |fn| assert_no_match(/^rant\//, fn, "#{fn} should be inlined by rant-import") } end end end rant-0.5.8/test/import/c/dependencies/test_on_the_fly.rb0000644000175000017500000001147410527253220022726 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportCDepDir ||= File.expand_path(File.dirname(__FILE__)) class TestImportCDependenciesOnTheFly < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir $testImportCDepDir end def teardown Dir.chdir $testImportCDepDir FileUtils.rm_f "c_dependencies" FileUtils.rm_rf Dir["*.t"] end def test_opts_without_filename open "rf.t", "w" do |f| f << <<-EOF file "bar.t" => "src/bar.c" do |t| sys.touch t.name end gen C::Dependencies, :sources => sys["src/*.c"], :search => "include" source "c_dependencies" EOF end assert_rant("-frf.t") assert(test(?f, "bar.t")) out, err = assert_rant("-frf.t") assert(out.strip.empty?) assert(err.strip.empty?) old_mtime = File.mtime "bar.t" timeout FileUtils.touch "src/abc" assert_rant("-frf.t") assert_equal(old_mtime, File.mtime("bar.t")) timeout FileUtils.touch "include/with space.h" assert_rant("-frf.t") assert(File.mtime("bar.t") > old_mtime) end def write(fn, content) open fn, "w" do |f| f.write content end end def test_md5 write "include/a.tt", <<-EOF void abc(void); EOF write "a.tt", <<-EOF #include "a.tt" EOF write "rf.t", <<-EOF import "md5", "c/dependencies", "autoclean" gen C::Dependencies, :search => ["include"], :sources => ["a.tt", "include/a.tt"] gen Action do source "c_dependencies" end gen AutoClean file "a.out" => "a.tt" do |t| sys.cp t.source, t.name end EOF out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(!out.empty?) assert(test(?f, "c_dependencies")) assert(test(?f, "a.out")) assert_equal(File.read("a.tt"), File.read("a.out")) out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(out.empty?) write "include/a.tt", <<-EOF int abc(void); EOF out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(!out.empty?) assert(test(?f, "c_dependencies")) assert(test(?f, "a.out")) assert_equal(File.read("a.tt"), File.read("a.out")) out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(out.empty?) assert_rant("-frf.t", "autoclean") assert(!test(?e, ".rant.meta")) assert(!test(?e, "a.out")) assert(test(?f, "include/a.tt")) assert(test(?f, "a.tt")) ensure FileUtils.rm_f %w(include/a.tt a.tt rf.t a.out .rant.meta) end @@case_insensitive_fs = nil def case_insensitive_fs? return @@case_insensitive_fs unless @@case_insensitive_fs.nil? Rant::Sys.touch "Case.t" if @@case_insensitive_fs = File.exist?("case.t") puts "\n*** testing on a case-insensitive filesystem ***" true else puts "\n*** testing on a case-sensitive filesystem ***" false end ensure Rant::Sys.rm_f %w(case.t) end def test_correct_case_md5 write "include/a.tt", <<-EOF void abc(void); EOF write "a.tt", <<-EOF #include "A.tt" EOF write "rf.t", <<-EOF import "md5", "c/dependencies", "autoclean" gen C::Dependencies, :correct_case => true, :search => ["include"], :sources => ["a.tt", "include/a.tt"] gen Action do source "c_dependencies" end gen AutoClean file "a.out" => "a.tt" do |t| sys.cp t.source, t.name end EOF out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(!out.empty?) assert(test(?f, "c_dependencies")) assert(test(?f, "a.out")) assert_equal(File.read("a.tt"), File.read("a.out")) out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(out.empty?) write "include/a.tt", <<-EOF int abc(void); EOF out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) return unless case_insensitive_fs? assert(!out.empty?) assert(test(?f, "c_dependencies")) assert(test(?f, "a.out")) assert_equal(File.read("a.tt"), File.read("a.out")) out, err = assert_rant("-frf.t", "a.out") assert(err.empty?) assert(out.empty?) assert_rant("-frf.t", "autoclean") assert(!test(?e, ".rant.meta")) assert(!test(?e, "a.out")) assert(test(?f, "include/a.tt")) assert(test(?f, "a.tt")) ensure FileUtils.rm_f %w(include/a.tt a.tt rf.t a.out .rant.meta) end end rant-0.5.8/test/import/command/0000755000175000017500000000000010527254520015757 5ustar xavierxavierrant-0.5.8/test/import/command/Rantfile0000644000175000017500000000477310527253221017456 0ustar xavierxavier import "var/strings", "var/booleans", "command", "autoclean" @ruby = Env::RUBY_EXE @sh_echo = "#{sys.sp Env::RUBY_EXE} -e \"puts ARGV.join(' ')\"" @sh_puts = "#{sys.sp Env::RUBY_EXE} -e \"puts ARGV\"" @sh_cat = "#{sys.sp Env::RUBY_EXE} -e \"print ARGF.read\"" desc "Build a.t" gen Command, "a.t" => ["b.t", "c.t"] do |t| "#@sh_echo #{sys.sp t.prerequisites} > #{t.name}" end gen Command, "f_a.t" do |t| sys "#@sh_echo #{sys.escape "I will fail."} > #{t.name}" end gen Command, "a2.t", ["b.t", "c.t"], "$[sh_puts] $(<) > $(>)" var :btxt => "b" var :be, :Bool gen Command, "b.t", "$[sh_echo] $(btxt) > $(>)" if var[:be] enhance "b.t" => "d.t" do |t| import "sys/more" sys.write_to_file(t.name, File.read(t.name) + File.read(t.source)) end end gen Command, "c.t", sys["d???.t"], "$[sh_echo] $(<) > $(>)" gen Command, "with space/a.t", ["b.t", "with space/b.t"], "$[sh_puts] $(<) > $(>)" var :fargs => "/I$(p_ath1)" var :p_ath1 => "a bc" gen Command, "f.t", "$[sh_echo] $[fargs] > $[>]" var :eargs => "/I$(epath)" var :epath => "a b/c/" gen Command, "e.t", "$[sh_echo] $[eargs] > ${>}" var :gargs => "/I${gpath}" var :gpath => "a b/c/" gen Command, "g.t", "$[sh_echo] $[gargs] > $(>)" var :h1 => 1 var :h2 => 2 gen Command, "h.t", < $(>)1 $[sh_echo] ${h2} > $(>)2 $[sh_cat] $(>)1 $(>)2 > $(>) end var :rargs => "$(prerequisites) $(source) > $(name)" var :rcmd => "$[sh_echo] " + var[:rargs] gen Rule, :out => [:in1, :in2] do |name, sources| gen Command, name, sources, var[:rcmd] end gen Directory, "a.in1" var :rc_dep => "puts 'a'" gen Command, "dep1.t", "$(ruby) -e \"$[rc_dep]\" > $(>)" gen Command, "t1.t", "dep1.t", "$[sh_echo] making t1 > $(>)" gen Command, "t2.t", "dep1.t", "$[sh_echo] making t2 > $(>)" gen Command, "sub1.t/a", "#@sh_echo ${>} > $(>)" gen Command, "sub2.t/a", "$[sh_echo] ${>} > $(>)" gen Directory, "sub2.t" task :sub3 do puts "task sub3" end gen Command, "sub3/a", "$[sh_echo] ${>} > $(>)" gen Command, "x.t", '[#$[sh_puts] ${a}#] ${b} > $(>)' gen Command, "delay.t", "$[sh_echo] ${foo} > $(>)" gen Command, "p1.t", '$[sh_puts] ${p1} > $(>)' gen Command, "p2.t", '$[sh_puts] ${p2} > $(>)' gen Command, "p3.t", '$[sh_puts] ${p2} > $(>)' var :foo => "foo value" task :change_foo do var[:foo] = "changed" end var[:p1] = lambda { |n| "#{n.full_name} $[foo]" } var[:p2] = lambda { var[:foo] << "." } if var[:inc_foo] var[:p2].call end @h = {:a => "b"} gen Command, "hash.t", "$[sh_puts] ${h} > $(>)" gen AutoClean rant-0.5.8/test/import/command/test_command.rb0000644000175000017500000005103710527253221020764 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportCommandDir ||= File.expand_path(File.dirname(__FILE__)) class TestImportCommand < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($testImportCommandDir) end def teardown Dir.chdir($testImportCommandDir) assert_rant "autoclean" assert Rant::FileList["*.t"].empty? assert Rant::FileList[".rant.meta"].empty? end def test_plain_syntax_no_deps out, err = assert_rant "b.t" assert err.empty? assert !out.empty? assert test(?f, "b.t") assert_equal "b", File.read("b.t").strip out, err = assert_rant "b.t" assert err.empty? assert out.empty? out, err = assert_rant "b.t", "btxt=x" assert err.empty? assert !out.empty? assert test(?f, "b.t") assert_equal "x", File.read("b.t").strip out, err = assert_rant "b.t", "btxt=x" assert err.empty? assert out.empty? end def test_plain_syntax_no_deps_md5 out, err = assert_rant "-imd5", "b.t" assert err.empty? assert !out.empty? assert test(?f, "b.t") assert_equal "b", File.read("b.t").strip out, err = assert_rant "-imd5", "b.t" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "b.t", "btxt=x" assert err.empty? assert !out.empty? assert test(?f, "b.t") assert_equal "x", File.read("b.t").strip out, err = assert_rant "-imd5", "b.t", "btxt=x" assert err.empty? assert out.empty? end def test_opt_tasks out, err = assert_rant "-T" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/\ba\.t\b.*\bBuild a\.t\b/, lines.first) end def test_block_syntax out, err = assert_rant assert err.empty? lines = out.split(/\n/) assert_match(/b\.t/, lines[0]) assert_match(/c\.t/, lines[1]) assert_match(/a\.t/, lines[2]) assert_file_content "a.t", "b.t c.t", :strip assert_file_content "b.t", "b", :strip assert_file_content "c.t", "", :strip out, err = assert_rant assert err.empty? assert out.empty? end def test_block_syntax_md5 out, err = assert_rant "-imd5" assert err.empty? lines = out.split(/\n/) assert_match(/b\.t/, lines[0]) assert_match(/c\.t/, lines[1]) assert_match(/a\.t/, lines[2]) assert_file_content "a.t", "b.t c.t", :strip assert_file_content "b.t", "b", :strip assert_file_content "c.t", "", :strip out, err = assert_rant "-imd5" assert err.empty? assert out.empty? Rant::Sys.write_to_file "d .t", "abc" out, err = assert_rant "-imd5" assert err.empty? lines = out.split(/\n/) assert_match(/c\.t/, lines[0]) assert_match(/a\.t/, lines[1]) assert_file_content "a.t", "b.t c.t", :strip assert_file_content "c.t", "d .t", :strip out, err = assert_rant "-imd5" assert err.empty? assert out.empty? ensure Rant::Sys.rm_f "d .t" end def test_prerequisites_array out, err = assert_rant "a2.t" assert err.empty? assert_file_content "a2.t", "b.t\nc.t\n" out, err = assert_rant "a2.t" assert err.empty? assert out.empty? end def test_enhance Rant::Sys.write_to_file "d.t", "d\n" out, err = assert_rant "b.t", "be=on" assert err.empty? assert !out.empty? assert_file_content "b.t", "b\nd\n" out, err = assert_rant "b.t", "be=on" assert err.empty? assert out.empty? ensure Rant::Sys.rm_f "d.t" end def test_enhance_md5 Rant::Sys.write_to_file "d.t", "d\n" out, err = assert_rant "-imd5", "b.t", "be=on" assert err.empty? assert !out.empty? assert_file_content "b.t", "b\nd\n" out, err = assert_rant "-imd5", "b.t", "be=on" assert err.empty? assert out.empty? Rant::Sys.write_to_file "d.t", "e\n" out, err = assert_rant "-imd5", "b.t", "be=on" assert err.empty? assert !out.empty? assert_file_content "b.t", "b\ne\n" out, err = assert_rant "-imd5", "b.t", "be=on" assert err.empty? assert out.empty? Rant::Sys.write_to_file "b.t", "c\n" out, err = assert_rant "-imd5", "b.t", "be=on" assert err.empty? assert !out.empty? assert_file_content "b.t", "b\ne\n" out, err = assert_rant "-imd5", "b.t", "be=on" assert err.empty? assert out.empty? ensure Rant::Sys.rm_f "d.t" end def test_rule out, err = assert_rant :fail, "a.out" assert out.empty? lines = err.split(/\n/) assert lines.size < 3 assert_match(/ERROR.*\ba\.out\b/, lines.first) assert !test(?e, "a.out") assert !test(?e, "a.in1") Rant::Sys.write_to_file "a.in2", "" assert test(?f, "a.in2") out, err = assert_rant "a.out" assert err.empty? assert !out.empty? assert_file_content "a.out", "a.in1 a.in2 a.in1", :strip assert test(?d, "a.in1") out, err = assert_rant "a.out", "rargs=$(prerequisites) $(source) > $(name)" assert err.empty? assert out.empty? out, err = assert_rant "a.out", "rargs=$(source) > $(name)" assert err.empty? assert !out.empty? assert_file_content "a.out", "a.in1", :strip assert test(?d, "a.in1") out, err = assert_rant "a.out", "rargs=$(source) > $(name)" assert err.empty? assert out.empty? timeout Rant::Sys.touch "a.in2" out, err = assert_rant "a.out", "rargs= $(source) > $(>)" assert err.empty? assert !out.empty? assert_file_content "a.out", "a.in1", :strip out, err = assert_rant "a.out", "rargs=$(source) > $(>)" assert err.empty? assert out.empty? ensure Rant::Sys.rm_f "a.in2" assert_rant "autoclean" assert !(test(?e, "a.out")) assert !(test(?e, "a.in1")) end def test_rule_md5 Rant::Sys.write_to_file "a.in2", "" assert test(?f, "a.in2") out, err = assert_rant "-imd5", "a.out" assert err.empty? assert !out.empty? assert_file_content "a.out", "a.in1 a.in2 a.in1", :strip assert test(?d, "a.in1") out, err = assert_rant "-imd5", "a.out", "rargs=$(prerequisites) $(source) > $(name)" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "a.out", "rargs=$(source) > $(>)" assert err.empty? assert !out.empty? assert_file_content "a.out", "a.in1", :strip assert test(?d, "a.in1") out, err = assert_rant "-imd5", "a.out", "rargs= $(source) > $(>)" assert err.empty? assert out.empty? Rant::Sys.write_to_file "a.in2", " " out, err = assert_rant "-imd5", "a.out", "rargs= $(source) > $(>)" assert err.empty? assert !out.empty? assert_file_content "a.out", "a.in1", :strip out, err = assert_rant "-imd5", "a.out", "rargs=$(source) > $(>)" assert err.empty? assert out.empty? ensure Rant::Sys.rm_f "a.in2" assert_rant "autoclean" assert !(test(?e, "a.out")) assert !(test(?e, "a.in1")) end def test_ignore_symbolic_node_var_changes Rant::Sys.mkdir "sub.t" Rant::Sys.touch ["sub.t/b.in1", "sub.t/b.in2"] out, err = assert_rant "sub.t/b.out", "rargs=$(<) $(-) > $(>)" assert err.empty? assert !out.empty? if Rant::Env.on_windows? assert_file_content "sub.t/b.out", "sub.t\\b.in1 sub.t\\b.in2 sub.t\\b.in1", :strip else assert_file_content "sub.t/b.out", "sub.t/b.in1 sub.t/b.in2 sub.t/b.in1", :strip end out, err = assert_rant "sub.t/b.out", "rargs=$(<) $(-) > $(>)" assert err.empty? assert out.empty? Dir.chdir "sub.t" out, err = assert_rant "-u", "b.out", "rargs=$(<) $(-) > $(>)" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/\(root\b.*\bsub\.t\)/, lines.first) if Rant::Env.on_windows? assert_file_content "b.out", "sub.t\\b.in1 sub.t\\b.in2 sub.t\\b.in1", :strip else assert_file_content "b.out", "sub.t/b.in1 sub.t/b.in2 sub.t/b.in1", :strip end ensure Dir.chdir $testImportCommandDir Rant::Sys.rm_rf "sub.t" end def test_do_not_ignore_non_symbolic_node_var_changes Rant::Sys.mkdir "sub.t" Rant::Sys.touch ["sub.t/b.in1", "sub.t/b.in2"] out, err = assert_rant "sub.t/b.out" assert err.empty? assert !out.empty? if Rant::Env.on_windows? assert_file_content "sub.t/b.out", "sub.t\\b.in1 sub.t\\b.in2 sub.t\\b.in1", :strip else assert_file_content "sub.t/b.out", "sub.t/b.in1 sub.t/b.in2 sub.t/b.in1", :strip end out, err = assert_rant "sub.t/b.out" assert err.empty? assert out.empty? Dir.chdir "sub.t" out, err = assert_rant "-u", "b.out" assert err.empty? assert !out.empty? lines = out.split(/\n/) assert_equal 2, lines.size assert_match(/\(root\b.*\bsub\.t\)/, lines.first) assert_match(/\bb\.out\b/, lines[1]) assert_file_content "b.out", "b.in1 b.in2 b.in1", :strip ensure Dir.chdir $testImportCommandDir Rant::Sys.rm_rf "sub.t" end def test_with_space Rant::Sys.mkdir "with space" Rant::Sys.write_to_file "with space/b.t", "content" out, err = assert_rant "with space/a.t" assert err.empty? assert !out.empty? content = Rant::Env.on_windows? ? "b.t\nwith space\\b.t\n" : "b.t\nwith space/b.t\n" assert_file_content "with space/a.t", content out, err = assert_rant "with space/a.t" assert err.empty? assert out.empty? assert_rant "autoclean" ["with space/a.t", "with space/a.t", "with space/.rant.meta", "b.t"].each { |fn| assert !test(?e, fn) } ensure Rant::Sys.rm_rf "with space" end def test_sp_var_inline out, err = assert_rant "f.t" assert err.empty? assert !out.empty? assert_file_content "f.t", "/Ia bc\n" out, err = assert_rant "f.t" assert err.empty? assert out.empty? end def test_sp_var_inline_path out, err = assert_rant "e.t" assert err.empty? assert !out.empty? content = Rant::Env.on_windows? ? "/Ia b\\c\\\n" : "/Ia b/c/\n" assert_file_content "e.t", content out, err = assert_rant "e.t" assert err.empty? assert out.empty? end def test_sp_var_inline_escaped out, err = assert_rant "g.t" assert err.empty? assert !out.empty? assert_file_content "g.t", "/Ia b/c/\n" out, err = assert_rant "g.t" assert err.empty? assert out.empty? end def test_sp_var_inline_escaped_md5 out, err = assert_rant "-imd5", "g.t" assert err.empty? assert !out.empty? assert_file_content "g.t", "/Ia b/c/\n" out, err = assert_rant "-imd5", "g.t" assert err.empty? assert out.empty? end def test_rant_import run_import("-q", "--auto", "-imd5", "ant.t") assert_exit out = run_ruby("ant.t", "-imd5", "e.t") assert_exit assert !out.empty? content = Rant::Env.on_windows? ? "/Ia b\\c\\\n" : "/Ia b/c/\n" assert_file_content "e.t", content out = run_ruby("ant.t", "-imd5", "e.t") assert out.empty? ensure Rant::Sys.rm_f "ant.t" end def test_multiple_commands out, err = assert_rant "h.t" assert err.empty? assert !out.empty? assert_file_content "h.t1", "1\n" assert_file_content "h.t2", "2\n" assert_file_content "h.t", "1\n2\n" meta = File.read ".rant.meta" out, err = assert_rant "h.t" assert err.empty? assert out.empty? assert_equal meta, File.read(".rant.meta") ensure Rant::Sys.rm_f ["h.t1", "h.t2"] end def test_multiple_commands_md5 out, err = assert_rant "-imd5", "h.t" assert err.empty? assert !out.empty? assert_file_content "h.t1", "1\n" assert_file_content "h.t2", "2\n" assert_file_content "h.t", "1\n2\n" meta = File.read ".rant.meta" out, err = assert_rant "-imd5", "h.t" assert err.empty? assert out.empty? assert_equal meta, File.read(".rant.meta") ensure Rant::Sys.rm_f ["h.t1", "h.t2"] end def test_block_sys_instead_of_string out, err = assert_rant :fail, "f_a.t" lines = err.split(/\n/) assert lines.size < 5 assert_match(/\[ERROR\]/, err) rf_path = File.join($testImportCommandDir, "Rantfile") assert_match(/#{Regexp.escape rf_path}\b.*\b13\b/, err) assert_match(/block has to return command string/i, err) assert_match(/\bf_a\.t\b/, err) end def test_only_one_arg in_local_temp_dir do Rant::Sys.write_to_file "root.rant", <<-EOF import "command" gen Command, "a" EOF out, err = assert_rant :fail lines = err.split(/\n/) assert lines.size < 4 assert_match(/\[ERROR\]/, err) assert_match(/\broot\.rant\b.*\b2\b/, err) assert_match(/argument/, err) assert_match(/\bname\b.*\bcommand\b/, err) old_out, old_err = out, err out, err = assert_rant :fail, "-T" assert_equal old_out, out assert_equal old_err, err end end def test_dep_rebuild_no_change_md5 out, err = assert_rant "-imd5", "t1.t", "t2.t" assert err.empty? assert !out.empty? assert_file_content "dep1.t", "a\n" assert_file_content "t1.t", "making t1\n" assert_file_content "t2.t", "making t2\n" out, err = assert_rant "-imd5", "t1.t", "t2.t" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "rc_dep=print 'a'; puts", "t1.t", "t2.t" assert err.empty? assert out.include?("print") assert !out.include?("making t2") assert !out.include?("making t1") end def test_dep_rebuild_same_content_md5 out, err = assert_rant "-imd5", "t1.t", "t2.t" assert err.empty? assert !out.empty? assert_file_content "dep1.t", "a\n" assert_file_content "t1.t", "making t1\n" assert_file_content "t2.t", "making t2\n" out, err = assert_rant "-imd5", "t1.t", "t2.t" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "rc_dep=print 'b'; puts", "dep1.t" out, err = assert_rant "-imd5", "rc_dep=print 'a'; puts", "t1.t", "t2.t" assert err.empty? assert out.include?("print") assert !out.include?("making t2") assert !out.include?("making t1") end def test_in_subdir out, err = assert_rant :fail, "sub1.t/a" Rant::Sys.mkdir "sub1.t" out, err = assert_rant "sub1.t/a" assert err.empty? assert !out.empty? assert_file_content "sub1.t/a", "sub1.t/a\n" out, err = assert_rant "sub1.t/a" assert err.empty? assert out.empty? out, err = assert_rant "autoclean" assert !test(?e, "sub1.t/a") assert test(?d, "sub1.t") ensure Rant::Sys.rm_rf "sub1.t" end def test_in_subdir_with_dirtask out, err = assert_rant "sub2.t/a" assert err.empty? assert !out.empty? assert test(?d, "sub2.t") assert_file_content "sub2.t/a", "sub2.t/a\n" out, err = assert_rant "sub2.t/a" assert err.empty? assert out.empty? assert_rant "autoclean" assert !test(?e, "sub2.t") end def test_in_subdir_with_task out, err = assert_rant :fail, "sub3/a" assert out !~ /task sub3/ Rant::Sys.mkdir "sub3" out, err = assert_rant "sub3/a" assert err.empty? assert !out.empty? assert out !~ /task sub3/ assert_file_content "sub3/a", "sub3/a\n" out, err = assert_rant "sub3/a" assert err.empty? assert out.empty? out, err = assert_rant "autoclean" assert !test(?e, "sub3/a") assert test(?d, "sub3") out, err = assert_rant "sub3" assert err.empty? assert_match(/task sub3/, out) ensure Rant::Sys.rm_rf "sub3" end def test_in_subdir_with_task_md5 out, err = assert_rant :fail, "-imd5", "sub3/a" assert out !~ /task sub3/ Rant::Sys.mkdir "sub3" out, err = assert_rant "-imd5", "sub3/a" assert err.empty? assert !out.empty? assert out !~ /task sub3/ assert_file_content "sub3/a", "sub3/a\n" out, err = assert_rant "-imd5", "sub3/a" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "autoclean" assert !test(?e, "sub3/a") assert test(?d, "sub3") out, err = assert_rant "-imd5", "sub3" assert err.empty? assert_match(/task sub3/, out) ensure Rant::Sys.rm_rf "sub3" end def test_ignore_for_sig out, err = assert_rant "x.t", "a=1", "b=2" assert err.empty? assert !out.empty? assert_file_content "x.t", "1\n2\n" out, err = assert_rant "x.t", "a=1", "b=2" assert err.empty? assert out.empty? out, err = assert_rant "x.t", "a=1", "b=3" assert err.empty? assert !out.empty? assert_file_content "x.t", "1\n3\n" out, err = assert_rant "x.t", "a=1", "b=3" assert err.empty? assert out.empty? out, err = assert_rant "x.t", "a=3", "b=3" assert err.empty? assert out.empty? assert_file_content "x.t", "1\n3\n" end def test_proc_var_with_arg out, err = assert_rant "p1.t" assert err.empty? assert !out.empty? assert_file_content "p1.t", "p1.t foo value\n" out, err = assert_rant "p1.t" assert err.empty? assert out.empty? end def test_proc_var_with_arg_md5 out, err = assert_rant "-imd5", "p1.t" assert err.empty? assert !out.empty? assert_file_content "p1.t", "p1.t foo value\n" out, err = assert_rant "-imd5", "p1.t" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "change_foo", "p1.t" assert err.empty? assert !out.empty? assert_file_content "p1.t", "p1.t changed\n" out, err = assert_rant "-imd5", "change_foo", "p1.t" assert err.empty? assert out.empty? end def test_proc_var_without_arg out, err = assert_rant "p2.t", "p3.t" assert err.empty? assert !out.empty? assert_file_content "p2.t", "foo value.\n" assert_file_content "p3.t", "foo value..\n" out, err = assert_rant "p2.t", "p3.t" assert err.empty? assert out.empty? out, err = assert_rant "p2.t", "p3.t", "inc_foo=on" assert err.empty? assert !out.empty? assert_file_content "p2.t", "foo value..\n" assert_file_content "p3.t", "foo value...\n" out, err = assert_rant "p2.t", "p3.t", "inc_foo=on" assert err.empty? assert out.empty? end def test_delayed_var_interpolation out, err = assert_rant "delay.t" assert err.empty? assert !out.empty? assert_file_content "delay.t", "foo value\n" out, err = assert_rant "delay.t" assert err.empty? assert out.empty? out, err = assert_rant "delay.t" assert err.empty? assert out.empty? out, err = assert_rant "change_foo", "delay.t" assert err.empty? assert !out.empty? assert_file_content "delay.t", "changed\n" out, err = assert_rant "change_foo", "delay.t" assert err.empty? assert out.empty? end # will probably change def test_warn_about_hash out, err = assert_rant "hash.t" assert !out.empty? assert err.split(/\n/).size < 3 assert_match(/\[WARNING\].*`h'/, err) assert_match(/\bhash(es)?\b/i, err) assert_file_content "hash.t", "", :strip end end rant-0.5.8/test/import/directedrule/0000755000175000017500000000000010527254520017014 5ustar xavierxavierrant-0.5.8/test/import/directedrule/Rantfile0000644000175000017500000000124510527253221020502 0ustar xavierxavier import %w(directedrule autoclean) task :mk_src => %w(src.t/1.b src.t/2.b src.t/3.b src.t/1.2b) gen Directory, "src.t" %w(src.t/1.b src.t/2.b src.t/3.b src.t/1.2b).each { |f| file f => "src.t" do |t| sys.touch t.name end } file "foo.t" => %w(build.t/1.a build.t/2.a) do |t| t.fail unless test(?f, "build.t/1.a") && test(?f, "build.t/2.a") sys.touch t.name end gen Directory, "build.t" gen Directory, "build2.t" gen Action do make "build.t" end gen DirectedRule, "build2.t" => ["src.t"], '.2a' => '.2b' do |t| sys.touch t.name end ro_tt = gen DirectedRule, "build.t" => ["src.t"], :a => :b do |t| sys.touch t.name end gen AutoClean # vim:ft=ruby rant-0.5.8/test/import/directedrule/test_directedrule.rb0000644000175000017500000000322410527253221023051 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportDrDir ||= File.expand_path(File.dirname(__FILE__)) class TestDirectedRule < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportDrDir) unless Dir.pwd == $testImportDrDir end def teardown assert_rant("autoclean") assert_equal(0, Dir["**/*.t"].size) end def rant(*args) Rant::RantApp.new(*args).run end def test_cmd_target assert_rant assert_rant("build.t/3.a") assert(test(?d, "build.t")) assert(test(?f, "build.t/3.a")) assert(!test(?e, "build.t/1.a")) end def test_dependencies assert_rant assert_rant("foo.t") assert(test(?f, "foo.t")) end def test_build_invoke_dir_task assert_rant assert_rant("build2.t/1.2a") assert(test(?d, "build2.t")) assert(test(?f, "build2.t/1.2a")) end =begin # This would currently be to complex to implement cleanly. def test_invoke_rule_in_subdir FileUtils.mkdir "sub.t" Dir.chdir "sub.t" FileUtils.mkdir "sub.t" open "Rantfile", "w" do |f| f << <<-EOF import "directedrule", "autoclean" gen Directory, "build.t" gen DirectedRule, "build.t" => ["src.t"], :a => :b do |t| sys.touch t.name end gen AutoClean subdirs "sub.t" EOF end open "sub.t/Rantfile", "w" do |f| f << <<-EOF task :a => "build.t/file.a" EOF end assert_rant(:v, "sub.t/a") assert(test(?f, "sub.t/build.t/file.a")) ensure Dir.chdir $testImportDrDir FileUtils.rm_rf "sub.t" end =end end rant-0.5.8/test/import/md5/0000755000175000017500000000000010527254520015026 5ustar xavierxavierrant-0.5.8/test/import/md5/root.rant0000644000175000017500000000020210527253220016665 0ustar xavierxavier import "md5" import "sys/more", "autoclean" gen Rule, :t => :tt do |t| sys.write_to_file t.name, "abc\n" end gen AutoClean rant-0.5.8/test/import/md5/test_md5.rb0000644000175000017500000000247710527253220017105 0ustar xavierxavier require 'test/unit' require 'tutil' $test_import_md5_dir ||= File.expand_path(File.dirname(__FILE__)) class TestImportMd5 < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($test_import_md5_dir) end def teardown Dir.chdir($test_import_md5_dir) Rant::Sys.rm_f Rant::FileList["**/*.rant.meta"] Rant::Sys.rm_rf Rant::FileList["*.t", "*.tt"] end def test_rule_root_and_subdir Rant::Sys.mkdir "sub.td" Rant::Sys.touch "sub.td/a.tt" out, err = assert_rant "sub.td/a.t" assert err.empty? assert_match(/\bwriting\b.*a\.t\b/, out) out, err = assert_rant "sub.td/a.t" assert err.empty? assert out.empty? Dir.chdir "sub.td" out, err = assert_rant "-u", "a.t" assert err.empty? lines = out.split(/\n/) assert(lines.size == 1) assert(out !~ /writing|a\.t/) Dir.chdir $test_import_md5_dir out, err = assert_rant "sub.td/a.t" assert err.empty? assert out.empty? assert_rant "autoclean" assert !test(?e, "sub.td/a.t") assert !test(?e, "sub.td/.rant.meta") assert Dir["**/*.rant.meta"].empty? ensure Dir.chdir $test_import_md5_dir Rant::Sys.rm_rf "sub.td" end end rant-0.5.8/test/import/metadata/0000755000175000017500000000000010527254520016121 5ustar xavierxavierrant-0.5.8/test/import/metadata/sub/0000755000175000017500000000000010527254520016712 5ustar xavierxavierrant-0.5.8/test/import/metadata/sub/Rantfile0000644000175000017500000000077610527253220020407 0ustar xavierxavier gen Task, "b" do |t| t.needed { #puts Dir.pwd #puts rac.current_subdir rv = var[:__metadata__].fetch("cmd", "b") != "create b" #p var[:__metadata__].fetch("cmd", "b") #p var[:__metadata__].instance_variable_get(:@read_dirs) #p var[:__metadata__].instance_variable_get(:@store) rv } t.act { sys.touch t.name var[:__metadata__].set("cmd", "create b", "b") #p var[:__metadata__].instance_variable_get(:@store) } end rant-0.5.8/test/import/metadata/Rantfile0000644000175000017500000000045110527253220017604 0ustar xavierxavier import "metadata" task :dummy p var[:__metadata__].fetch("cmd", "a") var[:__metadata__].set("cmd", "touch a", "a") if var[:subdir] subdirs "sub" rant.goto "sub" p var[:__metadata__].fetch("cmd", "b", "sub") rant.goto "@" else p var[:__metadata__].fetch("cmd", "a") end rant-0.5.8/test/import/metadata/test_metadata.rb0000644000175000017500000000665210527253220021272 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportMetaDataDir ||= File.expand_path(File.dirname(__FILE__)) class TestMetaData < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportMetaDataDir) end def teardown Dir.chdir($testImportMetaDataDir) FileUtils.rm_rf(Dir["*.t"] + %w(.rant.meta sub/.rant.meta sub/b)) end def test_fetch_set_fetch out, err = assert_rant assert(err.empty?) assert_equal("nil\n\"touch a\"\n", out) out, err = assert_rant assert(err.empty?) assert_equal("\"touch a\"\n\"touch a\"\n", out) end def test_subdir out, err = assert_rant("subdir=true", "sub/b") assert(err.empty?) assert_equal("nil\nnil\n(in sub)\ntouch b\n", out) assert(test(?f, ".rant.meta")) assert(test(?f, "sub/.rant.meta")) out, err = assert_rant("subdir=true", "sub/b") assert(err.empty?) assert_equal("\"touch a\"\n\"create b\"\n", out) end def test_rant_import run_import("-q", "--auto", "make.t") assert_exit FileUtils.rm ".rant.meta" out = run_ruby("make.t", "subdir=true", "sub/b") assert_exit assert_equal("nil\nnil\n(in sub)\ntouch b\n", out) assert(test(?f, ".rant.meta")) assert(test(?f, "sub/.rant.meta")) out = run_ruby("make.t", "subdir=true", "sub/b") assert_exit assert_equal("\"touch a\"\n\"create b\"\n", out) end def test_more_commands_and_lines FileUtils.mkdir "more.t" Dir.chdir "more.t" open "Rantfile", "w" do |f| f << <<-EOF import "metadata" task :dummy puts var[:__metadata__].fetch("x", "a") puts var[:__metadata__].fetch("y", "a") puts var[:__metadata__].fetch("x", "b") #STDERR.puts(var[:__metadata__].instance_variable_get(:@store).inspect) var[:__metadata__].set("x", "1\n2\n\n", "a") var[:__metadata__].set("y", "3\n4", "a") var[:__metadata__].set("x", "5", "b") #STDERR.puts(var[:__metadata__].instance_variable_get(:@store).inspect) EOF end out, err = assert_rant assert(err.empty?) assert_equal("nil\nnil\nnil\n", out) assert(test(?f, ".rant.meta")) out, err = assert_rant assert(err.empty?) assert_equal("1\n2\n3\n4\n5\n", out) out, err = assert_rant assert(err.empty?) assert_equal("1\n2\n3\n4\n5\n", out) end def test_write_current_version top_data = < "c2.t" file "f1.t" do |t| write_content(t.name) end rant-0.5.8/test/import/nodes/signed/Rantfile0000644000175000017500000000306710527253220020413 0ustar xavierxavier import "md5", "autoclean" var :content => "1\n" task :a => "f1.t" file "f1.t" do |t| write_content(t.name) end gen Directory, "d1.t" gen Directory, "base.t", "s/s" => ["a.t", "f1.t"] do |t| fn = t.name + "/t" puts "copying to #{fn}" open fn, "w" do |f| f.write(File.read(t.source)) f.write(File.read("f1.t")) end; sys.cd "sub1" end if var["subfile"] import "subfile" gen SubFile, "d2.t/f" => "f1.t" do |t| sys.cp t.source, t.name end end file "f2.t" => ["base.t/s/s"] do |t| sys.cp "#{t.source}/t", t.name end gen SourceNode, "c1.t" gen SourceNode, "c2.t" => ["c1.t", "c3.t"] file "f3.t" => "c1.t" do |t| var[:content] = File.read(t.source) write_content(t.name) end file "f4.t" => ["f3.t", "c2.t"] do |t| var[:content] = t.deps.inject("") { |c, fn| c + File.read(fn) } write_content(t.name) end gen SourceNode, "c4.t" => "c2.t" file "f5.t" => "c2.t" do |t| sys.cp t.source, t.name end gen SourceNode, "c5.t" => ["sub1/c1.t", "c6.t"] file "f6.t" => "c5.t" do |t| write_content(t.name) end gen Rule, ".r.t" => ".t" do |t| sys.cp t.source, t.name end gen Rule, ".r.t" => ".tt" do |t| sys.cp t.source, t.name end gen SourceNode, "c7.t" => ["f1.t", "c8.t"] file "f7.t" => "c7.t" do |t| sys.cp t.source, t.name sys.cd "sub1" end desc "copy f1.t from sub1 to f8.t" file "f8.t" => "sub1/f1.t" do |t| sys.cp t.source, t.name end subdirs "sub1" def write_content(fn) puts "writing #{fn}" open fn, "w" do |f| f.write var[:content] end end gen AutoClean rant-0.5.8/test/import/nodes/signed/test_signed.rb0000644000175000017500000003506710527253220021566 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportNodesSignedDir ||= File.expand_path(File.dirname(__FILE__)) class TestNodesSigned < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportNodesSignedDir) end def teardown Dir.chdir($testImportNodesSignedDir) assert_rant("autoclean") assert(Dir["*.t"].empty?) assert(Dir["*.tt"].empty?) assert(Dir["sub1/*.t"].empty?) assert(Dir["sub1/*.tt"].empty?) assert(!test(?e, ".rant.meta")) end def write(fn, str) open fn, "w" do |f| f.write str end end def test_file out, err = assert_rant("f1.t") assert(err.empty?) assert_equal("writing f1.t\n", out) assert(test(?f, "f1.t")) assert_equal("1\n", File.read("f1.t")) out, err = assert_rant assert(out.empty?) assert(err.empty?) assert(!test(?e, "a")) write("f1.t", "2\n") out, err = assert_rant assert_equal("writing f1.t\n", out) assert(test(?f, "f1.t")) assert_equal("1\n", File.read("f1.t")) out, err = assert_rant assert(out.empty?) assert(err.empty?) end def test_directory out, err = assert_rant("d1.t") assert(test(?d, "d1.t")) out, err = assert_rant("d1.t") assert(err.empty?) assert(out.empty?) end def test_directory_with_pre_and_block write("a.t", "a\n") out, err = assert_rant(:fail, "base.t/s/s") assert(!test(?e, "base.t")) assert_match(/Rantfile[^\n]+14/, err) assert_match(/base\.t/, err) FileUtils.mkdir "base.t" out, err = assert_rant("base.t/s/s") assert(test(?f, "base.t/s/s/t")) assert_equal("a\n1\n", File.read("base.t/s/s/t")) out, err = assert_rant("base.t/s/s") assert(out.empty?) assert(err.empty?) assert_rant("-af1.t", "content=2") assert_equal("2", File.read("f1.t")) out, err = assert_rant("base.t/s/s") assert(test(?f, "base.t/s/s/t")) assert_equal("a\n2", File.read("base.t/s/s/t")) assert(!out.include?("f1.t")) assert(out.include?("copying")) out, err = assert_rant("base.t/s/s") assert(out.empty?) assert(err.empty?) ensure FileUtils.rm_f "a.t" FileUtils.rm_rf "base.t" end def test_subfile out, err = assert_rant("d2.t/f", "subfile=1") assert(err.empty?) assert(test(?f, "f1.t")) assert(test(?f, "d2.t/f")) assert_equal("1\n", File.read("f1.t")) assert_equal("1\n", File.read("d2.t/f")) out, err = assert_rant("d2.t/f", "subfile=1") assert(err.empty?) assert(out.empty?) assert_rant("subfile=1", "autoclean") end def test_file_with_dep_on_dir_with_pre_and_block FileUtils.mkdir "base.t" write("a.t", "a\n") out, err = assert_rant("f2.t") assert(test(?f, "base.t/s/s/t")) assert(test(?f, "f2.t")) assert_equal(File.read("base.t/s/s/t"), File.read("f2.t")) assert(err.empty?) out, err = assert_rant("f2.t") assert(err.empty?) assert(out.empty?) FileUtils.rm "f2.t" out, err = assert_rant("f2.t") assert(err.empty?) assert_equal("cp base.t/s/s/t f2.t\n", out) out, err = assert_rant("f2.t") assert(err.empty?) assert(out.empty?) write("a.t", "aa\n") out, err = assert_rant("f2.t") assert_equal(File.read("base.t/s/s/t"), File.read("f2.t")) assert(err.empty?) assert_match(/\bcp\b/, out) out, err = assert_rant("f2.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_f "a.t" FileUtils.rm_rf "base.t" end def test_source_node_single_fail out, err = assert_rant(:fail, "f3.t") assert(out.empty?) assert(!test(?e, "f3.t")) assert(!test(?e, "c1.t")) assert(!test(?e, ".rant.meta")) lines = err.split(/\n/) assert(lines.size < 5) assert_match(/ERROR.*Rantfile.*34/, lines[0]) assert_match(/SourceNode.*c1\.t/, lines[1]) assert_match(/Task.*f3\.t.*fail/, lines[2]) end def test_source_node_single write("c1.t", "c\n") out, err = assert_rant("f3.t") assert(err.empty?) assert(test(?f, "f3.t")) assert_equal("c\n", File.read("f3.t")) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(!test(?e, "f3.t")) assert(test(?f, "c1.t")) assert_equal("c\n", File.read("c1.t")) out, err = assert_rant("f3.t") assert(err.empty?) assert(test(?f, "f3.t")) assert_equal("c\n", File.read("f3.t")) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) write("c1.t", "c1\n") out, err = assert_rant("f3.t") assert(err.empty?) assert_equal("writing f3.t\n", out) assert_equal("c1\n", File.read("f3.t")) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_f "c1.t" end def test_source_node_fail write("c1.t", "c\n") write("c2.t", "c\n") out, err = assert_rant(:fail, "f4.t") assert(test(?f, "f3.t")) assert_equal("writing f3.t\n", out) lines = err.split(/\n/) assert(lines.size < 5) assert_match(/ERROR.*Rantfile.*36/, lines[0]) assert_match(/SourceNode.*c3\.t/, lines[1]) assert_match(/Task.*f4\.t.*fail/, lines[2]) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_f "c1.t" FileUtils.rm_f "c2.t" end def test_source_node write("c1.t", "c\n") write("c2.t", "c\n") write("c3.t", "c\n") out, err = assert_rant("f4.t") assert(err.empty?) assert(!out.empty?) assert(test(?f, "f3.t")) assert(test(?f, "f4.t")) assert_equal("c\nc\n", File.read("f4.t")) out, err = assert_rant("f4.t") assert(err.empty?) assert(out.empty?) write("c3.t", "c3\n") out, err = assert_rant("f4.t") assert(err.empty?) assert_equal("writing f4.t\n", out) assert_equal("c\nc\n", File.read("f4.t")) out, err = assert_rant("f4.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(test(?f, "c1.t")) assert(test(?f, "c2.t")) assert(test(?f, "c3.t")) assert(!test(?e, "f3.t")) assert(!test(?e, "f4.t")) ensure FileUtils.rm_f "c1.t" FileUtils.rm_f "c2.t" FileUtils.rm_f "c3.t" end def test_2source_node write("c1.t", "c\n") write("c2.t", "c\n") write("c3.t", "c\n") write("c4.t", "c\n") out, err = assert_rant("f5.t") assert(err.empty?) assert(test(?f, "f5.t")) assert_match(/cp.*c2\.t.*f5\.t/, out) assert_equal("c\n", File.read("f5.t")) out, err = assert_rant("f5.t") assert(err.empty?) assert(out.empty?) write("c3.t", "c3\n") out, err = assert_rant("f5.t") assert(err.empty?) assert_match(/cp.*c2\.t.*f5\.t/, out) assert_equal("c\n", File.read("f5.t")) out, err = assert_rant("f5.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(test(?f, "c1.t")) assert(test(?f, "c2.t")) assert(test(?f, "c3.t")) assert(test(?f, "c4.t")) assert(!test(?e, "f5.t")) ensure FileUtils.rm_f "c1.t" FileUtils.rm_f "c2.t" FileUtils.rm_f "c3.t" FileUtils.rm_f "c4.t" end def test_source_node_in_subdir write("sub1/c1.t", "c\n") write("sub1/c2.t", "c\n") write("c5.t", "c\n") write("c6.t", "c\n") out, err = assert_rant("f6.t") assert(err.empty?) assert_equal("writing f6.t\n", out) assert(test(?f, "f6.t")) assert_equal("1\n", File.read("f6.t")) out, err = assert_rant("f6.t") assert(err.empty?) assert(out.empty?) write("sub1/c2.t", "c2\n") out, err = assert_rant("f6.t") assert(err.empty?) assert_equal("writing f6.t\n", out) assert_equal("1\n", File.read("f6.t")) out, err = assert_rant("f6.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(test(?f, "sub1/c1.t")) assert(test(?f, "sub1/c2.t")) assert(test(?f, "c5.t")) assert(test(?f, "c6.t")) assert(!test(?e, "f6.t")) ensure FileUtils.rm_f "sub1/c1.t" FileUtils.rm_f "sub1/c2.t" FileUtils.rm_f "c5.t" FileUtils.rm_f "c6.t" end def test_rule write("sub1/c1.t", "c\n") write("sub1/c2.t", "c\n") write("c5.t", "c\n") write("c6.t", "c\n") out, err = assert_rant("c5.r.t") assert(err.empty?) assert_match(/cp.*c5\.t.*c5\.r\.t\n/, out) assert(test(?f, "c5.r.t")) assert_equal("c\n", File.read("c5.r.t")) out, err = assert_rant("c5.r.t") assert(err.empty?) assert(out.empty?) write("sub1/c2.t", "c2\n") out, err = assert_rant("c5.r.t") assert(err.empty?) assert_match(/cp.*c5\.t.*c5\.r\.t\n/, out) assert_equal("c\n", File.read("c5.r.t")) out, err = assert_rant("c5.r.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(test(?f, "sub1/c1.t")) assert(test(?f, "sub1/c2.t")) assert(test(?f, "c5.t")) assert(test(?f, "c6.t")) assert(!test(?e, "c5.r.t")) ensure FileUtils.rm_f "sub1/c1.t" FileUtils.rm_f "sub1/c2.t" FileUtils.rm_f "c5.t" FileUtils.rm_f "c6.t" end def test_2rule write("1.tt", "t\n") out, err = assert_rant("1.r.t") assert(err.empty?) assert_match(/cp.*1\.tt.*1\.r\.t/, out) assert(test(?f, "1.r.t")) assert_equal("t\n", File.read("1.r.t")) out, err = assert_rant("1.r.t") assert(err.empty?) assert(out.empty?) write("1.tt", "r\n") out, err = assert_rant("1.r.t") assert(err.empty?) assert_match(/cp.*1\.tt.*1\.r\.t/, out) assert(test(?f, "1.r.t")) assert_equal("r\n", File.read("1.r.t")) out, err = assert_rant("1.r.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_f "1.tt" end def test_source_node_with_file_pre write("c7.t", "c\n") write("c8.t", "c\n") out, err = assert_rant("f7.t") assert(err.empty?) assert_match(/cp.*c7\.t.*f7\.t/, out) out, err = assert_rant("f7.t") assert(err.empty?) assert(out.empty?) write("c8.t", "c8\n") out, err = assert_rant("f7.t") assert(err.empty?) assert_match(/cp.*c7\.t.*f7\.t/, out) out, err = assert_rant("f7.t") assert(err.empty?) assert(out.empty?) assert_rant("-af1.t", "content=2") assert_equal("2", File.read("f1.t")) out, err = assert_rant("f7.t") assert(err.empty?) assert_match(/cp.*c7\.t.*f7\.t/, out) out, err = assert_rant("f7.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(test(?f, "c7.t")) assert(test(?f, "c8.t")) assert(!test(?e, "f7.t")) ensure FileUtils.rm_f "c7.t" FileUtils.rm_f "c8.t" end def test_source_node_no_invoke_pre write("c7.t", "c\n") write("c8.t", "c\n") out, err = assert_rant("c7.t") assert(err.empty?) assert(!test(?e, "f1.t"), "SourceNode#invoke shouldn't invoke prerequisites") assert(out.empty?) ensure FileUtils.rm_f "c7.t" FileUtils.rm_f "c8.t" end def test_file_last_pre_in_subdir out, err = assert_rant("f8.t", "content=8") assert(err.empty?) assert(test(?f, "sub1/f1.t")) assert(test(?f, "f8.t")) assert(!test(?e, "f1.t")) assert_equal("8", File.read("sub1/f1.t")) assert_equal("8", File.read("f8.t")) out, err = assert_rant("f8.t") assert(err.empty?) assert(out.empty?) end def test_rant_import_auto out = run_import("-q", "--auto", "make.t") assert_exit FileUtils.mkdir "base.t" write("a.t", "a\n") out = run_ruby("make.t", "--tasks") assert_exit lines = out.split(/\n/) assert_equal(2, lines.size) assert_match(/f1\.t/, lines.first) assert_match(/f8\.t.*copy f1\.t from sub1 to f8\.t/, lines[1]) out = run_ruby("make.t", "f2.t") assert_exit assert(test(?f, "base.t/s/s/t")) assert(test(?f, "f2.t")) assert_equal(File.read("base.t/s/s/t"), File.read("f2.t")) out = run_ruby("make.t", "f2.t") assert_exit assert(out.empty?) run_ruby("make.t", "autoclean") assert_exit assert(test(?f, "a.t")) assert(!test(?e, "base.t/s")) assert(!test(?e, "f2.t")) ensure FileUtils.rm_rf "base.t" FileUtils.rm_f "a.t" FileUtils.rm_f "make.t" end def test_rant_import run_import("-q", "-imd5,autoclean", "make.t") assert_exit write("sub1/c1.t", "c\n") write("sub1/c2.t", "c\n") write("c5.t", "c\n") write("c6.t", "c\n") out = run_ruby("make.t", "f6.t") assert_equal("writing f6.t", out.strip) assert(test(?f, "f6.t")) assert_equal("1\n", File.read("f6.t")) out = run_ruby("make.t", "f6.t") assert(out.strip.empty?) write("sub1/c2.t", "c2\n") out = run_ruby("make.t", "f6.t") assert_equal("writing f6.t", out.strip) assert_equal("1\n", File.read("f6.t")) out = run_ruby("make.t", "f6.t") assert(out.empty?) run_ruby("make.t", "autoclean") assert(test(?f, "sub1/c1.t")) assert(test(?f, "sub1/c2.t")) assert(test(?f, "c5.t")) assert(test(?f, "c6.t")) assert(!test(?e, "f6.t")) ensure FileUtils.rm_f "make.t" FileUtils.rm_f "sub1/c1.t" FileUtils.rm_f "sub1/c2.t" FileUtils.rm_f "c5.t" FileUtils.rm_f "c6.t" end end rant-0.5.8/test/import/package/0000755000175000017500000000000010527254520015734 5ustar xavierxavierrant-0.5.8/test/import/package/deep/0000755000175000017500000000000010527254520016651 5ustar xavierxavierrant-0.5.8/test/import/package/deep/sub/0000755000175000017500000000000010527254520017442 5ustar xavierxavierrant-0.5.8/test/import/package/deep/sub/sub/0000755000175000017500000000000010527254520020233 5ustar xavierxavierrant-0.5.8/test/import/package/deep/sub/sub/f10000644000175000017500000000002010527253221020451 0ustar xavierxavierdeep/sub/sub/f1 rant-0.5.8/test/import/package/sub/0000755000175000017500000000000010527254520016525 5ustar xavierxavierrant-0.5.8/test/import/package/sub/f10000644000175000017500000000000710527253221016750 0ustar xavierxaviersub/f1 rant-0.5.8/test/import/package/sub2/0000755000175000017500000000000010527254520016607 5ustar xavierxavierrant-0.5.8/test/import/package/sub2/f10000644000175000017500000000001010527253221017024 0ustar xavierxaviersub2/f1 rant-0.5.8/test/import/package/MANIFEST0000644000175000017500000000004110527253221017055 0ustar xavierxavierRantfile sub/f1 sub2/f1 MANIFEST rant-0.5.8/test/import/package/Rantfile0000644000175000017500000000250010527253221017415 0ustar xavierxavier import %w(package/tgz package/zip autoclean) gen Archive::Tgz, "t1", :manifest => "MANIFEST" desc "Create t1.zip" gen Archive::Zip, "t1", :manifest => "MANIFEST" desc "Create t2.tgz" gen Archive::Tgz, "t2", :files => sys["sub*/f?"], :manifest => "m2.tgz.t" gen Archive::Zip, "t2", :files => sys["sub*/f?"], :manifest => "m2.zip.t" gen Archive::Tgz, "t3", :files => %w(Rantfile sub/f1) gen Archive::Zip, "t3", :files => sys["Rantfile", "sub/f1"] gen Archive::Tgz, "pkg.t/t4", :manifest, :version => "1.0.0" gen Archive::Zip, "zip.t", "t4", :manifest, :version => "1.0.0" gen Package::Tgz, "pkg2.t", :manifest gen Package::Zip, "pkg.t/pkg", :manifest => "CONTENTS", :files => %w(deep/sub/sub/f1) gen Package::Tgz, "sub", "pkg.t/pkg", :manifest, :version => "0.1", :extension => ".tar.gz" gen Package::Tgz, "sub.t/", "pkg", :files => %w(sub/f1) gen Package::Tgz, "t5", :manifest => "mf5.t", :files => %w(Rantfile mf5.t) gen Package::Tgz, "t6", :files => %w(sub6.t) gen Package::Zip, "t6", :files => %w(sub6.t) gen Archive::Tgz, "t7", :files => %w(sub7.t) gen Archive::Zip, "t7", :files => %w(sub7.t) gen Archive::Tgz, "sym", :files => sys["subs.t/**/*"] gen Archive::Zip, "sym", :files => sys["subs.t/**/*"] gen Package::Tgz, "pkg.t/double", :files => %w(./Rantfile Rantfile) gen AutoClean rant-0.5.8/test/import/package/md5.rf0000644000175000017500000000034310527253221016747 0ustar xavierxavier import %w(md5 package/zip package/tgz autoclean) gen Package::Zip, "pkg.t/m1", :files => sys["sub*/*"].shun("pkg.t") gen Package::Tgz, "pkg.t/m1", :files => sys["sub*/*"].shun("pkg.t") gen AutoClean, "clean" # vim:ft=ruby rant-0.5.8/test/import/package/test_package.rb0000644000175000017500000004611610527253221020720 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import/sys/tgz' $testIPackageDir ||= File.expand_path(File.dirname(__FILE__)) class TestImportPackage < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir $testIPackageDir @pkg_dir = nil @contents = {} end def teardown assert_rant("autoclean") Dir["*.{tgz,zip,t}"].each { |f| assert(false, "#{f} should be removed by AutoClean") } end def check_contents(atype, archive, files, dirs = [], manifest_file = nil) old_pwd = Dir.pwd FileUtils.mkdir "u.t" FileUtils.cp archive, "u.t" FileUtils.cd "u.t" archive = File.basename archive case atype when :tgz: Rant::Sys.unpack_tgz(archive) when :zip: Rant::Sys.unpack_zip(archive) else raise "unknown archive type -- #{atype}" end if @pkg_dir assert(test(?d, @pkg_dir)) FileUtils.cd @pkg_dir end files.each { |f| assert(test(?f, f), "file #{f} is missing in archive") content = @contents[f] if content assert_equal(content, File.read(f)) end } dirs.each { |f| assert(test(?d, f), "dir #{f} is missing in archive") } count = files.size + dirs.size # + 1 because of the archive file count += 1 unless @pkg_dir assert_equal(count, Rant::FileList.glob_all("**/*").size) if manifest_file check_manifest(manifest_file, files) end yield if block_given? ensure FileUtils.cd old_pwd FileUtils.rm_r "u.t" end def check_manifest(file, entries) assert(test(?f, file)) m_entries = IO.read(file).split("\n") assert_equal(entries.size, m_entries.size) entries.each { |f| assert(m_entries.include?(f), "#{f} missing in manifest") } end def test_tgz_from_manifest assert_rant mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) check_contents(:tgz, "t1.tgz", mf, dirs, "MANIFEST") end def test_tgz_sync_manifest assert_rant("t2.tgz") mf = %w(sub/f1 sub2/f1 m2.tgz.t) dirs = %w(sub sub2) check_manifest("m2.tgz.t", mf) check_contents(:tgz, "t2.tgz", mf, dirs, "m2.tgz.t") out, err = assert_rant("t2.tgz") assert(out.strip.empty?) #assert(err.strip.empty?) FileUtils.touch "sub/f5" out, err = assert_rant("t2.tgz") assert_match(/writing m2\.tgz\.t.*\n.*tar/m, out) check_contents(:tgz, "t2.tgz", mf + %w(sub/f5), dirs, "m2.tgz.t") timeout FileUtils.rm "sub/f5" out, err = assert_rant("t2.tgz") assert_match(/writing m2\.tgz\.t.*\n.*tar/m, out) check_contents(:tgz, "t2.tgz", mf, dirs, "m2.tgz.t") # test autoclean assert_rant("autoclean") assert(!test(?e, "m2.tgz.t")) # hmm.. the tgz will be removed by check_contents anyway... assert(!test(?e, "t2.tgz")) ensure FileUtils.rm_rf "sub/f5" end def test_tgz_sync_manifest_md5 assert_rant("-imd5", "t2.tgz") mf = %w(sub/f1 sub2/f1 m2.tgz.t) dirs = %w(sub sub2) check_manifest("m2.tgz.t", mf) check_contents(:tgz, "t2.tgz", mf, dirs, "m2.tgz.t") out, err = assert_rant("t2.tgz") assert(out.strip.empty?) assert(err.strip.empty?) FileUtils.touch "sub/f5" out, err = assert_rant("-imd5", "t2.tgz") assert_match(/writing m2\.tgz\.t.*\n.*tar/m, out) check_contents(:tgz, "t2.tgz", mf + %w(sub/f5), dirs, "m2.tgz.t") FileUtils.rm "sub/f5" out, err = assert_rant("-imd5", "t2.tgz") assert_match(/writing m2\.tgz\.t.*\n.*tar/m, out) check_contents(:tgz, "t2.tgz", mf, dirs, "m2.tgz.t") # test autoclean assert_rant("-imd5", "autoclean") assert(!test(?e, "m2.tgz.t")) # hmm.. the tgz will be removed by check_contents anyway... assert(!test(?e, "t2.tgz")) assert(!test(?e, ".rant.meta")) ensure FileUtils.rm_rf "sub/f5" end def test_tgz_files_array assert_rant("t3.tgz") mf = %w(Rantfile sub/f1) dirs = %w(sub) check_contents(:tgz, "t3.tgz", mf, dirs) end def test_tgz_version_and_dir assert_rant("pkg.t/t4-1.0.0.tgz") assert(test(?d, "pkg.t")) mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) check_contents(:tgz, "pkg.t/t4-1.0.0.tgz", mf, dirs, "MANIFEST") ensure FileUtils.rm_rf "pkg.t" end def test_tgz_package_manifest assert(!test(?e, "pkg2.t")) assert_rant("pkg2.t.tgz") assert(?d, "pkg2.t") mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) @pkg_dir = "pkg2.t" check_contents(:tgz, "pkg2.t.tgz", mf, dirs, "MANIFEST") assert(test(?d, "pkg2.t")) assert_rant("autoclean") assert(!test(?e, "pkg2.t")) end def test_tgz_package_basedir_manifest_extension assert_rant("sub/pkg.t/pkg-0.1.tar.gz") assert(test(?f, "sub/pkg.t/pkg-0.1.tar.gz")) mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) @pkg_dir = "pkg-0.1" check_contents(:tgz, "sub/pkg.t/pkg-0.1.tar.gz", mf, dirs, "MANIFEST") assert(test(?f, "sub/f1")) assert_rant("autoclean") assert(!test(?e, "sub/pkg.t-0.1")) assert(test(?d, "sub")) assert(test(?f, "sub/f1")) end def test_tgz_package_basedir_with_slash assert(!test(?d, "sub.t")) assert_rant(:fail, "sub.t/pkg.tgz") assert(!test(?d, "sub.t")) FileUtils.mkdir "sub.t" FileUtils.touch "sub.t/a.t" out, err = assert_rant("sub.t/pkg.tgz") assert(!out.empty?) out, err = assert_rant("sub.t/pkg.tgz") assert(out.strip.empty?) assert(test(?d, "sub.t/pkg")) @pkg_dir = "pkg" mf = %w(sub/f1) dirs = %w(sub) check_contents(:tgz, "sub.t/pkg.tgz", mf, dirs) assert_rant("autoclean") assert(!test(?e, "sub.t/pkg.tgz")) assert(!test(?e, "sub.t/pkg")) assert(test(?d, "sub.t")) assert(test(?f, "sub.t/a.t")) ensure FileUtils.rm_rf "sub.t" end def test_tgz_import_archive open "rf.t", "w" do |f| f << <<-EOF import "archive/tgz", "autoclean" gen Archive::Tgz, "rf", :files => sys["deep/sub/sub/f1"] gen AutoClean EOF end assert_rant("-frf.t") mf = %w(deep/sub/sub/f1) dirs = %w(deep deep/sub deep/sub/sub) check_contents(:tgz, "rf.tgz", mf, dirs) assert(test(?f, "rf.tgz")) assert_rant("-frf.t", "autoclean") assert(!test(?e, "rf.tgz")) run_import("-frf.t", "-q", "--auto", "ant.t") assert_equal(0, $?.exitstatus) out = run_ruby("ant.t", "-frf.t") assert(!out.empty?) out = run_ruby("ant.t", "-frf.t") assert(out.empty?) check_contents(:tgz, "rf.tgz", mf, dirs) assert(test(?f, "rf.tgz")) assert_rant("-frf.t", "autoclean") assert(!test(?e, "rf.tgz")) ensure FileUtils.rm_rf %w(rf.t ant.t) end def test_tgz_package_empty_dir FileUtils.mkdir "sub6.t" assert_rant("t6.tgz") @pkg_dir = "t6" mf = %w() dirs = %w(sub6.t) check_contents(:tgz, "t6.tgz", mf, dirs) ensure FileUtils.rm_rf %w(sub6.t) end def test_tgz_files_manifest_desc out, err = assert_rant("--tasks") assert_match(/rant\s+t2\.tgz\s+#\s+Create t2\.tgz/, out) end def test_tgz_package_files_contains_manifest assert_rant("t5.tgz") @pkg_dir = "t5" mf = %w(Rantfile mf5.t) dirs = %w() check_contents(:tgz, "t5.tgz", mf, dirs, "mf5.t") assert_rant("autoclean") assert(!test(?e, "t5")) end def test_tgz_package_non_recursive FileUtils.mkdir "sub6.t" FileUtils.touch "sub6.t/.t" FileUtils.touch "sub6.t/a.t" assert_rant("t6.tgz") @pkg_dir = "t6" mf = %w() dirs = %w(sub6.t) check_contents(:tgz, "t6.tgz", mf, dirs) assert_rant("autoclean") assert(!test(?e, "t6")) ensure FileUtils.rm_rf "sub6.t" end def test_tgz_non_recursive FileUtils.mkdir "sub7.t" FileUtils.touch "sub7.t/a" assert_rant("t7.tgz") mf = %w() dirs = %w(sub7.t) check_contents(:tgz, "t7.tgz", mf, dirs) ensure FileUtils.rm_rf "sub7.t" end def test_tgz_follow_symlink have_symlinks = true FileUtils.mkdir "subs.t" open "target.t", "w" do |f| f.print "symlink test target file" end begin File.symlink "../target.t", "subs.t/symlink" rescue NotImplementedError have_symlinks = false end if have_symlinks assert(File.symlink?("subs.t/symlink")) assert(File.exist?("subs.t/symlink")) assert_rant("sym.tgz") mf = %w(subs.t/symlink) dirs = %w(subs.t) @contents["subs.t/symlink"] = "symlink test target file" check_contents(:tgz, "sym.tgz", mf, dirs) else STDERR.puts "*** platform doesn't support symbolic links ***" end ensure FileUtils.rm_f %w(target.t sym.tgz) FileUtils.rm_rf "subs.t" end def test_tgz_package_follow_symlink_dir have_symlinks = true FileUtils.mkdir "subs.t" begin File.symlink "subs.t", "sub6.t" rescue NotImplementedError have_symlinks = false end if have_symlinks assert_rant("t6.tgz") @pkg_dir = "t6" mf = %w() dirs = %w(sub6.t) check_contents(:tgz, "t6.tgz", mf, dirs) end ensure FileUtils.rm_rf %w(subs.t sub6.t) end def test_tgz_package_double assert_rant("pkg.t/double.tgz") out, err = assert_rant("pkg.t/double.tgz") assert(out.empty?) assert(err.empty?) mf = %w(Rantfile) dirs = %w() @pkg_dir = "double" check_contents(:tgz, "pkg.t/double.tgz", mf, dirs) end def test_zip_follow_symlink have_symlinks = true FileUtils.mkdir "subs.t" open "target.t", "w" do |f| f.print "symlink test target file" end begin File.symlink "../target.t", "subs.t/symlink" rescue NotImplementedError have_symlinks = false end if have_symlinks assert(File.symlink?("subs.t/symlink")) assert(File.exist?("subs.t/symlink")) assert_rant("sym.zip") mf = %w(subs.t/symlink) dirs = %w(subs.t) @contents["subs.t/symlink"] = "symlink test target file" check_contents(:zip, "sym.zip", mf, dirs) else STDERR.puts "*** platform doesn't support symbolic links ***" end ensure FileUtils.rm_f %w(target.t sym.zip) FileUtils.rm_rf "subs.t" end def test_zip_package_follow_symlink_dir have_symlinks = true FileUtils.mkdir "subs.t" begin File.symlink "subs.t", "sub6.t" rescue NotImplementedError have_symlinks = false end if have_symlinks assert_rant("t6.zip") @pkg_dir = "t6" mf = %w() dirs = %w(sub6.t) check_contents(:zip, "t6.zip", mf, dirs) end ensure FileUtils.rm_rf %w(subs.t sub6.t) end def test_zip_non_recursive FileUtils.mkdir "sub7.t" FileUtils.touch "sub7.t/a" assert_rant("t7.zip") mf = %w() dirs = %w(sub7.t) check_contents(:zip, "t7.zip", mf, dirs) ensure FileUtils.rm_rf "sub7.t" end def test_zip_package_non_recursive FileUtils.mkdir "sub6.t" FileUtils.touch "sub6.t/.t" FileUtils.touch "sub6.t/a.t" assert_rant("t6.zip") @pkg_dir = "t6" mf = %w() dirs = %w(sub6.t) check_contents(:zip, "t6.zip", mf, dirs) assert_rant("autoclean") assert(!test(?e, "t6")) ensure FileUtils.rm_rf "sub6.t" end def test_zip_package_empty_dir FileUtils.mkdir "sub6.t" assert_rant("t6.zip") @pkg_dir = "t6" mf = %w() dirs = %w(sub6.t) check_contents(:zip, "t6.zip", mf, dirs) ensure FileUtils.rm_rf %w(sub6.t) end def test_zip_manifest_desc out, err = assert_rant("--tasks") assert_match(/rant\s+t1\.zip\s+#\s+Create t1\.zip/, out) end def test_zip_rant_import run_import("-q", "--auto", "ant.t") assert_equal(0, $?.exitstatus) assert(test(?f, "ant.t")) out = run_ruby("ant.t", "pkg.t/pkg.zip") assert_equal(0, $?.exitstatus) assert(!out.empty?) out = run_ruby("ant.t", "pkg.t/pkg.zip") assert(out.empty?) mf = %w(deep/sub/sub/f1 CONTENTS) dirs = %w(deep deep/sub deep/sub/sub) @pkg_dir = "pkg" check_contents(:zip, "pkg.t/pkg.zip", mf, dirs, "CONTENTS") assert(test(?f, "CONTENTS")) run_ruby("ant.t", "autoclean") assert(!test(?f, "CONTENTS")) ensure FileUtils.rm_f "ant.t" end def test_zip_package_write_manifest assert(!test(?f, "CONTENTS")) assert_rant(:x, "pkg.t/pkg.zip") assert(test(?f, "CONTENTS")) mf = %w(deep/sub/sub/f1 CONTENTS) dirs = %w(deep deep/sub deep/sub/sub) @pkg_dir = "pkg" check_contents(:zip, "pkg.t/pkg.zip", mf, dirs, "CONTENTS") assert(test(?f, "CONTENTS")) assert_rant("autoclean") assert(!test(?f, "CONTENTS")) end def test_zip_with_basedir assert_rant(:fail, "zip.t/t4-1.0.0.zip") assert(!test(?d, "zip.t")) FileUtils.mkdir "zip.t" assert_rant(:x, "zip.t/t4-1.0.0.zip") mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) check_contents(:zip, "zip.t/t4-1.0.0.zip", mf, dirs, "MANIFEST") assert_rant("autoclean") assert(test(?d, "zip.t")) assert(!test(?e, "zip.t/t4-1.0.0.zip")) ensure FileUtils.rm_rf "zip.t" end def test_zip_with_basedir_md5 assert_rant(:fail, "-imd5", "zip.t/t4-1.0.0.zip") assert(!test(?d, "zip.t")) FileUtils.mkdir "zip.t" assert_rant(:x, "-imd5", "zip.t/t4-1.0.0.zip") mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) check_contents(:zip, "zip.t/t4-1.0.0.zip", mf, dirs, "MANIFEST") out, err = assert_rant("-imd5", "zip.t/t4-1.0.0.zip") assert(err.empty?) assert(out.empty?) assert_rant("-imd5", "autoclean") assert(test(?d, "zip.t")) assert(!test(?e, ".rant.meta")) assert(!test(?e, "zip.t/t4-1.0.0.zip")) ensure FileUtils.rm_rf "zip.t" end def test_zip_from_manifest assert_rant(:x, "t1.zip") mf = %w(Rantfile sub/f1 sub2/f1 MANIFEST) dirs = %w(sub sub2) check_contents(:zip, "t1.zip", mf, dirs, "MANIFEST") end def test_zip_sync_manifest assert_rant(:x, "t2.zip") mf = %w(sub/f1 sub2/f1 m2.zip.t) dirs = %w(sub sub2) check_manifest("m2.zip.t", mf) check_contents(:zip, "t2.zip", mf, dirs, "m2.zip.t") ensure FileUtils.rm_f "m2.zip.t" end def test_zip_filelist assert_rant(:x, "t3.zip") mf = %w(Rantfile sub/f1) dirs = %w(sub) check_contents(:zip, "t3.zip", mf, dirs) end def write(fn, str) open fn, "w" do |f| f.write str end end def test_md5_zip_package write("sub/pkg.t", "abc\n") write("sub2/a.t", "a\n") out, err = assert_rant("-fmd5.rf", "pkg.t/m1.zip") assert(err.empty?) mf = %w(sub/f1 sub2/f1 sub2/a.t) dirs = %w(sub sub2) @pkg_dir = "m1" check_contents(:zip, "pkg.t/m1.zip", mf, dirs) FileUtils.rm "sub/pkg.t" out, err = assert_rant("-fmd5.rf", "pkg.t/m1.zip") assert(err.empty?) assert(out.empty?) FileUtils.rm "sub2/a.t" out, err = assert_rant("-fmd5.rf", "pkg.t/m1.zip") assert(err.empty?) assert(!out.empty?) mf = %w(sub/f1 sub2/f1) dirs = %w(sub sub2) @pkg_dir = "m1" check_contents(:zip, "pkg.t/m1.zip", mf, dirs) out, err = assert_rant("-fmd5.rf", "pkg.t/m1.zip") assert(err.empty?) assert(out.empty?) assert_rant("-fmd5.rf", "clean") assert(!test(?e, "pkg.t")) assert(!test(?e, ".rant.meta")) ensure FileUtils.rm_f %w(.rant.meta sub1/pkg.t sub2/a.t) end def test_md5_tgz_package write("sub/pkg.t", "abc\n") write("sub2/a.t", "a\n") out, err = assert_rant("-fmd5.rf", "pkg.t/m1.tgz") assert(err.empty?) mf = %w(sub/f1 sub2/f1 sub2/a.t) dirs = %w(sub sub2) @pkg_dir = "m1" check_contents(:tgz, "pkg.t/m1.tgz", mf, dirs) FileUtils.rm "sub/pkg.t" out, err = assert_rant("-fmd5.rf", "pkg.t/m1.tgz") assert(err.empty?) assert(out.empty?) FileUtils.rm "sub2/a.t" out, err = assert_rant("-fmd5.rf", "pkg.t/m1.tgz") assert(err.empty?) assert(!out.empty?) mf = %w(sub/f1 sub2/f1) dirs = %w(sub sub2) @pkg_dir = "m1" check_contents(:tgz, "pkg.t/m1.tgz", mf, dirs) out, err = assert_rant("-fmd5.rf", "pkg.t/m1.tgz") assert(err.empty?) assert(out.empty?) assert_rant("-fmd5.rf", "clean") assert(!test(?e, "pkg.t")) assert(!test(?e, ".rant.meta")) ensure FileUtils.rm_f %w(.rant.meta sub1/pkg.t sub2/a.t) end def test_rant_import_md5_empty_archive_tgz write("empty.rf", <<-EOF) import "md5", "archive/tgz" gen Archive::Tgz, "empty.t", :files => [] EOF run_import("-q", "-fempty.rf", "--auto", "make.t") assert_exit out = run_ruby("make.t", "-fempty.rf", "empty.t.tgz") assert_exit mf = %w() dirs = %w() check_contents(:tgz, "empty.t.tgz", mf, dirs) out = run_ruby("make.t", "-fempty.rf", "empty.t.tgz") assert(out.empty?) ensure FileUtils.rm_f %w(make.t empty.rf empty.t.tgz) end def test_package_tgz_flag_manifest_opt_files in_local_temp_dir do write_to_file "root.rant", <<-EOF import "md5", "package/tgz", "autoclean" gen Package::Tgz, "a-b", :manifest, :files => sys["*"].exclude("u", "*.tgz") gen AutoClean EOF write_to_file "a", "a\n" out, err = assert_rant "a-b.tgz" assert err.empty? assert !out.empty? assert(test(?f, "a-b.tgz")) assert(test(?f, "MANIFEST")) Rant::Sys.unpack_tgz "a-b.tgz", :in => "u" assert_nothing_raised do assert Rant::Sys.compare_file("root.rant", "u/a-b/root.rant") assert_equal "a\n", File.read("u/a-b/a") entries = File.read("u/a-b/MANIFEST").split(/\n/) assert_equal 3, entries.size assert entries.include?("MANIFEST") assert entries.include?("a") assert entries.include?("root.rant") end out, err = assert_rant "a-b.tgz" assert err.empty? assert out.empty? assert_rant "autoclean" assert !test(?e, "a-b.tgz") assert !test(?e, "a-b") assert !test(?e, ".rant.meta") end end def test_package_zip_exclude_package_dir in_local_temp_dir do write_to_file "root.rant", <<-EOF import "md5", "package/zip", "autoclean" gen Package::Zip, "pkg", :files => sys["**/*.t"] gen AutoClean EOF write_to_file "a.t", "a\n" Rant::Sys.mkdir "dir" write_to_file "dir/a.t", "dir_a\n" write_to_file "pkg.t", "pkg\n" out, err = assert_rant "pkg.zip" assert err.empty? assert !out.empty? mf = %w(a.t dir/a.t pkg.t) dirs = %w(dir) @pkg_dir = "pkg" check_contents(:zip, "pkg.zip", mf, dirs) out, err = assert_rant "pkg.zip" check_contents(:zip, "pkg.zip", mf, dirs) assert err.empty? assert out.empty? assert_rant "autoclean" assert Rant::FileList["**/*.zip"].empty? assert !test(?e, ".rant.meta") end end end rant-0.5.8/test/import/signeddirectory/0000755000175000017500000000000010527254520017537 5ustar xavierxavierrant-0.5.8/test/import/signeddirectory/Rantfile0000644000175000017500000000042110527253221021220 0ustar xavierxavier import "signedfile", "autoclean" gen SignedDirectory, "d1.t" gen SignedDirectory, "d2.t" do |t| sys.touch "#{t.name}/a" end gen SignedDirectory, "d3.t" => sys["{a,b,c}.t"] do |t| sys.rm_f sys["#{t.name}/*"] sys.cp t.prerequisites, t.name end gen AutoClean rant-0.5.8/test/import/signeddirectory/test_signeddirectory.rb0000644000175000017500000000471610527253221024326 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportSignedDirectoryDir ||= File.expand_path(File.dirname(__FILE__)) class TestSignedDirectory < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportSignedDirectoryDir) end def teardown Dir.chdir($testImportSignedDirectoryDir) assert_rant("autoclean") assert(Dir["*.t"].empty?) assert(!test(?e, ".rant.meta")) end def write(fn, str) open fn, "w" do |f| f.write str end end def test_plain_dir out, err = assert_rant assert(err.empty?) assert(!out.include?("touch")) assert(test(?d, "d1.t")) out, err = assert_rant assert(err.empty?) assert(out.empty?) end def test_with_block out, err = assert_rant("d2.t") assert(test(?d, "d2.t")) assert(test(?f, "d2.t/a")) assert(err.empty?) out, err = assert_rant("d2.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("d2.t") assert(err.empty?) assert(out.empty?) end def test_block_and_plain_file_deps write("a.t", "a\n") out, err = assert_rant("d3.t") assert(test(?f, "d3.t/a.t")) assert_equal("a\n", File.read("d3.t/a.t")) assert(err.empty?) out, err = assert_rant("d3.t") assert(err.empty?) assert(out.empty?) write("b.t", "b\n") out, err = assert_rant("d3.t") assert(test(?f, "d3.t/a.t")) assert(test(?f, "d3.t/b.t")) assert_equal("a\n", File.read("d3.t/a.t")) assert_equal("b\n", File.read("d3.t/b.t")) assert(err.empty?) out, err = assert_rant("d3.t") assert(err.empty?) assert(out.empty?) write("b.t", "bb\n") out, err = assert_rant("d3.t") assert(test(?f, "d3.t/a.t")) assert(test(?f, "d3.t/b.t")) assert_equal("a\n", File.read("d3.t/a.t")) assert_equal("bb\n", File.read("d3.t/b.t")) assert(err.empty?) out, err = assert_rant("d3.t") assert(err.empty?) assert(out.empty?) FileUtils.rm "a.t" out, err = assert_rant("d3.t") assert(test(?f, "d3.t/b.t")) assert_equal("bb\n", File.read("d3.t/b.t")) assert(err.empty?) out, err = assert_rant("d3.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_f Dir["{a,b,c}.t"] end end rant-0.5.8/test/import/signedfile/0000755000175000017500000000000010527254520016452 5ustar xavierxavierrant-0.5.8/test/import/signedfile/sub1/0000755000175000017500000000000010527254520017324 5ustar xavierxavierrant-0.5.8/test/import/signedfile/sub1/Rantfile0000644000175000017500000000011010527253220020777 0ustar xavierxavier gen SignedFile, "s1.t" => "@f2.t" do |t| write_content(t.name) end rant-0.5.8/test/import/signedfile/Rantfile0000644000175000017500000000303610527253220020137 0ustar xavierxavier import "signedfile", "autoclean" var :content => "1\n" gen SignedFile, "f1.t" gen SignedFile, "f2.t" do |t| write_content(t.name) end desc "create f3.t" gen SignedFile, "f3.t" => "f2.t" do |t| write_content(t.name) end gen SignedFile, "f4.t" => ["f3.t"] do |t| write_content(t.name) end file "f5.t" => "f2.t" do |t| write_content(t.name) end gen SignedFile, "f6.t" => "f5.t" do |t| write_content(t.name) end gen SignedFile, "f7.t" => ["a.t", "f2.t", "b.t"] do |t| write_content(t.name) end gen SignedFile, "f8.t" => "f1.t" do |t| write_content(t.name) end gen SignedFile, "f9.t" => "f1.t" do |t| t.fail "need #{t.source}" unless test(?f, t.source) write_content(t.name) end gen SignedFile, "f10.t" do |t| puts "should create f10.t" end gen SignedFile, "f11.t" => ["sub1/s1.t", "f12.t"] do |t| write_content(t.name) end gen SignedFile, "f12.t" do |t| write_content(t.name) end gen Directory, "d1.t" gen SignedFile, "d1.t/f13.t" => "d1.t" do |t| write_content(t.name) end var :dn2 => "d2.t" gen SignedFile, "#{var :dn2}/f14.t" => var[:dn2] do |t| write_content(t.name) end gen SignedFile, "f15.t" => "f15.t" do |t| write_content(t.name) end gen SignedFile, "f16.t" => "f17.t" do |t| write_content(t.name) end gen SignedFile, "f17.t" => "f16.t" do |t| write_content(t.name) end gen SignedFile, "f18.t" do |t| sys.mkdir t.name end subdirs "sub1" gen AutoClean def write_content(fn) puts "writing #{fn}" open fn, "w" do |f| f.write(var[:content]) end end rant-0.5.8/test/import/signedfile/test_signedfile.rb0000644000175000017500000002572010527253220022151 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportSignedFileDir ||= File.expand_path(File.dirname(__FILE__)) class TestSignedFile < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportSignedFileDir) end def teardown Dir.chdir($testImportSignedFileDir) assert_rant("autoclean") assert(Dir["*.t"].empty?) assert(Dir["sub1/*.t"].empty?) assert(!test(?e, ".rant.meta")) assert(!test(?e, "sub1/.rant.meta")) end def write(fn, str) open fn, "w" do |f| f.write str end end def test_no_pre_no_action assert_rant assert(!test(?f, "f1.t")) assert_rant assert(!test(?f, "f1.t")) end def test_no_pre out, err = assert_rant("f2.t") assert(err.empty?) assert_match(/writing/, out) assert(test(?f, "f2.t")) assert_equal("1\n", File.read("f2.t")) out, err = assert_rant("f2.t") assert(err.empty?) assert(out.empty?) write("f2.t", "2\n") out, err = assert_rant("f2.t") assert(err.empty?) assert_match(/writing/, out) assert_equal("1\n", File.read("f2.t")) out, err = assert_rant("f2.t") assert(err.empty?) assert(out.empty?) end def test_one_pre out, err = assert_rant("f3.t") assert(test(?f, "f2.t")) assert(test(?f, "f3.t")) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("f3.t")) assert_match(/writing f2\.t\nwriting f3\.t/, out) assert(err.empty?) FileUtils.rm "f2.t" out, err = assert_rant("f3.t") assert(test(?f, "f2.t")) assert(test(?f, "f3.t")) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("f3.t")) assert_match(/\Awriting f2\.t\n\Z/, out) assert(!out.include?("f3")) # OK, redundant FileUtils.rm "f2.t" assert_rant("f2.t", "content=2") assert(test(?f, "f2.t")) assert_equal("2", File.read("f2.t")) out, err = assert_rant("f3.t") assert_equal("2", File.read("f2.t")) assert_equal("1\n", File.read("f3.t")) assert(err.empty?) assert_match(/\Awriting f3\.t\n\Z/, out) assert(!out.include?("f2")) # OK, redundant out, err = assert_rant("f3.t") assert(out.empty?) end def test_one_pre_list out, err = assert_rant("f4.t") assert(test(?f, "f2.t")) assert(test(?f, "f3.t")) assert(test(?f, "f4.t")) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("f3.t")) assert_equal("1\n", File.read("f4.t")) assert(err.empty?) assert_equal("writing f2.t\nwriting f3.t\nwriting f4.t\n", out) out, err = assert_rant("f4.t") assert(err.empty?) assert(out.empty?) assert_rant("autoclean") assert(!test(?e, "f2.t")) assert(!test(?e, "f3.t")) assert(!test(?e, "f4.t")) end def test_opt_tasks out, err = assert_rant("--tasks") assert(err.empty?) lines = out.split(/\n/) assert_equal(2, lines.size) assert_match(/rant\s+#.*f1\.t/, lines[0]) assert_match(/rant\s+f3\.t\s+#\s+create f3\.t\b/, lines[1]) assert(!test(?e, ".rant.meta")) assert(Dir["*.t"].empty?) end def test_file_mtime_with_signed_dep out, err = assert_rant("f5.t") assert(err.empty?) assert(test(?f, "f2.t")) assert(test(?f, "f5.t")) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("f5.t")) out, err = assert_rant("f5.t") assert(err.empty?) assert(out.empty?) timeout FileUtils.touch "f2.t" out, err = assert_rant("f5.t") assert(err.empty?) assert_match(/\Awriting f5\.t\n\Z/, out) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("f5.t")) out, err = assert_rant("f5.t") assert(err.empty?) assert(out.empty?) end def test_with_file_mtime_dep out, err = assert_rant("f6.t") assert(test(?f, "f2.t")) assert(test(?f, "f5.t")) assert(test(?f, "f6.t")) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("f5.t")) assert_equal("1\n", File.read("f6.t")) out, err = assert_rant("f6.t") assert(err.empty?) assert(out.empty?) =begin # could be optimized to work timeout FileUtils.touch "f2.t" out, err = assert_rant("f6.t") assert(err.empty?) lines = out.split(/\n/) p lines assert_equal(1, lines.size) assert_equal("writing f5.t", lines.first) =end end def test_file_dep_signed_dep_file_dep out, err = assert_rant(:fail, "f7.t") assert(out.empty?) lines = err.split(/\n/) assert_equal(4, lines.size) assert_match(/\[ERROR\].*Rantfile.*29/, lines[0]) assert_match(/no.*a\.t/, lines[1]) assert_match(/f7\.t.*fail/, lines[2]) write("a.t", "a\n") out, err = assert_rant(:fail, "f7.t") assert_equal("writing f2.t\n", out) lines = err.split(/\n/) assert_equal(4, lines.size) assert_match(/\[ERROR\].*Rantfile.*29/, lines[0]) assert_match(/no.*b\.t/, lines[1]) assert_match(/f7\.t.*fail/, lines[2]) write("b.t", "b\n") out, err = assert_rant("f7.t") assert(err.empty?) lines = out.split(/\n/) assert_equal(1, lines.size) assert_equal("writing f7.t", lines.first) timeout FileUtils.touch "b.t" out, err = assert_rant("f7.t") assert(err.empty?) assert(out.empty?) write("b.t", "c\n") out, err = assert_rant("f7.t") assert(err.empty?) lines = out.split(/\n/) assert_equal(1, lines.size) assert_equal("writing f7.t", lines.first) out, err = assert_rant("f7.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("f7.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_f %w(a.t b.t) end def test_dep_on_no_action out, err = assert_rant("f8.t") assert(err.empty?) assert(test(?f, "f8.t")) assert(!test(?e, "f1.t")) assert_equal("writing f8.t\n", out) assert_equal("1\n", File.read("f8.t")) out, err = assert_rant("f8.t") assert(err.empty?) assert(out.empty?) end def test_dep_on_no_action_fail out, err = assert_rant(:fail, "f9.t") assert(out.empty?) assert_match(/need f1\.t/, err) assert_match(/38/, err) out, err = assert_rant(:fail, "f9.t") assert(out.empty?) assert_match(/need f1\.t/, err) assert_match(/38/, err) end def test_action_no_create out, err = assert_rant("f10.t") assert(err.empty?) assert_equal("should create f10.t\n", out) out, err = assert_rant("f10.t") assert(err.empty?) assert_equal("should create f10.t\n", out) out, err = assert_rant("f10.t") assert(err.empty?) assert_equal("should create f10.t\n", out) end def test_sub1_no_task out, err = assert_rant(:fail, "s1.t") assert(out.empty?) assert_match(/ERROR.*s1\.t/, err) assert(!test(?e, ".rant.meta")) end def test_sub1 out, err = assert_rant("sub1/s1.t") assert(err.empty?) assert(test(?f, "f2.t")) assert(test(?f, "sub1/s1.t")) out, err = assert_rant("sub1/s1.t") assert(err.empty?) assert(out.empty?) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("sub1/s1.t")) end def test_rant_import run_import "--auto", "-q", "make.t" assert_exit out = run_ruby("make.t", "sub1/s1.t") assert_exit assert(test(?f, "f2.t")) assert(test(?f, "sub1/s1.t")) out = run_ruby("make.t", "sub1/s1.t") assert(out.empty?) assert_equal("1\n", File.read("f2.t")) assert_equal("1\n", File.read("sub1/s1.t")) ensure FileUtils.rm_f "make.t" end def test_dep_on_mtime_dirnode out, err = assert_rant("d1.t/f13.t") assert(err.empty?) assert(!out.empty?) assert(test(?f, "d1.t/f13.t")) assert_equal("1\n", File.read("d1.t/f13.t")) out, err = assert_rant("d1.t/f13.t") assert(err.empty?) assert(out.empty?) end def test_dep_on_dir FileUtils.mkdir "d2.t" FileUtils.mkdir "d3.t" out, err = assert_rant("d2.t/f14.t") assert(err.empty?) assert(!out.empty?) assert(test(?f, "d2.t/f14.t")) assert_equal("1\n", File.read("d2.t/f14.t")) out, err = assert_rant("d2.t/f14.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("d2.t/f14.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("d3.t/f14.t", "dn2=d3.t") assert(err.empty?) assert_equal("writing d3.t/f14.t\n", out) assert(test(?f, "d3.t/f14.t")) assert_equal("1\n", File.read("d3.t/f14.t")) out, err = assert_rant("d3.t/f14.t", "dn2=d3.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("d3.t/f14.t", "dn2=d3.t") assert(err.empty?) assert(out.empty?) ensure FileUtils.rm_rf %w(d2.t d3.t) end def test_force out, err = assert_rant("f3.t") assert(test(?f, "f2.t")) assert(test(?f, "f3.t")) assert(err.empty?) assert_equal("writing f2.t\nwriting f3.t\n", out) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) out, err = assert_rant("-af3.t") assert_equal("writing f2.t\nwriting f3.t\n", out) out, err = assert_rant("f3.t") assert(err.empty?) assert(out.empty?) end def test_dep_on_self out, err = nil, nil th = Thread.new { out, err = assert_rant("f15.t") } assert_equal(th, th.join(0.5)) #assert_match(/WARNING/, err) #assert_match(/f15\.t/, err) assert(test(?f, "f15.t")) th = Thread.new { assert_rant("f15.t") } assert_equal(th, th.join(0.5)) #assert_match(/WARNING/, err) #assert_match(/f15\.t/, err) end def test_circular_dependency out, err = nil, nil th = Thread.new { out, err = assert_rant("f17.t") } assert_equal(th, th.join(0.5)) assert_match(/WARNING/, err) assert_match(/f17\.t/, err) assert(test(?f, "f16.t")) assert(test(?f, "f17.t")) end def test_mkdir out, err = assert_rant("f18.t") assert(err.empty?) assert(!out.empty?) assert(test(?d, "f18.t")) out, err = assert_rant("f18.t") assert(err.empty?) assert(out.empty?) end end rant-0.5.8/test/import/subfile/0000755000175000017500000000000010527254520015772 5ustar xavierxavierrant-0.5.8/test/import/subfile/Rantfile0000644000175000017500000000112110527253221017451 0ustar xavierxavierimport "subfile" gen SubFile, "sub.t/file" do |t| sys.touch t.name end desc "some subfile" gen SubFile, "sub2.t/file" gen SubFile, "sub3.t", "file" do |t| sys.touch t.name end gen SubFile, "sub.t/file2" do |t| sys.touch t.name end gen SubFile, "sub4.t/sub/file" do |t| sys.touch t.name end gen SubFile, "sub5.t", "sub/sub/file" do |t| sys.touch t.name end gen SubFile, "file.t" do |t| sys.touch t.name end gen SubFile, "sub.t", "sub/file" => "file.t" do |t| sys.touch t.name end gen SubFile, "a.t" => %w(file.t sub.t/file2) do |t| sys.touch t.name end rant-0.5.8/test/import/subfile/autoclean.rf0000644000175000017500000000035410527253221020275 0ustar xavierxavier import %w(autoclean subfile) gen AutoClean gen SubFile, "sub.t/file" do |t| sys.touch t.name end gen SubFile, "sub2.t", "sub.t/file" do |t| sys.touch t.name end gen SubFile, "sub3.t", "file" do |t| sys.touch t.name end rant-0.5.8/test/import/subfile/test_subfile.rb0000644000175000017500000000641410527253221021011 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportSubFileDir ||= File.expand_path(File.dirname(__FILE__)) class TestSubFile < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportSubFileDir) end def teardown FileUtils.rm_rf Dir["*.t"] end def test_run_cmd assert_rant("sub.t/file") assert(test(?d, "sub.t")) assert(test(?f, "sub.t/file")) end def test_desc out, err = assert_rant("--tasks") assert_match(%r{sub2\.t/file\s*#.*some subfile}, out) end def test_no_block assert_rant("sub2.t/file") assert(test(?d, "sub2.t")) assert(!test(?e, "sub2.t/file")) end def test_fail_no_basedir assert_rant(:fail, "sub3.t/file") assert(!test(?e, "sub3.t")) assert(!test(?e, "sub3.t/file")) end def test_basedir FileUtils.mkdir "sub3.t" assert_rant("sub3.t/file") assert(test(?d, "sub3.t")) assert(test(?f, "sub3.t/file")) end def test_dirtask_exists assert_rant("sub.t/file2") assert(test(?d, "sub.t")) assert(test(?f, "sub.t/file2")) assert(!test(?e, "sub.t/file")) end def test_make_two assert_rant("sub.t/file", "sub.t/file2") assert(test(?f, "sub.t/file")) assert(test(?f, "sub.t/file2")) end def test_two_dirs assert_rant("sub4.t/sub/file") assert(test(?f, "sub4.t/sub/file")) end def test_basedir_two_dirs FileUtils.mkdir "sub5.t" out, err = assert_rant("sub5.t/sub/sub/file") assert(!out.strip.empty?) assert(test(?f, "sub5.t/sub/sub/file")) out, err = assert_rant("sub5.t/sub/sub/file") assert(out.strip.empty?) end def test_make_dir assert_rant("sub.t") assert(test(?d, "sub.t")) assert(!test(?e, "sub.t/file")) end def test_only_file assert_rant("file.t") assert(test(?f, "file.t")) out, err = assert_rant("file.t") assert(out.strip.empty?) end def test_dependency FileUtils.mkdir "sub.t" assert_rant("sub.t/sub/file") assert(test(?f, "file.t")) assert(test(?f, "sub.t/sub/file")) end def test_dependencies assert_rant("a.t") assert(test(?f, "file.t")) assert(test(?f, "sub.t/file2")) assert(test(?f, "a.t")) end def test_autoclean assert_rant("-fautoclean.rf", "sub.t/file") assert(test(?f, "sub.t/file")) FileUtils.mkdir "sub2.t" assert_rant("-fautoclean.rf", "sub2.t/sub.t/file") assert(test(?f, "sub2.t/sub.t/file")) FileUtils.mkdir "sub3.t" assert_rant("-fautoclean.rf", "sub3.t/file") assert(test(?f, "sub3.t/file")) assert_rant("-fautoclean.rf", "autoclean") assert(test(?d, "sub2.t")) assert(test(?d, "sub3.t")) %w(sub.t sub2.t/sub.t sub3.t/file).each { |f| assert(!test(?e, f), "#{f} should have been unlinked by AutoClean") } end def test_with_slash open "with_slash.t", "w" do |f| f << <<-EOF import "subfile", "autoclean" gen SubFile, "base.t/", "a" do |t| sys.touch t.name end gen AutoClean EOF end assert_rant(:fail, "-fwith_slash.t") FileUtils.mkdir "base.t" assert_rant("-fwith_slash.t") assert(test(?f, "base.t/a")) out, err = assert_rant("-fwith_slash.t") assert(out.empty?) assert(err.empty?) assert_rant("-fwith_slash.t", "autoclean") assert(test(?d, "base.t")) assert(!test(?e, "base.t/a")) end end rant-0.5.8/test/import/sys/0000755000175000017500000000000010527254520015157 5ustar xavierxavierrant-0.5.8/test/import/sys/data/0000755000175000017500000000000010527254520016070 5ustar xavierxavierrant-0.5.8/test/import/sys/data/pkg/0000755000175000017500000000000010527254520016651 5ustar xavierxavierrant-0.5.8/test/import/sys/data/pkg/bin/0000755000175000017500000000000010527254520017421 5ustar xavierxavierrant-0.5.8/test/import/sys/data/pkg/bin/test0000755000175000017500000002175610527253221020336 0ustar xavierxavierELFƒ4,4 (&#44€4€444€€<<<<•<•PP•P•ÈÈHHH hhhQåtd/lib/ld-linux.so.2GNUSuSE Dè.65$…  _Jv_RegisterClasses__gmon_start__libc.so.6printf_IO_stdin_used__libc_start_mainGLIBC_2.0$ii V–(–,–U‰åƒìèièÐèÉÃÿ5 –ÿ%$–ÿ%(–héàÿÿÿÿ%,–héÐÿÿÿ1í^‰áƒäðPTRh„h€„QVh¼ƒè·ÿÿÿôU‰åSè[ÃïR‹ƒüÿÿÿ…ÀtÿÐX[ÉÃU‰åPP€=<–u.¡8–‹…Òt´&ƒÀ£8–ÿÒ¡8–‹…ÒuëÆ<–ÉÉöU‰åQQ‹L•…Òt¸…Àtƒì hL•èK|û÷ƒÄÉÃU‰åƒì‹EˆEÿƒäð¸ƒÀƒÀÁèÁà)Äèè ƒì h(…èóþÿÿƒÄ¸ÉÃU‰å]ÃU‰å]ÃU‰åƒì‰]ôèºÃþ‰}üƒ ÿÿÿ» ÿÿÿ‰uø)øÁø…Àpÿu轋]ô‹uø‹}ü‰ì]Ãÿ·‰ðN…Àuö‰ö蟋]ô‹uø‹}ü‰ì]ô&¼'U‰åƒì‰]ô‰uø1öèEɉ}üèþÿÿ“ ÿÿÿƒ ÿÿÿ)ÂÁú9Ös‰Eð‰×t&ÿ°F‹Eð9þrõ‹]ô‹uø‹}ü‰ì]Ë$ÃU‰åSR»<•¡<•ƒøÿt ƒëÿЋƒøÿuôX[]ÃU‰åSè[ÃPè6þÿÿY[ÉÃHello, world! ÿÿÿÿÿÿÿÿ$ °‚ …€ ‚¬ ` – ‚˜‚þÿÿox‚ÿÿÿoðÿÿol‚P•Þ‚î‚H•GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)GCC: (GNU) 3.3.4 (pre 3.3.5 20040809)ƒ",…°‚ $ƒ!{„È$‰…Å‚!o‘y_IO_stdin_used6{E__libc_csu_finij__libc_csu_initkƒ"ƒ../sysdeps/i386/elf/start.S/usr/src/packages/BUILD/glibc-2.3/csuGNU AS 2.15.91.0.2€Œ$ƒ$ƒå!…idGintQ_Viv‹$…OwV°/usr/src/packages/BUILD/glibc-2.3/cc/csu/crti.S/usr/src/packages/BUILD/glibc-2.3/csuGNU AS 2.15.91.0.2€ fAØ„„á!…int¶Õ7iVj¦[„r„Ui], ý=€„Ø„U Q iR,4 , °²  °Î+¥ в ½,Å è²  -Ý ² ì.õw Ð/usr/src/packages/BUILD/glibc-2.3/cc/csu/crtn.S/usr/src/packages/BUILD/glibc-2.3/csuGNU AS 2.15.91.0.2€%% $ > $ > 4: ; I?  &I%% $ > : ; I$ > .? : ; ' @ 4: ; I U4: ; I &I I ! '  I4: ; I? < %T/û ../sysdeps/i386/elfstart.SƒÀ01:"VWYXû û  û init.cCû /usr/src/packages/BUILD/glibc-2.3/cc/csucrti.S…3,Wd°‚#,:$ƒ ,Wdd,,-‹Yû /usr/lib/gcc-lib/i586-suse-linux/3.3.4/includeelf-init.cstddef.h„Úeµ;VuVÁºVZ›·j+Ó[Ô8ƒ kCû /usr/src/packages/BUILD/glibc-2.3/cc/csucrtn.S…Å‚ ÿÿÿÿ| ˆ „bA…B FƒN‡O†€„XA…B I†ƒP‡short unsigned intunsigned char/usr/src/packages/BUILD/glibc-2.3/csushort intlong long intlong long unsigned int_IO_stdin_usedGNU C 3.3.4 (pre 3.3.5 20040809)__libc_csu_finisize_t__init_array_end__init_array_startelf-init.c__fini_array_end__libc_csu_init__fini_array_startsize0=VDNVNUPU[V~ÁV|Œ‘È.symtab.strtab.shstrtab.interp.note.ABI-tag.note.SuSE.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment.debug_aranges.debug_pubnames.debug_info.debug_abbrev.debug_line.debug_frame.debug_str.debug_loc.debug_ranges44#HH 1hh<€€,B ¬¬`J ‚ `Rÿÿÿol‚l _þÿÿox‚x n ˜‚˜w  ‚  €°‚°{È‚È0†ƒŒ…’ … š8…8¤<•<«D•D²L•L·P•PÈÀ–Å–Î0–0 Ô<–<Ù<_â ˜ñ8_— › ¸ ?'øX40P%?uGJ¼ÔX %g < ²4Hh€¬ ‚l‚x‚˜‚  ‚ °‚ È‚ ƒ … …8…<•D•L•P•––0–<– !"#$%ñÿñÿñÿ>ñÿIñÿqñÿ|ñÿqñÿñÿqñÿñÿñÿñÿ>ñÿqñÿIñÿqñÿ¯ñÿñÿ¯ñÿñÿñÿñÿ>ñÿ¯ñÿIñÿ¯ñÿ»ñÿÂñÿòñÿ"ñÿÂñÿñÿñÿñÿ>ñÿÂñÿ-$ƒ =ñÿH<•VD•dL•q8–u<–Pƒ —ƒ =ñÿ£@•°H•½8…ËL•×à„ íñÿòñÿ"ñÿíñÿñÿñÿñÿ>ñÿíñÿñÿ$ñÿ+ñÿ:ñÿE„ RP•[ …b<•ñÿs4–€„b °‚ –„ ›ƒ ¢<•ñÿµ€„X Å<–ñÿѼƒC Öèó<•ñÿ0– 6!…'<–ñÿ.Ø„ E–[@–ñÿ`<•ñÿs$…‚0– £ /usr/src/packages/BUILD/glibc-2.3/cc/config.h/usr/src/packages/BUILD/glibc-2.3/csu//abi-note.S/usr/src/packages/BUILD/glibc-2.3/cc/csu/abi-tag.hsuse-note.Sinit.c/usr/src/packages/BUILD/glibc-2.3/cc/csu/crti.S/usr/src/packages/BUILD/glibc-2.3/cc/csu/defs.hinitfini.ccall_gmon_startcrtstuff.c__CTOR_LIST____DTOR_LIST____JCR_LIST__p.0completed.1__do_global_dtors_auxframe_dummy__CTOR_END____DTOR_END____FRAME_END____JCR_END____do_global_ctors_aux/usr/src/packages/BUILD/glibc-2.3/cc/csu/crtn.Smain.ctest.canother_test.celf-init.canother_test_DYNAMIC_fp_hw__fini_array_end__dso_handle__libc_csu_fini_inittest_start__fini_array_start__libc_csu_init__bss_startmain__libc_start_main@@GLIBC_2.0__init_array_enddata_startprintf@@GLIBC_2.0_fini_edata__i686.get_pc_thunk.bx_GLOBAL_OFFSET_TABLE__end__init_array_start_IO_stdin_used__data_start_Jv_RegisterClasses__gmon_start__rant-0.5.8/test/import/sys/data/pkg/bin/test.o0000644000175000017500000000123510527253221020556 0ustar xavierxavierELF¨4( U‰å]ÃGCC: (GNU) 3.3.4 (pre 3.3.5 20040809).symtab.strtab.shstrtab.text.data.bss.note.GNU-stack.comment4!<'<,<<<'cE€  ñÿtest.ctestrant-0.5.8/test/import/sys/data/pkg/test.c0000644000175000017500000000004410527253221017767 0ustar xavierxavier #include "test.h" void test() { } rant-0.5.8/test/import/sys/data/pkg/test.h0000644000175000017500000000007110527253221017774 0ustar xavierxavier #ifndef TEST_H #define TEST_H void test(void); #endif rant-0.5.8/test/import/sys/data/pkg.tgz0000644000175000017500000000753410527253221017405 0ustar xavierxavier‹…ØCíZ{lǟݽ3‡_ØæÆYÀæ¡Ø{w~CL‚1L06(¢›óÝÚwá|ç܃G“´‡$–ƒJ[… æ’¦­*5RQÔ”( â¤"ªÑ4MÒJ­’ª‘…´¤!5)×ï›™ÝÛà”@Õö>{væ7ß7ß7ïÙùnwõ»{C7¹ŽäjjhÀØÛÔà¡Ø[_OcNÄë©óÔ74Ô{¼^âñÖyꚈÚp=+eR2žðÅT•ÄFŸ/r%9#¿‚³!fü_Bƒ0þ #žÐü×ÏöG#ïìãiÿºº&¯·®Ç¿¦zC:ñÿ|üó†"þp2`¨ è4.ÈÏß TDK–æß—ÿ@þºŽ9º~d­ÿàõ³qÕõßä¸þsëÿF¬ÿ¾HÀèS7·÷lÖ×å/„t(b˜Ð¶`jémùù H Ô—Ûþ7h¿ÿá_/W~ÿ«…¼Fóý¯¶É[ë¿©±)·þo}³½s$I–‰B(v¸ê!ª.cùõD%.²„," Iņ@-Á ‘‚‚<Àõûa:àéœÇEaY-P–'%ŒOóÞcL¿BçËu¿ xNÆÇm`XòëÀ6°jãa?< ³϶ñ6½Ÿdë+S¿;êu‡5áP$¹W‹GµZ–_ÂÛ¶vÃÞ—,8y9¿'ÙÓŽü©2ë«<ΗxßÙIæ±#K}Vóx“Æq#Çxª<èpaÙR2-Ýï¼?f ˜èëwëÝFÖB¬-ì‹Ç8ÑõþhDÇU’ÐuM÷c“É`,Iô½c#0¡ˆ+$â(ÁÅ|¡YÛÙ±ªM¯Õ<´52¯…•¼ÏJB¡"äoåxö‡+:e ÄS H5ÆÐ[FÞ>ëa³Ïàc.d¾6FR *H¤ª*á‰åSUX’¾Ô|ðn ¨ 5‘÷Á€f»½}mä½á?ŸëÚÜ,y$†à±ikðE˜ÿã'@ðüáÃ`³û˜ìØ?öWèØîÑáÏsðdBNÙ¶ãµ±ÃiÙ®®¡-X¤öd3Ä£%ßH”úù"T1|Òñf¦Þ°xÉéDyéµ±‘ P~Ӧљ0­¡ÔÍÏa0T2|¶0ˆ™ã_¹ÿ³¿¿R‚FY_Œ¶»iOAžcú§Áÿ©qÇ©wK_Ÿ‡]„c‹å—À\ÿäR*å©0(;ÇX½Íxøìì‘çÇŸýc—J yàóCÃ*´øÐ øI^\zñÔEùàÉCƒ©dÙøK 8ºóühòâ蟜Ý9–šqbä܆ƒ'“F.Œ_äòŽ8ôâbŒ,{ Õ{a¼aFǧA]}—Z¦ö—¾|êò²·â3GÚϼ}øPbIÍ8¾f´ýܲK±O3¬Œ–WŽññèé~w‘'ñ1|1•(þБ:3ª@:y~ÛŽcöñ¦»ÆÁðvX¶%˜—ëŒp8Z­î‰ÆÂùüå'ÅçQÊ6Ÿpvã¼.„pü€ÃU„k—¯Á!ØkpB>®ù§£ª»!ðµ`®I´[“Bæù3è&ä&Ç <¦BŒë'õtCÅ£{!ªźœƒ8|€­‰/J¸·šé?‚Ž¿Øô¬CÞÚ¶¶åêØî–ªuZV¯.Œ4Ù ÖµÞÓìY¶”äÄþM1¤rbžü€}imŒª_2ù8×JùÜÃ9U òómòLß}–<îy§müJÊ‘Mþ>w_=Àæ¢Ió©\žß†°‚p 5ZÆŠ!àÚæÇƒ?žÔáš"÷dæA{ Ý•n¶d5‚¦¹ãûâc0îÕ57ºpŸ›ž5Zq'ã1w<æwúü»|ýFܽjKGçjw?ª¯©ÕêÜ`gµµG­Õ¼ Ú2¯æÃ[:DÍÍ æ彆á}ÞÒƒ$Ç—šÄ:Cž"X&®[)O^‹™NNFâpÊ›¹œ²ŽÑ6ÈäZÊiÇnŒnŧSÁÃÚ¹ï!æaèŽÃsmócóÜþX"tm}‘/¡é>jºÒïÀ ÁYò^f_Ð*¿Àzþ¶ …Y»¬•NXéÇøØ!aéiK^ˆH;«Í9ä<†üâc…„çî;Th ò\*˜–6¡êÆBõ´ÔTLæc§Eô ôŒôÅ"rm!•’’¼R©LR\s]U®éô±T*É+ƒ§R<·¸ªx:,q¹’(Å·CR¤‹%õ»¼à¶‚ŽéwÊpÊ8‘:sQëÕ5r5Ê,K èL[rj’Šoq!Ör%É«.&.à 7¥†Ʋަd*«G>\:¦c¢çTáL²NÌ¢i¤€ ÛkßR˜¥Ú›é¸¸é }V ÃÇ.1R– ‚s‡ [‰SÆÍD9I$O…wù‚[”Ùó¶ÞUqËöm2¼“J³¨Þ’ z ¹ÂW6oîóJTQ]qWE€Ö2qÓWBærÙÉrpS ¸XE ºº¦¢¢‚ŠÒ mÏ^!¼àôûý5‡škâ°§³ûŽ›žXnî5&:¸&ÝN8FŸ4«‰K_ù=l—Ƴ·mMn=õüVå«®'”Y'”{nUJ~³Ãõfó°\Æw}ýI÷ž[J™d¶rðS¦Jã8â{€û]…Ç#|‘°Ò½·»Ê«Šœk†V6>„ç`9—Áíj›M¦ã!ǰ³ëa%ŒÆj2õGŒ€ŠÛ¤üA_l’Sƒ¥ÃÑH¿J™(ÄpöâvÒv¥W ñއ¾nè †Àº/óíÓH 3ƒ®"ûxë´p†xæIž!ÀŠ£%‚WÁt…lÅ;ì–Äh Mva´ƒåš‡Å7 œÊ̺žÖøb‡!Z|ß@Â× q"Æâ ™‚Þ2bƒD‹D†Öºª£&áëçˆ^˵ /$Z`_t°8#Z$©í6bñP4’tàÅŒ0ʱÄ`8F ñZÂØ OÚ¿Z,ð%|D3‚z_Ì7`ÍŸˆÆâ`€E÷øcÔ˜o äÑ}0m¬doÄüÑ#‚yFo²:ÖdÂÁd/(HãP¤/j‰ööÆŒÝ&‚•l˜i^hc¹LÔo&¹™ÉÒ\¶-Ò8ê’ØýÄ$Ó±Â.G}5¶5ˆdú<¼69곑˜¿F”k!i? Þµ†$ö~ì´ÉaXEØ} åðö´Äî`y¼n¦/g=a÷0”Ã;[¡ÌäÄvtº—DQïZa™ÝÿL»¦ß/q9¼£í•Y{ív‘Ð 9•—Á;ÞQ™Ýíìí@¼Ç&‡wÂc2«7½cÚ䆸~´ƒGÄq™Ý#Åþ»Ï&wäNƒœGÃðMÏRì$‡ír`útµÉÑ;0TlN»ß!éù¢‚œêÌ^¿ïÙäšA®Ù™é3Ó?$i_õ!:Y?ˆr?µÉ­¹Õ—‘{Æ&‡>˜ÎËØ=ÁÛŠrÔ7éLû%M9ÔÒ¦ý\³ó&ê£w.›úÊó˜@”{Ý&ç9O±~orû´_@®%‹]¤ß›eAN·e˜É? rÇ`‘µa³/?äš]l-ˆúðÄ·Ë=re¢\‘ ÷,æyYê7W{:åy¢ÜbAîb1!ÛlØì—z.g®‰®i„T v1Ü!èK‚X›¥½ë¹K2÷5“‡÷y»_øMÛ–¥½æÚ0©”©Ð‡Uî'é}mª ¯E…9ž¥Ÿ³îç„–gRë,Ì,-ÌY˜õâÓf»Yá³U¶0½Ò}’a6›ŽZx*ÅÇ,ÌœrÇ-\@ñi ó1lâ" q_b¸˜bÕÂÓ(n¶0ÛÕZ3q)Å«-Ì~(é´0;ñº,ÌVîì#&fž¾r Ï¢Øca¶¶XØ>3ß,à9ž+àrÏp…€o°*àù^ à…®pUÆ|·1÷{æ×4~›¿ßÎWÒçSœO‹•L{šÂ¼³”/‘v%}ž”Ây²ðJ›üÝŠÐ_JæïÃlõ)ú|ËöN BxJÀ¤H@Åûäí“ôF#}¡~-HZz“¡p¢&™TÁxÒí&¾ÞP »ªOÞ‹eàŽ©‹çŒëàVgò£/&Ñ :À”ßÛ~JGwU<‘ìë£~”¶Í»õÎŽžÍºhuZßfAÍC ËÃFÂh^`¢z8Úë ëÔ‘ û’{ ½ÐëäÀÀ>Suû†ÕiÍ&XÓÝzg»…ÐŒ™Nkõ[Z¿¨ãû‡¶ño‹}ÐïA#¦shs"Ù9D_½}CëmDïÔƒ{²y˜ñ¨„96&x±˜Ó‰)bœÅ5ÑGÕsý^a +W¦?a˜à!Cç /Í>ˆÈfÕ2¨Š667jýFBôë‰`2²KëÝKôµWµvê׬éi߬on]ÕÙcpïÛ„¯-lö'ñõÆD²ÿ¥E³| t•ï?½µu Â÷_õµžÜ÷_7„Äï¿$ëfÇè'Ž&©FL]Ãòݼm[¶ïæ}É‚“™Ëû“ý](/Y_åq¹ÄûÎþÈÀqå ‡+:eÄùP¤cèŒÝï§.ºfÂØì øZ ™¯M“ô:0Òµ5ðÆòéZ,Âäï¦á©E !”}p ÙîÆ¾>ñ^ê/—zwõ…Üc ^;B/ÂüŸ9ÄËǃÍ~ìc2xtúoб}“©ÏA2~6!§/ì|múxænoïØÆV¬FR}²âI÷ø‰ª©_¬@©³Ž§03ý†%K~ø+'ò¥×¦'®@ù;'ôÀ´†R·>‡eÀ;u±(„™3_=òÙ?R¯¸Ñ(ë‹É.×#]ihÂsL)ü?7ã8÷®cõ+3K°‹pl±ü*˜ 3Ÿ\K§¡<%ƒбošÕÛŒS+'ö]žyG§¯•2ñÍϧRhñÔ øžH^]}õÜUyüìÔh:Y>ó'÷]žL^üæç÷M§çŸ™¸´}ülòÊÄ•™ŠRÞS/®ÄȲZ¯ÌtQ£ÌèL)ÔuêûÔ2µ¿úåsÿ”׿e,˜èº4ñöñ©Ä ’žzód×¥õ×âŸfY™¬ª™æãÑß÷î"Oâ+u5(J}èH_˜T ¼¼gpß´}|KÁtïL3Þ ËÖ£ór«‰Äê<cñH`é<6wÒ|¥mó g7Îë"§9\Ÿvùƒ½×yäãš0ªºB!_ æšD»U0)dž?Ÿ®aBnp Êcú$ĸÎqRWð*;ùP¥ÖåÄ‘clM|Ù÷V3ý'ÐñW›ž­(ÛÒѱÁ³ ¶»ÕžµêZµÉ³j4®Óä:Ïš††¦†–†õ«Éÿiÿ! Ÿ*bžü€}i™mŒê˜\2å8×ÊøÜÃ9Uü¥6>Ó÷€ÅÇ=ï¼M^Cå²)_Äçî«ÇØ\4Ÿ¥”ƒ÷w!† ø4[ÆJ àÚæÇƒßHjÃáh˜Ü—Y Zr¦Ù’Õlªê5}Ôð†×¶4{õȰ—ž5j?ñ&¸×ˆû½£>ÿ~_P7¼í»»{:½AT_¿F]ëx8{Úú=kÔÆuêúFµoilŠš›OÍ=Ê{ Ãû¼¥ã$G¾ŒKMb!çË–‰ëVÊ“·`¦S“‘8œòN@.§¬a”/ãÑ™\K-à8€Ñ­“øv*xX;w âƒÄ< ÒixÏ¡m~lž×O„o®/æIhz˜šnƒô;0Cp–¼—Ý´…Ê/±ž¿û ’Y»¬•NXé§ø”°t‚´;/L¤}uæržBù¿ðµQÂs÷JÊG™Ë¦¥ÈÀB}X¨‰–*Àä<ì”gî›ER±QÉë¨èöŸà[šwÁ’—àù$Õ½Êòg2ùÅh¡þ,WÏ¿ˆ9ê§4ŸD1ý½ }¹ˆÞÜ@HeÄW&•KŠk±«ÖUA?Ë$w^9¼•’Å%µ%°Äå¢”Þ I’.–t4Aî†Â; »+î*’á”q® Ýtæ¢Ökäj”…–Й±äT% 3ÙÊ"¬å&0’V]Œ.à —_CcÙdSRÀê1.˜(Ä9U´€¬„³¸”RR‰½ö­E9ª½‹Ž‹—Òg%0|ì#åØ 1wº•8eÜL”³Dj¨nܰì6¥rÉÀ=Õ·íÝ#Ã7©´êuÏÒKÈõ%*Y4[‚ۙ꧳–í0³8sO„ð•ÍÛ€û¼²Vª®«¾§:@k™¸é+Ë!sƒìd9¸‰)…œV¨««¯®®¦ôIZ¡½¹+„œ ß_qx]Ks½{:»ïxé‰å Gý‘d@':¸>ÓN8ú°2«‰K_ùl—ú³w $Î=? |Íõ„²ðŒrßíŠû·ƒ®7[Rr1ßÿßèŸh¦ðÜRÊ%³µƒ'˜R qŒãˆßmޏŠŽGø8⃕‚¸­Ä5.·;7§œÛVv<„ç`çàvµÇÆé~È‘rö>¬¡X<áIFp0ª<¸MZÀòÅç¸!05X:‹=ô•²Lg/n'_ôi!ÃFøºjè¬ùâqßaM²3è*²·F gѳOò,+Ž–^7Ò2€wØí,‰ÑnšìÅh嚇ŷ œËÎ:Okü±ÃÕ8<’ð Aœˆ³8d¦ ·ôø(Q£±„®¶µw×'|AŽèµ\ ùŒQ‡£ ƒÅ‰8QƒÑ¤z@áX4 h ‹ëä±Äh$F ñjB?oÚ¿j<ð%|DÕCÚpÜ7¢ÕŸˆÅ 0À¢ûüqjÌ7öƒX‚¾˜6VrÈš?62¢G1OJ¡c}Q˜A&M‚ G‡cuh(®0¬dÝLóú0@Ë91¿™äfæú,fÛ"ý€£> ‰ÝOÌÇôC,‡ÏyÔWc[ƒø˜>Fúl$æ¯y­$ãgÁ»Ö˜Ä¾6†vÂî[ÈÃ;ØÓ»ƒåñº™¾œm„ÝÇw¶"™ñÄvôº—ćw­ˆÌî¦]Ó„ß×8ïh‡dÖ^»]|¢ x¼ã”ÙÝÎÞÄm<¼ž’Y½éÓÆãúѧevûïï<ðίAàaxÈÆÃ³;Éa»˜>Gm¿'6-r§Ù2ÌäŸÞ)X¤'mØìË^‹‹­QžøvÞãÀs(³yÅï XÌKrÔo±À{:å.y6o¥À»ZBÈ6û¥‰óÌ5Ñ[JH­`Ã]‚¾$,ˆ-9Ú»Mà½èÎÞ×LÞçí~á7·'G{͵a>U Ì}X é Éìk‚¾VÌñýœëÁýœÐòŒµÕÂÌrÈÂLᘅY/>ma¶›31[% Ók Ý'f³é¤… (>eaæ”;máBŠÏ[˜¯ˆ”‰‹)Ä}‰áŠ=.¥¸ÅÂlWk}ÌÄewZ˜ýPÒcavâõZ˜­ÜÊ&fž¾* /¤¸ÁÂllµ°}f ¾UÀ‹¼XÀU^"àjß&`€— x™€— ¸FÀµYóÌA>N»,Êïp·€ïð‘ÈE{¢üfë'ê»Q}~{¢üfë'êë'Ê_ðËþ»€—I_Ì¿Ùú‹úê%æ¹C,ÃúÞ(ØÇïKsýJ°~¤Ìú•`ý¤Ìú•`ý޸儉o!I‰­G¶ÏW’£èµÙ{ðñ/°ÿàM6û§%îGçö_’2û ÞP-ÔçmÀï>˜Ñÿ‘ ÿFý/òo¶ÿE}Käl\#àÛ¼AÀ]øû û¤Ú×'göÏ2Ø?e¶?›¿gÉ™ñtCyp¯2ù2&gîò¨ï¸œ9Êá|8ömöž@œ2åÅäG‚þgå̽å¯Ê™ý忌¿vpù[¼}æï³ŸúðÛÆÜï=0¿Jùlþ~»TÉœOåp>­T²í© óÎR¹\Lº”ÌyRçÉ àM6þ½ŠÐ_JöïÃÇlõqC}¾cû&ð@xJÀ¤hÀƒ÷É;çèE‡ÃA5DZ‡’áH¢>SA#éõßP¸ž]Õçî‡Å2pǃԇŋsÆÿÀ­Îø}Ø“h`Êï‹Dl?¥£»ÊH$‡‡©¥c׎>­§»—¦êÌBÛ:,0ª6èòшžÐj#1-‰ ù"u$h¾ä!B/ôZ 92rØTݵ½3£Ù›ûÚî1Ó­~Kë—u|ãoÿж„n`Gû ßCz\ãÐæD²KˆÖ¹w{ÛÝÝDÕBsy˜FL Áœ‹è³¼XÌéıNÎယí£2 .£¯0ë/6mÊü Ã,:gxiöYlV-zp hsK³ÔÚ¨_K„’ÑýêÐ!¢méÙÑÞÖ£íØ¼¹¿k—¶«­½§ Æà:Þ·Yma³?‡¿Þø7PKÚ[3ãÝQÿpkg/bin/test.oUT ÌÕCÛßCUxèd«wõqcddd€Ffa”6“ œ l ¡Ocƒ¸îÎÎV î~¡š ÆzÆz& E©`¦©‚‘‰…¥&ƒ^qenIb.)‚Ð0VIjE ƒ^JbI"ƒ^Rq1ƒ^^~IªÐDÝâ’Ääl½äüÜÜÔ¼b4Ø @W"ÜÍŠ$Ï¥¡ê˜¡|4s`êÔ˜ƒu:Póp¨cDGW§ŽE ’ Ä®XÔh&$q §êfv¨û€˜ͼ @u¼XÌCÈâ, ÿ#Ë1£ébFq ˆÏŒÆgAã³‚Ý  8™W’Z\¢— ¦PK ]3 íApkg/bin/UT ØCUxPKì“3?]ôá#$ ¤;pkg/test.cUT[ò CUxPKì“3깎'19 ¤›pkg/test.hUT[ò CUxPKÚ[3Þ†Æ w î# í pkg/bin/testUTÌÕCUxPKÚ[3ãÝQÿ ¤¿pkg/bin/test.oUTÌÕCUxPK]ÿrant-0.5.8/test/import/sys/data/pkg2.zip0000644000175000017500000000775110527253221017466 0ustar xavierxavierPK ]3pkg/bin/UT  ØCâßCUxèdPKì“3?]ôá#$ pkg/test.cUT [ò CâßCUxèdãRÎÌKÎ)MIUP*I-.ÑËPââ*ËÏLQñ44¹ª¹j¹PKì“3깎'19 pkg/test.hUT [ò CâßCUxèdãRÎLËKIMSq ‰÷àR²3óRa\®²üÌ…’Ôâ KÓš‹K95/%3 PKÚ[3Þ†Æ w î# pkg/bin/testUT ÌÕCâßCUxèdíZ{lǟݽ³Ïøu¶8`œlнgƒq 1 ~¦¨E7ç»õÝ…ós{Ç£¡­áâ$–c•¶ AÍ%MÛ©(jJ”‡qRUŠhš&i¥VIÕHŽBZÒŠš”ë÷ÍÌîíà”ö¿.ÌÎüæûÍ÷Í{v¾ó·»z6K’DÌG& ¡(åp5ATWÎò›ˆ‡¸È*²‚,'yCZ‚"e€›Ž‚Bà .ãTö`Y­P–'n&§yì1¦ß!ËeˆzAÞ 2 çcÈã60lþV°ÁØc“a<2 •€+m²ï'$Çcê÷FÂCÞH >Ž&©FL]Ãòݼm[¶ïæ}É‚“™Ëû“ý](/Y_åq¹ÄûÎþÈÀqå ‡+:eÄùP¤cèŒÝï§.ºfÂØì øZ ™¯M“ô:0Òµ5ðÆòéZ,Âäï¦á©E !”}p ÙîÆ¾>ñ^ê/—zwõ…Üc ^;B/ÂüŸ9ÄËǃÍ~ìc2xtúoб}“©ÏA2~6!§/ì|múxænoïØÆV¬FR}²âI÷ø‰ª©_¬@©³Ž§03ý†%K~ø+'ò¥×¦'®@ù;'ôÀ´†R·>‡eÀ;u±(„™3_=òÙ?R¯¸Ñ(ë‹É.×#]ihÂsL)ü?7ã8÷®cõ+3K°‹pl±ü*˜ 3Ÿ\K§¡<%ƒбošÕÛŒS+'ö]žyG§¯•2ñÍϧRhñÔ øžH^]}õÜUyüìÔh:Y>ó'÷]žL^üæç÷M§çŸ™¸´}ülòÊÄ•™ŠRÞS/®ÄȲZ¯ÌtQ£ÌèL)ÔuêûÔ2µ¿úåsÿ”׿e,˜èº4ñöñ©Ä ’žzód×¥õ×âŸfY™¬ª™æãÑß÷î"Oâ+u5(J}èH_˜T ¼¼gpß´}|KÁtïL3Þ ËÖ£ór«‰Äê<cñH`é<6wÒ|¥mó g7Îë"§9\Ÿvùƒ½×yäãš0ªºB!_ æšD»U0)dž?Ÿ®aBnp Êcú$ĸÎqRWð*;ùP¥ÖåÄ‘clM|Ù÷V3ý'ÐñW›ž­(ÛÒѱÁ³ ¶»ÕžµêZµÉ³j4®Óä:Ïš††¦†–†õ«Éÿiÿ! Ÿ*bžü€}i™mŒê˜\2å8×ÊøÜÃ9Uü¥6>Ó÷€ÅÇ=ï¼M^Cå²)_Äçî«ÇØ\4Ÿ¥”ƒ÷w!† ø4[ÆJ àÚæÇƒßHjÃáh˜Ü—Y Zr¦Ù’Õlªê5}Ôð†×¶4{õȰ—ž5j?ñ&¸×ˆû½£>ÿ~_P7¼í»»{:½AT_¿F]ëx8{Úú=kÔÆuêúFµoilŠš›OÍ=Ê{ Ãû¼¥ã$G¾ŒKMb!çË–‰ëVÊ“·`¦S“‘8œòN@.§¬a”/ãÑ™\K-à8€Ñ­“øv*xX;w âƒÄ< ÒixÏ¡m~lž×O„o®/æIhz˜šnƒô;0Cp–¼—Ý´…Ê/±ž¿û ’Y»¬•NXé§ø”°t‚´;/L¤}uæržBù¿ðµQÂs÷JÊG™Ë¦¥ÈÀB}X¨‰–*Àä<ì”gî›ER±QÉë¨èöŸà[šwÁ’—àù$Õ½Êòg2ùÅh¡þ,WÏ¿ˆ9ê§4ŸD1ý½ }¹ˆÞÜ@HeÄW&•KŠk±«ÖUA?Ë$w^9¼•’Å%µ%°Äå¢”Þ I’.–t4Aî†Â; »+î*’á”q® Ýtæ¢Ökäj”…–Й±äT% 3ÙÊ"¬å&0’V]Œ.à —_CcÙdSRÀê1.˜(Ä9U´€¬„³¸”RR‰½ö­E9ª½‹Ž‹—Òg%0|ì#åØ 1wº•8eÜL”³Dj¨nܰì6¥rÉÀ=Õ·íÝ#Ã7©´êuÏÒKÈõ%*Y4[‚ۙ꧳–í0³8sO„ð•ÍÛ€û¼²Vª®«¾§:@k™¸é+Ë!sƒìd9¸‰)…œV¨««¯®®¦ôIZ¡½¹+„œ ß_qx]Ks½{:»ïxé‰å Gý‘d@':¸>ÓN8ú°2«‰K_ùl—ú³w $Î=? |Íõ„²ðŒrßíŠû·ƒ®7[Rr1ßÿßèŸh¦ðÜRÊ%³µƒ'˜R qŒãˆßmޏŠŽGø8⃕‚¸­Ä5.·;7§œÛVv<„ç`çàvµÇÆé~È‘rö>¬¡X<áIFp0ª<¸MZÀòÅç¸!05X:‹=ô•²Lg/n'_ôi!ÃFøºjè¬ùâqßaM²3è*²·F gѳOò,+Ž–^7Ò2€wØí,‰ÑnšìÅh嚇ŷ œËÎ:Okü±ÃÕ8<’ð Aœˆ³8d¦ ·ôø(Q£±„®¶µw×'|AŽèµ\ ùŒQ‡£ ƒÅ‰8QƒÑ¤z@áX4 h ‹ëä±Äh$F ñjB?oÚ¿j<ð%|DÕCÚpÜ7¢ÕŸˆÅ 0À¢ûüqjÌ7öƒX‚¾˜6VrÈš?62¢G1OJ¡c}Q˜A&M‚ G‡cuh(®0¬dÝLóú0@Ë91¿™äfæú,fÛ"ý€£> ‰ÝOÌÇôC,‡ÏyÔWc[ƒø˜>Fúl$æ¯y­$ãgÁ»Ö˜Ä¾6†vÂî[ÈÃ;ØÓ»ƒåñº™¾œm„ÝÇw¶"™ñÄvôº—ćw­ˆÌî¦]Ó„ß×8ïh‡dÖ^»]|¢ x¼ã”ÙÝÎÞÄm<¼ž’Y½éÓÆãúѧevûïï<ðίAàaxÈÆÃ³;Éa»˜>Gm¿'6-r§Ù2ÌäŸÞ)X¤'mØìË^‹‹­QžøvÞãÀs(³yÅï XÌKrÔo±À{:å.y6o¥À»ZBÈ6û¥‰óÌ5Ñ[JH­`Ã]‚¾$,ˆ-9Ú»Mà½èÎÞ×LÞçí~á7·'G{͵a>U Ì}X é Éìk‚¾VÌñýœëÁýœÐòŒµÕÂÌrÈÂLᘅY/>ma¶›31[% Ók Ý'f³é¤… (>eaæ”;máBŠÏ[˜¯ˆ”‰‹)Ä}‰áŠ=.¥¸ÅÂlWk}ÌÄewZ˜ýPÒcavâõZ˜­ÜÊ&fž¾* /¤¸ÁÂllµ°}f ¾UÀ‹¼XÀU^"àjß&`€— x™€— ¸FÀµYóÌA>N»,Êïp·€ïð‘ÈE{¢üfë'ê»Q}~{¢üfë'êë'Ê_ðËþ»€—I_Ì¿Ùú‹úê%æ¹C,ÃúÞ(ØÇïKsýJ°~¤Ìú•`ý¤Ìú•`ý޸儉o!I‰­G¶ÏW’£èµÙ{ðñ/°ÿàM6û§%îGçö_’2û ÞP-ÔçmÀï>˜Ñÿ‘ ÿFý/òo¶ÿE}Käl\#àÛ¼AÀ]øû û¤Ú×'göÏ2Ø?e¶?›¿gÉ™ñtCyp¯2ù2&gîò¨ï¸œ9Êá|8ömöž@œ2åÅäG‚þgå̽å¯Ê™ý忌¿vpù[¼}æï³ŸúðÛÆÜï=0¿Jùlþ~»TÉœOåp>­T²í© óÎR¹\Lº”ÌyRçÉ àM6þ½ŠÐ_JöïÃÇlõqC}¾cû&ð@xJÀ¤hÀƒ÷É;çèE‡ÃA5DZ‡’áH¢>SA#éõßP¸ž]Õçî‡Å2pǃԇŋsÆÿÀ­Îø}Ø“h`Êï‹Dl?¥£»ÊH$‡‡©¥c׎>­§»—¦êÌBÛ:,0ª6èòшžÐj#1-‰ ù"u$h¾ä!B/ôZ 92rØTݵ½3£Ù›ûÚî1Ó­~Kë—u|ãoÿж„n`Gû ßCz\ãÐæD²KˆÖ¹w{ÛÝÝDÕBsy˜FL Áœ‹è³¼XÌéıNÎယí£2 .£¯0ë/6mÊü Ã,:gxiöYlV-zp hsK³ÔÚ¨_K„’ÑýêÐ!¢méÙÑÞÖ£íØ¼¹¿k—¶«­½§ Æà:Þ·Yma³?‡¿Þø7PK ]3 íApkg/bin/UT ØCUxPKì“3?]ôá#$ ¤;pkg/test.cUT[ò CUxPKì“3깎'19 ¤›pkg/test.hUT[ò CUxPKÚ[3Þ†Æ w î# í pkg/bin/testUTÌÕCUxPK¿rant-0.5.8/test/import/sys/test_tgz.rb0000644000175000017500000000403610527253221017347 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import/sys/more' $test_import_sys_dir ||= File.expand_path(File.dirname(__FILE__)) class TestImportSysTgz < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($test_import_sys_dir) end def test_unpack_tgz assert !test(?e, "pkg") begin out, err = assert_rant "-ftgz.rf" assert err.empty? assert !out.empty? assert out.split(/\n/).size < 3 dirs = %w(pkg pkg/bin) files = %w(pkg/test.c pkg/test.h pkg/bin/test pkg/bin/test.o) dirs.each { |dir| assert test(?d, dir), "dir `#{dir}' missing" } files.each { |fn| src_fn = File.join "data", fn assert test(?f, fn), "file `#{fn}' missing" assert Rant::Sys.compare_file(fn, src_fn), "#{fn} corrupted" } actual = Rant::FileList["pkg", "pkg/**/*", "pkg/**/.*"] actual.shun ".", ".." assert_equal((files + dirs).sort, actual.sort) # test overwriting of existing files Rant::Sys.write_to_file "pkg/test.c", "changed!\n" out, err = assert_rant "-ftgz.rf" assert err.empty? assert !out.empty? assert out.split(/\n/).size < 3 dirs = %w(pkg pkg/bin) files = %w(pkg/test.c pkg/test.h pkg/bin/test pkg/bin/test.o) dirs.each { |dir| assert test(?d, dir), "dir `#{dir}' missing" } files.each { |fn| src_fn = File.join "data", fn assert test(?f, fn), "file `#{fn}' missing" assert Rant::Sys.compare_file(fn, src_fn), "#{fn} corrupted" } actual = Rant::FileList["pkg", "pkg/**/*", "pkg/**/.*"] actual.shun ".", ".." assert_equal((files + dirs).sort, actual.sort) ensure Rant::Sys.rm_rf "pkg" end end end rant-0.5.8/test/import/sys/test_zip.rb0000644000175000017500000000453310527253221017347 0ustar xavierxavier require 'test/unit' require 'tutil' $test_import_sys_dir ||= File.expand_path(File.dirname(__FILE__)) class TestImportSysZip < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($test_import_sys_dir) end def test_unpack_zip_in_dir assert !test(?e, "dir.t") out, err = assert_rant "-fzip.rf" assert err.empty? assert !out.empty? assert test(?f, "t.zip") assert out.split(/\n/).size < 6 dirs = %w(pkg pkg/bin) files = %w(pkg/test.c pkg/test.h pkg/bin/test pkg/bin/test.o) dirs.each { |dir| dir = File.join("dir.t", dir) assert test(?d, dir), "dir `#{dir}' missing" } files.each { |fn| src_fn = File.join "data", fn fn = File.join("dir.t", fn) assert test(?f, fn), "file `#{fn}' missing" assert Rant::Sys.compare_file(fn, src_fn), "#{fn} corrupted" } actual = Rant::FileList["dir.t/**/*", "dir.t/**/.*"] actual.shun ".", ".." actual.map! { |e| e.sub(/^dir\.t\//, '') } assert_equal((files + dirs).sort, actual.sort) out, err = assert_rant "-fzip.rf" assert err.empty? assert out.empty? out, err = assert_rant "-fzip.rf", "src=data/pkg2.zip" assert err.empty? assert !out.empty? assert out.split(/\n/).size < 6 dirs = %w(pkg pkg/bin) files = %w(pkg/test.c pkg/test.h pkg/bin/test) dirs.each { |dir| dir = File.join("dir.t", dir) assert test(?d, dir), "dir `#{dir}' missing" } files.each { |fn| src_fn = File.join "data", fn fn = File.join("dir.t", fn) assert test(?f, fn), "file `#{fn}' missing" assert Rant::Sys.compare_file(fn, src_fn), "#{fn} corrupted" } actual = Rant::FileList["dir.t/**/*", "dir.t/**/.*"] actual.shun ".", ".." actual.map! { |e| e.sub(/^dir\.t\//, '') } assert_equal((files + dirs).sort, actual.sort) out, err = assert_rant "-fzip.rf", "src=data/pkg2.zip" assert err.empty? assert out.empty? assert_rant "-fzip.rf", "autoclean" assert !test(?e, "dir.t") assert !test(?e, "t.zip") assert Dir["**/*.rant.meta"].empty? end end rant-0.5.8/test/import/sys/tgz.rf0000644000175000017500000000011110527253221016302 0ustar xavierxavier import "sys/tgz" task :unpack do sys.unpack_tgz "data/pkg.tgz" end rant-0.5.8/test/import/sys/zip.rf0000644000175000017500000000040210527253221016303 0ustar xavierxavier import "md5", "sys/zip", "autoclean" var :src => "data/pkg.zip" gen Directory, "dir.t" => "t.zip" do sys.rm_rf "dir.t/pkg" sys.unpack_zip "t.zip", :in => "dir.t" end file "t.zip" => var[:src] do |t| sys.cp t.source, t.name end gen AutoClean rant-0.5.8/test/import/truth/0000755000175000017500000000000010527254520015507 5ustar xavierxavierrant-0.5.8/test/import/truth/Rantfile0000644000175000017500000000055510527253220017177 0ustar xavierxavier import %w(truth) task(:hello) { puts "hello" } % "print hello" file("rm.t") { |t| sys.touch t.name } % "touch rm.t" % "this file is useless" task :sys_pipe do |t| sys.touch %w(a.t b.t) test(?f, "a.t") && test(?f, "b.t") or t.fail sys["*.t"] % :rm test(?f, "a.t") || test(?f, "b.t") and t.fail end drag :AutoClean, :clean # vim:ft=ruby rant-0.5.8/test/import/truth/test_truth.rb0000644000175000017500000000112510527253220020234 0ustar xavierxavier require 'test/unit' require 'tutil' $testImportTruthDir ||= File.expand_path(File.dirname(__FILE__)) class TestTruth < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testImportTruthDir) unless Dir.pwd == $testImportTruthDir end def test_opt_tasks out, err = assert_rant("--tasks") assert_match( /print hello.*\n.*touch rm\.t.*\n.*this file is useless/, out) end def test_drag assert_rant("rm.t") assert(test(?f, "rm.t")) assert_rant("clean") assert(!test(?e, "rm.t")) end def test_pipe assert_rant("sys_pipe") end end rant-0.5.8/test/lib/0000755000175000017500000000000010527254520013575 5ustar xavierxavierrant-0.5.8/test/lib/test_filelist.rb0000644000175000017500000003662610527253217017013 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/filelist' require 'rant/import/sys/more' $test_lib_dir ||= File.expand_path(File.dirname(__FILE__)) class TestRantFileList < Test::Unit::TestCase include Rant::TestUtil def assert_entries(entry_ary, fl) assert_equal entry_ary.size, fl.size entry_ary.each { |entry| assert fl.include?(entry) } end def setup # Ensure we run in test directory. Dir.chdir($test_lib_dir) end def test_require in_local_temp_dir do Rant::Sys.write_to_file "fl.rb", <<-EOF require 'rant/filelist' File.open("out", "w") do |f| f.puts Rant::FileList["*.{rb,t}"].sort! end EOF Rant::Sys.touch "a.t" Rant::Sys.ruby "-I", ENV["RANT_DEV_LIB_DIR"], "fl.rb" assert_equal "a.t\nfl.rb\n", File.read("out") end end def test_create_new fl = Rant::FileList.new assert_equal [], fl.entries in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl.include "*.t" assert_entries(["a.t", ".a.t"], fl) end end def test_create_new_with_ary ary = ["foo", "bar"] fl = Rant::FileList.new(ary) assert_equal ["foo", "bar"], fl.entries end def test_create_bracket_op in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl = Rant::FileList["*.t"] assert_entries(["a.t"], fl) end end def test_create_glob in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl = Rant::FileList.glob("*.t") assert_entries(["a.t"], fl) fl = Rant::FileList.glob(".*.t") # note: no "." and ".." entries assert_entries([".a.t"], fl) fl = Rant::FileList.glob("*.t") do |fl| fl.glob ".*.t" end assert_equal ["a.t", ".a.t"], fl.entries end end def test_create_glob_all in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl = Rant::FileList.glob_all("*.t") assert_entries(["a.t", ".a.t"], fl) fl = Rant::FileList.glob_all(".*.t") # note: no "." and ".." entries assert_entries([".a.t"], fl) fl = Rant::FileList.glob_all("*.t") do |fl| fl.keep(".") end assert_entries ["a.t", ".a.t", "."], fl end end def test_conversion_from_filelist fl1 = Rant::FileList.new fl2 = Rant::FileList(fl1) assert fl1.equal?(fl2) # same object end def test_conversion_from_ary fl = Rant::FileList(["foo", "bar"]) assert fl.kind_of?(Rant::FileList) assert_equal ["foo", "bar"], fl.entries end def test_conversion_from_to_ary obj = Object.new def obj.to_ary ["foo", "bar"] end fl = Rant::FileList(obj) assert fl.kind_of?(Rant::FileList) assert_equal ["foo", "bar"], fl.entries end def test_conversion_from_to_rant_filelist obj = Object.new def obj.to_rant_filelist Rant::FileList.new ["foo", "bar"] end fl = Rant::FileList(obj) assert fl.kind_of?(Rant::FileList) assert_equal ["foo", "bar"], fl.entries end # note: this behaviour might change def test_conversion_type_error_string assert_raise_kind_of(TypeError) do fl = Rant::FileList("some_string") end end def test_conversion_type_error assert_raise_kind_of(TypeError) do fl = Rant::FileList(Object.new) end end def test_each in_local_temp_dir do Rant::Sys.touch ["a.t", "b.t", "c.t"] ary = [] fl = Rant::FileList["*.t"] assert(fl.each { |fn| ary << fn }.equal?(fl)) assert_entries ["a.t", "b.t", "c.t"], ary end end def test_enumerable_find fl = Rant::FileList.new ["bb", "aa", "c", "d", "a"] assert_equal "aa", fl.find { |fn| fn =~ /a/ } end def test_glob in_local_temp_dir do all = ["a.t", "b.t", "c.t", ".a.t"] Rant::Sys.touch all fl = Rant::FileList.new assert fl.include("*.t").equal?(fl) assert_entries all, fl fl = Rant::FileList.new assert fl.glob("*.t").equal?(fl) assert_entries all, fl fl = Rant::FileList.new fl.hide_dotfiles fl.glob "*.t" assert_entries all - [".a.t"], fl fl = Rant::FileList.new fl.glob "*a*", "*c*" assert_entries ["a.t", "c.t", ".a.t"], fl assert fl.entries.index("a.t") < fl.entries.index("c.t") end end def test_exclude fl = Rant::FileList(["a.rb", "b.t", "foo_bar"]) assert fl.exclude("*.rb").equal?(fl) assert_entries ["b.t", "foo_bar"], fl fl.concat ["b.rb", "a.rb"] assert_entries ["b.t", "foo_bar", "b.rb", "a.rb"], fl end def test_exclude_regexp fl = Rant::FileList(["a.rb", "b.t", "foo_bar"]) fl.exclude %r{_} assert_entries ["a.rb", "b.t"], fl Rant::Sys.touch "a_b.t" fl.include "*.t" assert_entries ["a.rb", "b.t", "a_b.t"], fl ensure Rant::Sys.rm_f "a_b.t" end def test_exclude_more_args fl = Rant::FileList(["a.rb", "b.t", "foo_bar"]) fl.exclude "*.t", /_/ assert_entries ["a.rb"], fl end def test_exclude_dotfiles_always in_local_temp_dir do Rant::Sys.touch [".a.t", "b.t", "c.t", "a.rb"] fl = Rant::FileList[".*.{rb,t}", "*.{rb,t}"] assert_entries [".a.t", "b.t", "c.t", "a.rb"], fl fl.exclude "*.t" assert_entries ["a.rb"], fl end end def test_shun fl = Rant::FileList(["a/CVS/b", "CVS", "CVS.rb", "cvs"]) assert fl.shun("CVS").equal?(fl) assert_entries ["CVS.rb", "cvs"], fl fl.push "CVS/foo" assert_entries ["CVS/foo", "CVS.rb", "cvs"], fl end def test_exclude_name fl = Rant::FileList(["a/CVS/b", "CVS", "CVS.rb", "cvs"]) assert fl.exclude_name("CVS").equal?(fl) assert_entries ["CVS.rb", "cvs"], fl fl.push "CVS/foo" assert_entries ["CVS/foo", "CVS.rb", "cvs"], fl end def test_exclude_path fl = Rant::FileList(["a.rb", "lib/b.rb", "lib/foo/c.rb"]) assert fl.exclude_path("lib/*.rb").equal?(fl) assert_equal ["a.rb", "lib/foo/c.rb"], fl.entries end def test_exclude_vs_exclude_path fl = Rant::FileList(["a.rb", "lib/b.rb", "lib/foo/c.rb"]) assert fl.exclude("lib/*.rb").equal?(fl) assert_equal ["a.rb"], fl.entries end def test_ignore fl = Rant::FileList(["a/CVS/b", "CVS", "CVS.rb", "cvs"]) assert fl.ignore("CVS").equal?(fl) assert_entries ["CVS.rb", "cvs"], fl fl.push "CVS/foo" assert_entries ["CVS.rb", "cvs"], fl end def test_no_dir in_local_temp_dir do Rant::Sys.mkdir_p ["foo/bar", "baz"] Rant::Sys.touch ["foo/bar/xy", "xy"] fl = Rant::FileList["**/*"] assert_entries ["foo", "foo/bar", "baz", "foo/bar/xy", "xy"], fl assert fl.no_dir.equal?(fl) assert_entries ["foo/bar/xy", "xy"], fl end end def test_select fl1 = Rant::FileList(["a", "b", "c.rb", "d.rb"]) fl2 = fl1.select { |fn| fn =~ /\.rb$/ } assert fl2.kind_of?(Rant::FileList) assert_entries ["c.rb", "d.rb"], fl2 assert_entries ["a", "b", "c.rb", "d.rb"], fl1 fl2.push "e.rb" assert_entries ["c.rb", "d.rb", "e.rb"], fl2 assert_entries ["a", "b", "c.rb", "d.rb"], fl1 fl1.push "c" assert_entries ["a", "b", "c", "c.rb", "d.rb"], fl1 assert_entries ["c.rb", "d.rb", "e.rb"], fl2 end def test_map fl1 = Rant::FileList(["a.rb", "b.rb"]) fl2 = fl1.map { |fn| "#{fn}~" } assert_entries ["a.rb~", "b.rb~"], fl2 assert_entries ["a.rb", "b.rb"], fl1 fl1.push "foo" assert_entries ["a.rb", "b.rb", "foo"], fl1 assert_entries ["a.rb~", "b.rb~"], fl2 end def test_ext fl1 = Rant::FileList(["a.c", "b.c"]) fl2 = fl1.ext("o") assert fl2.kind_of?(Rant::FileList) assert !fl1.equal?(fl2) assert_entries ["a.c", "b.c"], fl1 assert_entries ["a.o", "b.o"], fl2 assert_entries ["a.txt"], Rant::FileList(["a"]).ext("txt") end def test_uniq! fl = Rant::FileList(["a", "b", "a"]) assert fl.uniq!.equal?(fl) assert_entries ["a", "b"], fl assert fl.uniq!.equal?(fl) assert_entries ["a", "b"], fl end def test_sort! fl = Rant::FileList(["a", "c", "b"]) assert fl.sort!.equal?(fl) assert_equal ["a", "b", "c"], fl.entries assert fl.sort!.equal?(fl) assert_equal ["a", "b", "c"], fl.entries end def test_map! fl = Rant::FileList(["a", "b", "c"]) assert(fl.map! { |fn| "#{fn}.rb" }.equal?(fl)) assert_equal ["a.rb", "b.rb", "c.rb"], fl.entries end def test_reject! fl = Rant::FileList(["a.rb", "b", "c", "d.rb"]) assert(fl.reject! { |fn| fn =~ /\.rb$/ }.equal?(fl)) assert_equal ["b", "c"], fl.entries end def resolve fl = Rant::FileList(["a", "b"]) fl2 = nil out = capture_stdout do fl2 = fl.map { |fn| puts fn; "#{fn}.rb" } assert fl2.resolve.equal?(fl2) end assert_equal "a\nb\n", out end def test_to_a ary = Rant::FileList(["a", "b"]).to_a assert ary.kind_of?(Array) assert_equal ["a", "b"], ary end def test_to_ary ary = Rant::FileList(["a", "b"]).to_ary assert ary.kind_of?(Array) assert_equal ["a", "b"], ary end # TODO: comprehensive testing of FileList#+ operator def test_size assert_equal 2, Rant::FileList(["a", "b"]).size end def test_empty? fl = Rant::FileList(["a", "b"]) assert !fl.empty? fl.exclude "a", "b" assert fl.empty? end def test_join fl = Rant::FileList(["a", "b"]) assert_equal "a b", fl.join assert_equal "a\nb", fl.join("\n") end def test_pop fl = Rant::FileList(["a", "b"]) assert_equal "b", fl.pop assert_equal "a", fl.pop assert_equal nil, fl.pop assert_equal nil, fl.pop end def test_push fl = Rant::FileList(["a", "b"]) assert fl.push("c").equal?(fl) assert_equal ["a", "b", "c"], fl.entries end def test_shift fl = Rant::FileList(["a", "b"]) assert_equal "a", fl.shift assert_equal "b", fl.shift assert_equal nil, fl.shift assert_equal nil, fl.shift end def test_unshift fl = Rant::FileList(["a", "b"]) assert fl.unshift("c").equal?(fl) assert_equal ["c", "a", "b"], fl.entries end def test_keep fl = Rant::FileList(["b.t"]) assert fl.keep("a.t").equal?(fl) assert_entries ["b.t", "a.t"], fl fl.exclude "*.t" assert_entries ["a.t"], fl fl.ignore "a.t" assert_entries ["a.t"], fl end def test_to_rant_filelist fl = Rant::FileList.new assert fl.to_rant_filelist.equal?(fl) end def test_dup fl1 = Rant::FileList(["a", "b"]) fl2 = fl1.dup assert !fl1.equal?(fl2) fl1.push "c" assert_entries ["a", "b", "c"], fl1 assert_entries ["a", "b"], fl2 end def test_copy fl1 = Rant::FileList(["a", "b"]) fl2 = fl1.copy fl1.each { |fn| fn << ".rb" } assert_entries ["a.rb", "b.rb"], fl1 assert_entries ["a", "b"], fl2 end def test_hide_dotfiles fl = Rant::FileList.new in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] assert fl.hide_dotfiles.equal?(fl) fl.glob "*.t" assert_entries ["a.t"], fl end end def test_glob_dotfiles fl = Rant::FileList.new fl.glob_dotfiles = false in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] assert fl.glob_dotfiles.equal?(fl) fl.glob "*.t" assert_entries [".a.t", "a.t"], fl end end def test_concat fl = Rant::FileList(["a"]) assert fl.concat(["b", "c"]).equal?(fl) assert_equal ["a", "b", "c"], fl.entries end def test_concat_after_include_with_ignore in_local_temp_dir do Rant::Sys.touch ["a.1", "a.2", "b.1", "b.2"] fl = Rant::FileList.new fl.ignore %r{^a.*1$} fl.include "*.[12]" fl.concat ["abc1", "abc3"] assert_entries ["a.2", "b.1", "b.2", "abc3"], fl assert_equal 3, fl.entries.index("abc3") end end def test_concat_with_ignore fl = Rant::FileList.new.ignore("foo") fl.concat ["foo", "bar", "bar/foo", "baz"] assert_entries ["bar", "baz"], fl end def test_inspect assert_nothing_raised do assert Rant::FileList.new.inspect.kind_of?(String) end end def test_object_inspect assert_nothing_raised do assert Rant::FileList.new.object_inspect.kind_of?(String) end end def test_to_s out = ext_rb_test <<-'EOF', :touch => ["a.c", "b.c"], :return => :stdout require 'rant/filelist' print "#{Rant::FileList['*.c']}" EOF assert_equal "a.c b.c", out end def test_no_dir_with_arg files = ["a/bc/d.t", "xy/f.t", "a.t"] out = ext_rb_test <<-'EOF', :touch => files, :return => :stdout require 'rant/filelist' puts Rant::FileList["**/*.t"].no_dir("bc") EOF lines = out.split(/\n/) assert_equal ["a.t", "xy/f.t"], lines.sort end def test_files files = ["a/bc/d.t", "xy/f.t", "a.t"] out = ext_rb_test <<-'EOF', :touch => files, :return => :stdout require 'rant/filelist' puts Rant::FileList["**/*"].exclude("*.rb").files EOF lines = out.split(/\n/) assert_entries ["a/bc/d.t", "a.t", "xy/f.t"], lines end def test_dirs files = ["a/bc/d.t", "xy/f.t", "a.t"] out = ext_rb_test <<-'EOF', :touch => files, :return => :stdout require 'rant/filelist' puts Rant::FileList["**/*"].exclude("*.rb").dirs EOF lines = out.split(/\n/) assert_entries ["a", "a/bc", "xy"], lines end def test_rant_version ext_rb_test <<-'EOF' require 'rant/filelist' if defined?(Rant::VERSION) && Rant::VERSION =~ /\d+\.\d+\.\d+/ exit 0 end exit 1 EOF end def test_rant_sys_regular_filename out = nil if Rant::Env.on_windows? out = ext_rb_test <<-'EOF', :return => :stdout require 'rant/filelist' print Rant::Sys.regular_filename('foo\bar/baz') EOF else out = ext_rb_test <<-'EOF', :return => :stdout require 'rant/filelist' print Rant::Sys.regular_filename('foo//bar/baz') EOF end assert_equal "foo/bar/baz", out end end rant-0.5.8/test/plugin/0000755000175000017500000000000010527254520014325 5ustar xavierxavierrant-0.5.8/test/plugin/configure/0000755000175000017500000000000010527254520016306 5ustar xavierxavierrant-0.5.8/test/plugin/configure/Rantfile0000755000175000017500000000131410527253216020000 0ustar xavierxavier#!/usr/bin/env ruby rd = File.expand_path("../../../lib") $:.unshift rd unless $:.include? rd task :hello do |t| sys.touch t.name end conf = plugin :Configure do |conf| conf.init_modes = [:default] conf.override_modes = nil conf.task # define a task named :configure conf.check "a" do |c| c.default "value_a" c.guess { "value_a_guess" } c.react { |val| p val } end conf.check "b" do |c| c.default "value_b" end conf.check "c" do |c| end conf.check "d" do |c| c.react { } end conf.check "e" do |c| c.guess { false } end end file conf["a"] do |t| sys.touch t.name end task :clean do sys.rm_f %w(config hello value_a value_a_guess) end rant-0.5.8/test/plugin/configure/test_configure.rb0000644000175000017500000000305510527253216021657 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' $testPluginConfigureDir = File.expand_path(File.dirname(__FILE__)) class TestPluginConfigure < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testPluginConfigureDir) unless Dir.pwd == $testPluginConfigureDir end def teardown capture_std do assert_equal(Rant.run("clean"), 0) end assert(!File.exist?("hello"), "hello should have been removed by `clean'") assert(!File.exist?("config"), "config should have been removed by `clean'") end def test_startup capture_std do assert_equal(Rant.run([]), 0) end assert(File.exist?("hello"), "target `hello' is first, and should have been run") assert(!File.exist?("config"), "config should have used defaults, no writing") end def test_configure capture_std do assert_equal(0, Rant.run("configure")) end assert(File.exist?("config"), "config should have been created by `configure'") capture_std do assert_equal(Rant.run("value_a_guess"), 0, "value_a_guess should be choosen based on config") end assert(File.exist?("value_a_guess")) end def test_configure_immediate capture_std do assert_equal(Rant.run(%w(configure value_a)), 0, "on task creation time, conf['a'] had the value `value_a'") end assert(!File.exist?("value_a_guess")) assert(File.exist?("value_a")) end def test_defaults capture_std do assert_equal(Rant.run("value_a"), 0) end assert(File.exist?("value_a")) assert(!File.exist?("value_a_guess")) end end rant-0.5.8/test/plugin/csharp/0000755000175000017500000000000010527254520015605 5ustar xavierxavierrant-0.5.8/test/plugin/csharp/src/0000755000175000017500000000000010527254520016374 5ustar xavierxavierrant-0.5.8/test/plugin/csharp/src/A.cs0000644000175000017500000000010210527253216017075 0ustar xavierxavier class A { public string MethodA() { return "A"; } } rant-0.5.8/test/plugin/csharp/src/B.cs0000644000175000017500000000010210527253216017076 0ustar xavierxavier class B { public string MethodB() { return "B"; } } rant-0.5.8/test/plugin/csharp/Hello.cs0000644000175000017500000000017410527253216017202 0ustar xavierxavier using System; class Hello { public static void Main(string[] args) { Console.WriteLine("Hello, world!"); } } rant-0.5.8/test/plugin/csharp/Rantfile0000644000175000017500000000100110527253216017265 0ustar xavierxavier plugin :Csharp src_files = FileList["src/*.cs"] gen Assembly, "hello.exe" do |t| t.sources = %w(Hello.cs) end gen Assembly, "AB.dll" => "hello.exe" do |t| t.sources = src_files t.debug = true t.optimize = true t.warnings = true end =begin assembly "hello.exe" => "Hello.cs" assembly "AB.dll" => src_files + ["hello.exe"] do |t| t.sources = src_files t.debug = true t.optimize = true t.warnings = true end =end task :clean do sys.rm_f FileList["*.{exe,dll,obj}"] end rant-0.5.8/test/plugin/csharp/test_csharp.rb0000644000175000017500000000624710527253216020463 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'rant/plugin/csharp' require 'tutil' $testPluginCsDir = File.expand_path(File.dirname(__FILE__)) $have_csc ||= Rant::Env.find_bin("csc") || Rant::Env.find_bin("cscc") || Rant::Env.find_bin("mcs") class TestPluginCsharp < Test::Unit::TestCase Assembly = Rant::Generators::Assembly Env = Rant::Env def setup # Ensure we run in test directory. Dir.chdir($testPluginCsDir) unless Dir.pwd == $testPluginCsDir end def teardown capture_std do assert(Rant.run("clean"), 0) end assert(Dir["*.{exe,dll,obj}"].empty?, "task :clean should remove exe, dll and obj files") end if $have_csc && ($have_csc !~ /mcs(\.exe)?$/) # TODO # Try to compile the "hello world" program. Requires cscc, csc # or mcs to be on your PATH. # TODO: In the following tests, when mcs is used as C# # compiler, the tasks will use cscc options anyway and the tests # fail. # Q: Why then do not fix the code? # A: The plugin code in general and especially the Csharp plugin # code is *really* crappy. I don't want to mess with it # anymore. I want to get rid of it. Consider it highly # deprecated. def test_hello capture_std do assert_equal(0, Rant.run([]), "first target, `hello.exe', should be compiled") end assert(File.exist?("hello.exe"), "hello.exe is the first target in Rantfile") if Env.on_windows? assert_equal(`hello.exe`.chomp, "Hello, world!", "hello.exe should print `Hello, world!'") elsif (ilrun = Env.find_bin("ilrun")) assert_equal(`#{ilrun} hello.exe`.chomp, "Hello, world!", "hello.exe should print `Hello, world!'") elsif (mono = Env.find_bin("mono")) assert_equal(`#{mono} hello.exe`.chomp, "Hello, world!", "hello.exe should print `Hello, world!'") else $stderr.puts "Can't run hello.exe for testing." end end def test_mcs old_csc = Assembly.csc mcs = Env.find_bin("mcs") unless mcs $stderr.puts "mcs not on path, will not test mcs" return end Assembly.csc = mcs test_opts Assembly.csc = old_csc end def test_opts capture_std do assert_equal(Rant.run("AB.dll"), 0) end assert(File.exist?("hello.exe"), "AB.dll depends on hello.exe") assert(File.exist?("AB.dll")) end def test_cscc old_csc = Assembly.csc cscc = Env.find_bin("cscc") unless cscc $stderr.puts "cscc not on path, will not test cscc" return end Assembly.csc = cscc test_opts Assembly.csc = old_csc end def test_csc old_csc = Assembly.csc csc = Env.find_bin("csc") unless csc $stderr.puts "csc not on path, will not test csc" return end Assembly.csc = csc test_opts Assembly.csc = old_csc end else def test_dummy # required to fool test/unit if no C# compiler available, # so we skip all real tests assert(true) # remove this method if a test is added that doesn't depend on # the C# compiler end print <<-EOF ************************************************************ * No C# compiler found on your path. Skipping all tests * * depending on a C# compiler. * ************************************************************ EOF end end rant-0.5.8/test/plugin/rantfile0000644000175000017500000000155310527253216016061 0ustar xavierxavier var.env "csc", "target" conf = plugin :Configure do |conf| conf.init_modes = [:explicit] conf.override_modes = [:env] conf.task # define a task named :configure conf.check "target" do |c| c.default "conf_csharp.exe" c.interact { c.prompt "Name for executable: " } end # define more checks end plugin :Csharp do |cs| cs.config = conf end conf.init task :default => ["conf_csharp.cs", conf["target"]] # from our example above gen Assembly, conf["target"] do |t| t.libs = %w(System.Xml.dll) t.sources = Dir["*.cs"] end file "conf_csharp.cs" do |t| File.open(t.name, "w") { |f| f << <<-EOF class ConfCSharp { public static void Main(string[] args) { System.Console.WriteLine("ConfCSharp"); } } EOF } end task :clean do sys.rm_f Dir["*.{exe,cs}"] + %w(config) end rant-0.5.8/test/plugin/test_conf_csharp.rb0000644000175000017500000000256110527253216020203 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' $testPluginConfCsDir = File.expand_path(File.dirname(__FILE__)) $have_csc ||= Rant::Env.find_bin("csc") || Rant::Env.find_bin("cscc") || Rant::Env.find_bin("mcs") class TestConfCsharp < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testPluginConfCsDir) unless Dir.pwd == $testPluginConfCsDir end def teardown capture_std do assert_equal(0, Rant.run("clean")) end end if $have_csc def test_defaults capture_std do assert_equal(0, Rant.run([])) end assert(test(?f, "conf_csharp.cs"), "conf_csharp.cs should be created by default task") assert(test(?f, "conf_csharp.exe"), "conf_csharp.exe should be compiled by default task") assert(test(?f, "config"), "config should habe been created by Configure plugin") end def test_with_explicit_target capture_std do assert_equal(0, Rant.run(%w(target=myprog.exe))) end assert(test(?f, "conf_csharp.cs")) assert(test(?f, "myprog.exe"), "myprog.exe was given as target name on commandline") assert(test(?f, "config")) File.delete "myprog.exe" capture_std do assert_equal(0, Rant.run([])) end assert(test(?f, "myprog.exe"), "target should be set to myprog.exe from config") end else # required to fool test/unit def test_dummy assert(true) end end end rant-0.5.8/test/project1/0000755000175000017500000000000010527254520014556 5ustar xavierxavierrant-0.5.8/test/project1/Rantfile0000644000175000017500000000401010527253217016242 0ustar xavierxavier require 'tutil' file "test_touch" do |t| sys.touch t.name end task :clean do sys.rm "test_touch" end task :create_target do sys.touch "target" end task :create_dep do sys.touch "dep" end file "target" => "dep" do |t| sys.touch t.name end file "t2" => ["dep1", "dep2"] do |t| sys.touch t.name end file "dep1" do |t| sys.touch t.name end file "dep2" do |t| sys.touch t.name end file "duplicate" do |t| sys.touch t.name sys.touch t.name + "1" end file "duplicate" do |t| sys.touch t.name sys.touch t.name + "2" end file "fallback" do # doesn't create our target... # so the next task with the same # name should be run sys.touch "fallback_" end file "fallback" do |t| sys.touch t.name end gen Directory, "dir/subdir" task :path => "dir/subdir" gen Directory, "dir/sub2" do |t| # This block should be called after the creation of the directory # `sub2' in `dir'. t.name == "dir/sub2" or t.fail sys.touch "#{t.name}/postprocess" end gen Directory, "dir/sub3" => "dep1" do |t| sys.touch "#{t.name}/postprocess" end file "tbe" => :dep1 do |t| sys.touch t.name timeout end enhance :tbe => "dep2" do |t| sys.touch "tbe2" timeout end # should generate warning because there is no existing task called # "nothing" and create the task "nothing" enhance :nothing task :order do |t| sys.touch t.name + "1" timeout end task :order do |t| sys.touch t.name + "2" timeout end file "incdep" do |t| sys.touch t.name end file "inc" => "incdep" do |t| sys.touch t.name end gen Task, :task_one do |t| t.needed do !test ?f, "one_target" end t.act do sys.touch "one_target" end end gen Task, :task_two => :task_one do |t| t.act { print t.name } end task :force_clean do sys.rm_f %w( test_touch target dep dep1 dep2 t2 duplicate duplicate1 duplicate2 fallback fallback_ order1 order2 tbe tbe2 inc incdep lt_target one_target ).find_all { |e| test(?e, e) } sys.rm_rf %w(dir) end # vim:ft=ruby rant-0.5.8/test/project1/test_project.rb0000644000175000017500000001422610527253217017617 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' # Ensure we run in testproject directory. $testProject1Dir = File.expand_path(File.dirname(__FILE__)) class TestProject1 < Test::Unit::TestCase def setup Dir.chdir($testProject1Dir) end def teardown assert_rant("force_clean") end def test_run out, err = assert_rant("test_touch") #"Exit code of rant should be 0.") assert(err =~ /\[WARNING\]/, "rant should print a warning because of enhance on non-existing task") assert(File.exist?("test_touch"), "file test_touch should have been created") assert_rant("clean") assert(!File.exist?("test_touch")) end def test_timedep assert_rant("create_target") assert(File.exist?("target")) timeout assert_rant("create_dep") assert(File.exist?("dep")) assert(Rant::Sys.uptodate?("dep", "target"), "`create_target' was run before `create_dep'") timeout assert_rant("target") assert(File.exist?("target")) assert(File.exist?("dep")) assert(Rant::Sys.uptodate?("target", "dep"), "`target' should be newer than `dep'") t1 = File.mtime "target" timeout assert_rant("target") assert_equal(t1, File.mtime("target"), "`target' was already up to date") end def test_two_deps assert_rant("t2") assert(File.exist?("t2"), "file `t2' should have been built") assert(File.exist?("dep1"), "dependancy `dep1' should have been built") assert(File.exist?("dep2"), "depandancy `dep2' should have been build") end def test_duplicate capture_std do assert_equal(Rant.run("duplicate"), 0) end assert(File.exist?("duplicate")) assert(File.exist?("duplicate1"), "duplicate1 should have been created as side effect " + "of running first task to build duplicate") assert(!File.exist?("duplicate2"), "the second task to build duplicate should have " + "been run, duplicate was already built") end def test_fallback capture_std do assert_equal(Rant.run("fallback"), 0) end assert(File.exist?("fallback_"), "should have been created as side-effect by fallback") assert(File.exist?("fallback"), "second task for `fallback' should have been run") end def test_directory capture_std do assert_equal(Rant.run("path"), 0) end assert(test(?d, "dir"), "dir should have been created as prerequisite of dir/subdir") assert(test(?d, "dir/subdir"), "dir/subdir should have been created as prerequisite of path") end def test_directory_postprocess capture_std do assert_equal(Rant.run("dir/sub2"), 0) end assert(test(?d, "dir/sub2"), "dir/sub2 should have been created by directory task") assert(test(?f, "dir/sub2/postprocess"), "dir/sub2/postprocess should have been created by block supplied to directory task") end def test_directory_postprocess_2 capture_std do assert_equal(Rant.run("dir/subdir"), 0) end assert(test(?d, "dir/subdir")) assert(!File.exist?("dir/sub2")) capture_std do assert_equal(Rant.run("dir/sub2"), 0) end assert(test(?f, "dir/sub2/postprocess"), "dir/sub2/postprocess should have been created by block supplied to directory task") end def test_directory_pre_postprocess capture_std do assert_equal(0, Rant.run("dir/sub3")) end assert(test(?d, "dir/sub3")) assert(test(?e, "dep1"), "dep1 is a prerequisite") assert(test(?e, "dir/sub3/postprocess")) old_mtime = File.mtime "dir/sub3" assert_equal(old_mtime, File.mtime("dep1")) timeout capture_std do assert_equal(0, Rant.run("dir/sub3")) end assert_equal(old_mtime, File.mtime("dir/sub3"), "no prerequisite requires update") assert_equal(old_mtime, File.mtime("dir/sub3/postprocess")) capture_std do assert_equal(0, Rant.run(%w(-a dep1))) assert(File.mtime("dep1") > old_mtime) timeout assert_equal(0, Rant.run("dir/sub3")) assert(File.mtime("dir/sub3") > old_mtime) assert(File.mtime("dir/sub3/postprocess") > old_mtime) end end def test_order capture_std do assert_equal(Rant.run("order"), 0) end assert(File.exist?("order1")) assert(File.exist?("order2")) assert(File.mtime("order1") < File.mtime("order2"), "tasks from same file should be run in definition order") end def test_enhance capture_std do assert_equal(Rant.run("tbe"), 0) end assert(File.exist?("dep1")) assert(File.exist?("dep2"), "dep2 was added as prerequisite to tbe by enhance") assert(File.exist?("tbe")) assert(File.exist?("tbe2"), "tbe2 should be created by enhance for tbe") assert(test(?<, "tbe", "tbe2"), "block added by enhance should run after \"normal\" block") end def test_enhance_nothing capture_std do assert_equal(Rant.run("nothing"), 0, "enhance should create new task if no task with given name exists") end end def test_incremental_build capture_std do assert_equal(Rant.run("inc"), 0) end assert(test(?f, "inc")) assert(test(?f, "incdep")) old_mtime = test(?M, "incdep") timeout capture_std do assert_equal(Rant.run(%w(--force-run incdep)), 0, "--force-run should unconditionally run `incdep'") end assert(old_mtime < test(?M, "incdep"), "incdep should have been updated by a forced run") capture_std do assert_equal(Rant.run("inc"), 0) end assert(old_mtime < test(?M, "inc"), "dependency `incdep' is newer, so `inc' should get rebuilt") end =begin def test_lighttask capture_std do assert_equal(Rant.run("lighttask"), 0) end assert(test(?e, "lt_target"), "lt_target should get `touched' by lighttask") end =end def test_task_gen_one_arg capture_std do assert_equal(Rant.run("task_one"), 0) end assert(test(?e, "one_target"), "one_target should get `touched' by task_one") old_mtime = test(?M, "one_target") timeout capture_std do assert_equal(Rant.run("task_one"), 0) end assert_equal(test(?M, "one_target"), old_mtime) end def test_task_gen_with_pre out, err = capture_std do assert_equal(0, Rant.run("task_two")) end assert(test(?e, "one_target"), "one_target should be created as prerequisite of task_two") assert_match(/task_two$/, out, "task_two action prints task name") end end rant-0.5.8/test/project2/0000755000175000017500000000000010527254520014557 5ustar xavierxavierrant-0.5.8/test/project2/sub1/0000755000175000017500000000000010527254520015431 5ustar xavierxavierrant-0.5.8/test/project2/sub1/Rantfile0000644000175000017500000000021010527253217017113 0ustar xavierxavier task :create_s1f1 do sys.touch "s1f1" end task :insub1_s1f1 do sys.touch "s1f1" end task :clean do sys.rm_f %w(s1f1) end rant-0.5.8/test/project2/sub2/0000755000175000017500000000000010527254520015432 5ustar xavierxavierrant-0.5.8/test/project2/buildfile0000644000175000017500000000030310527253217016437 0ustar xavierxavier file "b_f1" => ["r_f4", "b_f2"] do |t| sys.touch t.name end file "b_f2" do |t| sys.touch t.name end task :clean do |t| sys.rm_f Dir["b_f*"] end subdirs %w(sub1 sub2) :return_val rant-0.5.8/test/project2/root.rant0000644000175000017500000000041010527253217016425 0ustar xavierxavier file "r_f1" do |t| sys.touch t.name end file "r_f2" => "r_f1" do |t| sys.touch t.name end file "r_f3" => ["r_f2", :r_f4] do |t| sys.touch t.name end file "r_f4" => "r_f2" do |t| sys.touch t.name end task :clean do sys.rm_f Dir["r_f*"] end rant-0.5.8/test/project2/test_project.rb0000644000175000017500000000442610527253217017621 0ustar xavierxavier require 'test/unit' require 'tutil' # We require 'rant/rantlib' instead of 'rant', # which would cause the rant.rb (which is ment as a Rantfile) # to be loaded! require 'rant/rantlib' # Ensure we run in testproject directory. $testProject2Dir = File.expand_path(File.dirname(__FILE__)) class TestProject2 < Test::Unit::TestCase include Rant include ::Rant::Sys def app(*args) @app = ::Rant::RantApp.new @app.args.concat(args.flatten) @app end def setup Dir.chdir($testProject2Dir) unless Dir.pwd == $testProject2Dir end def teardown assert_rant(%w(-f root.rant -f buildfile clean sub1/clean)) assert(Dir["r_f*"].empty?, "r_f* files should have been removed by `clean'") assert(Dir["b_f*"].empty?, "b_f* files should have been removed by `clean'") assert(Dir["sub1/s*f*"].empty?, "sub1/s*f* files should have been removed by `clean'") end def test_use_first_task capture_std do assert_equal(app.run, 0, "run method of RantApp should return 0 on success") end assert(File.exist?("r_f1")) end def test_deps capture_std do assert_equal(app("r_f4").run, 0) end assert(File.exist?("r_f4")) assert(File.exist?("r_f2")) assert(File.exist?("r_f1")) assert(!File.exist?("r_f3")) end def test_load_rantfile capture_std do app("b_f2", "-C", $testProject2Dir) assert_equal(:return_val, @app.source("buildfile"), "source should return value of last expression in Rantfile") assert_equal(@app.run, 0) end assert(File.exist?("b_f2")) end def test_subdirs capture_std do assert_equal(0, app(%w(-f buildfile sub1/create_s1f1)).run) end assert(File.exist?("sub1/s1f1")) end def test_opt_directory app %w(insub1_s1f1 -C sub1) capture_std do assert_equal(@app.run, 0) end assert(Dir.pwd !~ /sub1$/, "rant should cd to original dir before returning from `run'") assert(test(?f, "sub1/s1f1"), "rant should cd to sub1 and run task insub1_s1f1") end def test_opth_directory app %w(insub1_s1f1) @app[:verbose] = 2 capture_std do assert_equal(@app.run("-Csub1"), 0) end assert(Dir.pwd !~ /sub1$/, "rant should cd to original dir before returning from `run'") assert(test(?f, "sub1/s1f1"), "rant should cd to sub1 and run task insub1_s1f1") end end rant-0.5.8/test/project_rb1/0000755000175000017500000000000010527254520015241 5ustar xavierxavierrant-0.5.8/test/project_rb1/bin/0000755000175000017500000000000010527254520016011 5ustar xavierxavierrant-0.5.8/test/project_rb1/bin/wgrep0000644000175000017500000000010210527253217017053 0ustar xavierxavier#!/usr/local/bin/ruby require 'wgrep' exit WGrep::WGrep.new.run rant-0.5.8/test/project_rb1/lib/0000755000175000017500000000000010527254520016007 5ustar xavierxavierrant-0.5.8/test/project_rb1/lib/wgrep.rb0000644000175000017500000000175010527253217017465 0ustar xavierxavier module WGrep class CommandError < StandardError end class WGrep attr_reader :word attr_reader :files attr_reader :count def initialize @word = nil @files = [] @count = 0 end def run(args = ARGV) process_args args @count = 0 if @files.empty? self.grep(@word, $stdin) { |line| print line @count += 1 } else @files.each { |fn| File.open(fn) { |file| self.grep(@word, file) { |line| print line @count += 1 } } } end $stderr.puts "Found `#@word' in #{@count} lines." 0 rescue CommandError => e $stderr.puts "Invalid commandline: #{e.message}" 1 end def process_args(args) if args.nil? || args.empty? raise CommandError, "No word given." end args = [args] unless Array === args @word, @files = args[0], args[1..-1] end def grep(word, stream) stream.each { |rec| yield(rec) if rec =~ /\b#{Regexp.escape(word)}\b/ } end end end rant-0.5.8/test/project_rb1/test/0000755000175000017500000000000010527254520016220 5ustar xavierxavierrant-0.5.8/test/project_rb1/test/tc_wgrep.rb0000644000175000017500000000072410527253217020364 0ustar xavierxavier require 'test/unit' require 'wgrep' require 'fileutils' class TestWGrep < Test::Unit::TestCase def test_run stdout = $stdout output = File.new "wgrep_out", "w" $stdout = output assert_equal(WGrep::WGrep.new.run(%w(Hello text)), 0, "Input to wgrep is ok, so `run' should return 0.") output.close $stdout = stdout lines = File.read("wgrep_out").split("\n") assert_equal(lines.size, 1) ensure FileUtils.rm_f "wgrep_out" $stdout = stdout end end rant-0.5.8/test/project_rb1/test/text0000644000175000017500000000006610527253217017133 0ustar xavierxavier This is a sample text for wgrep testing. Hello, you! rant-0.5.8/test/project_rb1/README0000644000175000017500000000041210527253217016120 0ustar xavierxavier = wgrep wgrep is a simple grep utility written in Ruby. It searches for one word in one or more files or in standard input and prints each line which contains the word. == Usage wgrep WORD [FILE(s)] == Running the tests Type `rant test' to run the unit tests. rant-0.5.8/test/project_rb1/rantfile0000644000175000017500000000124610527253217016775 0ustar xavierxavierimport %w(rubytest rubydoc rubypackage clean) lib_files = Dir["lib/**/*.rb"] dist_files = lib_files + %w(rantfile README test_project_rb1.rb) + Dir["{test,bin}/*"] desc "Run unit tests." gen RubyTest do |t| t.test_dir = "test" t.pattern = "tc_*.rb" end desc "Generate html documentation." gen RubyDoc do |t| t.opts = %w(--title wgrep --main README README) end desc "Create packages." gen RubyPackage, :wgrep do |t| t.version = "1.0.0" t.summary = "Simple grep program." t.files = dist_files t.bindir = "bin" t.executable = "wgrep" t.pkg_dir = "packages" t.package_task "pkg" end gen Clean var[:clean].include "doc", "packages", "*~" rant-0.5.8/test/project_rb1/test_project_rb1.rb0000644000175000017500000001025710527253217021046 0ustar xavierxavier require 'test/unit' require 'tutil' $testProjectRb1Dir = File.expand_path(File.dirname(__FILE__)) class TestProjectRb1 < Test::Unit::TestCase def setup @manifest = %w(bin lib test bin/wgrep lib/wgrep.rb test/text test/tc_wgrep.rb README test_project_rb1.rb rantfile) # Ensure we run in test directory. Dir.chdir($testProjectRb1Dir) end def teardown assert_rant("clean") manifest = @manifest.dup check_manifest "after clean: " end def check_manifest(msg_prefix = "") manifest = @manifest.dup #Dir["**/*"].each { |e| Rant::FileList["**/*"].shun(".svn").each { |e| assert(manifest.reject! { |mf| mf == e } , "#{msg_prefix}#{e} shouldn't exist according to manifest") } manifest.each { |e| assert(false, "#{msg_prefix}#{e} missing according to manifest") } end def test_doc have_rdoc = true begin require 'rdoc/rdoc' rescue LoadError have_rdoc = false end if have_rdoc assert_rant("doc") assert(test(?d, "doc"), "RDoc task should generate dir `doc'") assert(test(?f, "doc/index.html"), "doc/index.html should exist after `doc'") fl = Dir["doc/files/**/*"] assert(fl.find { |f| f =~ /wgrep/ }, "lib/wgrep.rb should get documented") assert(fl.find { |f| f =~ /README/ }, "README should be in html docs") else STDERR.puts "*** rdoc not available ***" out, err = assert_rant(:fail, "doc") lines = err.split(/\n/) lines.reject! { |s| s.strip.empty? } assert_equal(4, lines.size) assert_match(/ERROR.*in file.*line \d+/, lines[0]) assert_match(/RDoc not available/, lines[1]) assert_match(/doc.*fail/, lines[2]) end end def test_test assert_rant("test") end def test_package assert_rant("pkg") assert(test(?d, "packages"), "task `pkg' should create dir `packages'") have_gem = false pkg_base = "packages/wgrep-1.0.0" begin require 'rubygems' have_gem = true rescue LoadError end tar_fn = pkg_base + ".tar.gz" assert(test(?f, tar_fn), "tar is available, so a tar.gz should have been built") verify_tar "packages", "wgrep-1.0.0", ".tar.gz" assert(test(?f, pkg_base + ".zip"), "zip is available, so a zip should have been built") verify_zip "packages", "wgrep-1.0.0", ".zip" if have_gem assert(test(?f, pkg_base + ".gem"), "gem is available, so a gem should have been built") else puts "*** gem not available ***" end end def verify_tar(dir, pkg_base, ext) tar_fn = pkg_base + ext old_pwd = Dir.pwd FileUtils.cd dir tmp_dir = "_tmp_tar" tmp_dir.freeze FileUtils.mkdir tmp_dir FileUtils.cp tar_fn, tmp_dir FileUtils.cd tmp_dir do Rant::Sys.unpack_tgz tar_fn assert(test(?d, pkg_base), "`#{pkg_base}' should be root directory of all files in tar") FileUtils.cd pkg_base do check_manifest "tar content: " end end ensure FileUtils.cd old_pwd unless Dir.pwd == old_pwd FileUtils.rm_rf tmp_dir end def verify_zip(dir, pkg_base, ext) zip_fn = pkg_base + ext old_pwd = Dir.pwd FileUtils.cd dir tmp_dir = "_tmp_zip" tmp_dir.freeze FileUtils.mkdir tmp_dir FileUtils.cp zip_fn, tmp_dir FileUtils.cd tmp_dir do Rant::Sys.unpack_zip zip_fn assert(test(?d, pkg_base), "`#{pkg_base}' should be root directory of all files in zip") FileUtils.cd pkg_base do check_manifest "zip content: " end end ensure FileUtils.cd old_pwd unless Dir.pwd == old_pwd FileUtils.rm_rf tmp_dir end def test_rant_import require 'rant/import' out, err = capture_std do assert_equal(0, Rant::RantImport.run(%w(--auto make))) end # TODO: some out, err checking # run the monolithic rant script #out = `#{Rant::Env::RUBY} make -T` out = run_ruby("make", "-T") assert_equal(0, $?, "imported `rant -T' should return 0") assert_match(/\bpkg\b/, out, "imported `rant -T' should list described task `pkg'") ensure FileUtils.rm_f "make" end end rant-0.5.8/test/rant-import/0000755000175000017500000000000010527254520015303 5ustar xavierxavierrant-0.5.8/test/rant-import/Rantfile0000644000175000017500000000035010527253221016765 0ustar xavierxavier gen Action do sys.touch "action.t" end desc "Print hello." task :hello do |t| puts t.name end task :private do |t| puts t.name end task :sys_shun_demo do |t| puts(sys["**/*.s.t"].shun("a.t")) end # vim: ft=ruby rant-0.5.8/test/rant-import/test_rant-import.rb0000644000175000017500000001730510527253221021146 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import' $testRantImportDir ||= File.expand_path(File.dirname(__FILE__)) class TestRantImport < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($testRantImportDir) end def teardown Dir.chdir($testRantImportDir) FileUtils.rm_f Dir["ant*"] FileUtils.rm_f Dir["make*"] FileUtils.rm_rf Dir["*.t"] end def test_option_help out, err = capture_std do assert_equal(0, Rant::RantImport.new("--help").run) end assert err.empty? assert out.include?("rant-import") assert out.include?("Options are") assert out.include?("--help") end def test_option_version_and_V out, err = capture_std do assert_equal(0, Rant::RantImport.new("--version").run) end assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_equal "rant-import #{Rant::VERSION}", lines.first out2, err = capture_std do assert_equal(0, Rant::RantImport.new("-V").run) end assert err.empty? assert_equal out, out2 end def test_no_import run_import("--quiet", "make.rb") assert(test(?f, "make.rb")) content = File.read("make.rb") assert(!test(?f, "action.t")) assert_equal(run_rant("hello"), run_ruby("make.rb", "hello")) assert(test(?f, "action.t")) out, err = capture_std do assert_equal(1, Rant::RantImport.new("-iclean", "make.rb").run) end assert_equal(content, File.read("make.rb")) assert_match(/\[ERROR\].*\bmake\.rb\b.*\bexists\b.*--force/m, err) end def test_import_from_custom_lib FileUtils.mkpath "mylib.t/rant/import" open("mylib.t/rant/import/mygen.rb", "w") { |f| f << <<-EOF mygen = Object.new def mygen.rant_gen(rac, ch, args, &block) tn = args.first || "mygen" rac.task(:__caller__ => ch, tn => []) do |t| puts "Greetings from `" + t.name + "', generated by MyGen." rac.cx.sys.touch t.name end end Rant::Generators::MyGen = mygen EOF } open("mygen.rf.t", "w") { |f| f << <<-EOF $:.unshift "mylib.t" import "mygen" desc "task created by mygen" gen MyGen EOF } out, err = capture_std do assert_equal(0, Rant::RantApp.new.run("-fmygen.rf.t")) end assert_match(/Greetings.*mygen/, out) assert(test(?f, "mygen")) FileUtils.rm_f "mygen" assert(!test(?e, "mygen")) run_import("--quiet", "-fmygen.rf.t", "ant") assert(test(?f, "ant")) FileUtils.rm_r "mylib.t" out = run_ruby("ant", "-fmygen.rf.t") assert_match(/Greetings.*mygen/, out) assert(test(?f, "mygen")) ensure FileUtils.rm_f "mygen" end def test_import_subdir old_pwd = Dir.pwd FileUtils.mkdir "sub.t" Dir.chdir "sub.t" FileUtils.mkpath "lib.t/rant/import/sub" open("lib.t/rant/import/sub/t.rb", "w") { |f| f << <<-EOF module Rant::Generators module Sub class T def self.rant_gen(rac, ch, args, &blk) raise "no ch" unless Hash === ch rac.cx.task args.first do |t| puts args puts "block_given" if block_given? end end end end end EOF } write_to_file "rantfile", <<-EOF $:.unshift "lib.t" import "sub/t" gen Sub::T, "hello", "test" do end EOF out, err = assert_rant assert_match(/.*hello.*\n.*test.*\n.*block_given/, out) run_import("--quiet", "--auto", "ant") assert(test(?f, "ant")) FileUtils.rm_r "lib.t" out = run_ruby("ant") assert_match(/.*hello.*\n.*test.*\n.*block_given/, out) ensure Dir.chdir old_pwd FileUtils.rm_rf "sub.t" end def test_import_marked_require old_pwd = Dir.pwd FileUtils.mkdir "sub2.t" Dir.chdir "sub2.t" FileUtils.mkpath "lib.t/rant/import/sub2" FileUtils.mkdir "lib.t/misc" open("lib.t/misc/printer.rb", "w") { |f| f << <<-EOF def misc_print(*args) puts args.flatten.join('') end EOF } open("lib.t/rant/import/sub2/t.rb", "w") { |f| f << <<-EOF require 'misc/printer' # rant-import module Rant::Generators module Sub2 class T def self.rant_gen(rac, ch, args, &blk) rac.cx.task args.first do |t| misc_print(args) end end end end end EOF } write_to_file "root.rant", <<-EOF $:.unshift "lib.t" import "sub2/t" gen Sub2::T, "hello", "test" do end EOF out, err = assert_rant n_out = out.dup n_err = err.dup assert_match(/hellotest/, out) run_import("--quiet", "--auto", "ant.rb") run_import("--quiet", "--auto", "--zip", "make.rb") assert(test(?f, "ant.rb")) FileUtils.rm_r "lib.t" out = run_ruby("ant.rb") assert_equal(n_out, out) FileUtils.rm "ant.rb" assert(test(?f, "make.rb")) assert(test(?f, "make.rb.gz")) out = run_ruby("make.rb") assert_equal(n_out, out) ensure Dir.chdir old_pwd FileUtils.rm_rf "sub2.t" end def test_sys_shun FileUtils.mkdir "a.t" FileUtils.mkdir "b.t" FileUtils.touch %w(a.t/1.s.t a.t/2.s.t a.t/xy.s.t b.t/b.s.t) out = run_rant("-q", "sys_shun_demo") files = out.strip.split "\n" assert_equal(1, files.size) assert(files.include?("b.t/b.s.t")) run_import("-q", "--auto", "ant") assert(test(?f, "ant")) out = run_ruby("ant", "-q", "sys_shun_demo") files = out.strip.split "\n" assert_equal(1, files.size) assert(files.include?("b.t/b.s.t")) end def test_loaded_features FileUtils.mkdir "features.t" Dir.chdir "features.t" FileUtils.mkpath "rant/import" open "rant/import/t_loaded_features_foo.rb", "w" do |f| f << <<-EOF def t_loaded_features_foo puts "t_loaded_features_foo" end EOF end open "Rantfile", "w" do |f| f << <<-EOF import "t_loaded_features_foo" require 'rant/import/t_loaded_features_foo' task :default do t_loaded_features_foo end EOF end out, err = assert_rant assert_match(/t_loaded_features_foo/, out) assert(err.empty?) run_import("-q", "--auto", "make.rb") assert_exit assert(test(?f, "make.rb")) out = run_ruby("make.rb") assert_exit assert_match(/t_loaded_features_foo/, out) FileUtils.rm_rf "rant" out = run_ruby("make.rb") assert_exit assert_match(/t_loaded_features_foo/, out) end def test_init_import FileUtils.mkdir "init.t" Dir.chdir "init.t" FileUtils.mkpath "rant/import" open "rant/import/t_init_import_foo.rb", "w" do |f| f << <<-EOF module Rant def self.init_import_t_init_import_foo(rac, *rest) puts "init import t_init_import_foo" rac.cx.var["t_init_import_foo"] = "t_init_import_foo init" end end EOF end open "Rantfile", "w" do |f| f << <<-EOF import "t_init_import_foo" task :a do |t| puts var[:t_init_import_foo] end import "t_init_import_foo" EOF end out, err = assert_rant assert_match(/\Ainit import t_init_import_foo\nt_init_import_foo init\n\n?\Z/m, out) assert(err.empty?) run_import("-q", "--auto", "make.rb") assert_exit assert(test(?f, "make.rb")) out = run_ruby("make.rb") assert_exit assert_match(/\Ainit import t_init_import_foo\nt_init_import_foo init\n\n?\Z/m, out) FileUtils.rm_rf "rant" out = run_ruby("make.rb") assert_exit assert_match(/\Ainit import t_init_import_foo\nt_init_import_foo init\n\n?\Z/m, out) end end rant-0.5.8/test/subdirs/0000755000175000017500000000000010527254520014502 5ustar xavierxavierrant-0.5.8/test/subdirs/sub1/0000755000175000017500000000000010527254520015354 5ustar xavierxavierrant-0.5.8/test/subdirs/sub1/Rantfile0000644000175000017500000000031310527253217017042 0ustar xavierxavier file "t" do |t| sys.touch t.name end file "rootdep.t" => "@subdep.t" do |t| test(?f, "../subdep.t") || t.fail sys.touch t.name end task :clean do sys.rm_f Dir["*t"] end # vim:ft=ruby rant-0.5.8/test/subdirs/sub2/0000755000175000017500000000000010527254520015355 5ustar xavierxavierrant-0.5.8/test/subdirs/sub2/sub/0000755000175000017500000000000010527254520016146 5ustar xavierxavierrant-0.5.8/test/subdirs/sub2/sub/rantfile0000644000175000017500000000061710527253217017703 0ustar xavierxavier file "rootdep.t" do |t| sys.touch t.name end file "rootref.t" => "@t" do |t| test(?f, "../../t") || t.fail sys.touch t.name end gen Directory, "dt/dt" gen Task, "gt" => "dt" do |t| t.needed { !test(?f, t.name) } t.act { sys.touch t.name } end task :create_param do sys.touch var[:param] end task :clean do sys.rm_rf %w(dt) sys.rm_f Dir["*t"] end # vim:ft=ruby rant-0.5.8/test/subdirs/sub2/rantfile0000644000175000017500000000025010527253217017103 0ustar xavierxavier subdirs "sub" file "t" do |t| sys.touch t.name end file "subdep.t" => "sub/rootdep.t" do |t| sys.touch t.name end task :clean do sys.rm_f Dir["*t"] end rant-0.5.8/test/subdirs/Rantfile0000644000175000017500000000077610527253217016205 0ustar xavierxavier var "param" => "param_default.t" file "t" => "sub1/t" do |t| sys.touch t.name end file "2t" => "sub2/t" do |t| sys.touch t.name end file "subdep.t" do |t| sys.touch t.name end task :sub_sub => "sub2/sub/rootref.t" do |t| test(?f, "sub2/sub/rootref.t") or t.fail sys.touch "sub_sub.t" end #task :clean => ["sub1/clean", "sub2/clean"] do task :clean => ["sub2/clean", "sub1/clean", "sub2/sub/clean"] do sys.rm_f Dir["*t"] end subdirs FileList["sub*"].exclude("*.*") # vim:ft=ruby rant-0.5.8/test/subdirs/test_subdirs.rb0000644000175000017500000000574710527253217017560 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' # Ensure we run in testproject directory. $testSubdirsDir = File.expand_path(File.dirname(__FILE__)) class TestSubdirs < Test::Unit::TestCase def setup Dir.chdir($testSubdirsDir) unless Dir.pwd == $testSubdirsDir end def teardown capture_std do assert_equal(0, Rant.run("clean")) end created = Rant::FileList["**/*t"].shun(".svn")#Dir["**/*t"] assert(created.empty?) end def test_load capture_std do assert_equal(0, Rant.run("-T")) end end def test_sub_dep capture_std do assert_equal(0, Rant.run("t")) end assert(test(?f, "sub1/t"), "t depends on sub1/t") assert(test(?f, "t")) end def test_sub_dep2 capture_std do assert_equal(0, Rant.run("2t")) end assert(test(?f, "sub2/t")) assert(test(?f, "2t")) assert(!test(?e, "sub1/t")) end def test_sub_task_from_commandline capture_std do assert_equal(0, Rant.run("sub1/t")) end assert(test(?f, "sub1/t")) assert(!test(?e, "t")) capture_std do assert_equal(0, Rant.run("sub1/clean")) end assert(!test(?f, "sub1/t")) end def test_root_dep capture_std do assert_equal(0, Rant.run("sub1/rootdep.t")) end assert(test(?f, "subdep.t")) assert(test(?f, "sub1/rootdep.t")) end def test_sub_sub_dep capture_std do assert_equal(0, Rant.run("sub2/subdep.t")) end assert(test(?f, "sub2/subdep.t")) assert(test(?f, "sub2/sub/rootdep.t")) end def test_sub_sub_rootref capture_std do assert_equal(0, Rant.run("sub2/sub/rootref.t")) end assert(test(?f, "t")) assert(test(?f, "sub2/sub/rootref.t")) end def test_root_sub_sub_rootref capture_std do assert_equal(0, Rant.run("sub_sub")) end assert(test(?f, "sub_sub.t")) assert(test(?f, "sub2/sub/rootref.t")) assert(test(?f, "t")) end def test_import run_import %w(-q --auto ant) assert_equal($?, 0) capture_std do assert_nothing_raised { Rant::Sys.ruby("ant", "-q", "sub_sub") } end assert(test(?f, "sub_sub.t")) assert(test(?f, "sub2/sub/rootref.t")) assert(test(?f, "t")) ensure File.delete "ant" if File.exist? "ant" end def test_directory capture_std do assert_equal(0, Rant.run("sub2/sub/dt/dt")) end assert(test(?d, "sub2/sub/dt")) assert(test(?d, "sub2/sub/dt/dt")) capture_std do assert_equal(0, Rant.run("sub2/sub/dt/dt")) end end def test_gen_task capture_std do assert_equal(0, Rant.run("sub2/sub/gt")) end assert(test(?f, "sub2/sub/gt")) assert(test(?d, "sub2/sub/dt")) assert(!test(?d, "sub2/sub/dt/dt")) capture_std do assert_equal(0, Rant.run("sub2/sub/gt")) end assert(!test(?d, "sub2/sub/dt/dt")) end def test_param_default capture_std do assert_equal(0, Rant.run("sub2/sub/create_param")) end assert(test(?f, "sub2/sub/param_default.t")) end def test_param_override capture_std do assert_equal(0, Rant.run( %w(sub2/sub/create_param param=param.t))) end assert(test(?f, "sub2/sub/param.t")) end end rant-0.5.8/test/subdirs2/0000755000175000017500000000000010527254520014564 5ustar xavierxavierrant-0.5.8/test/subdirs2/sub00/0000755000175000017500000000000010527254520015515 5ustar xavierxavierrant-0.5.8/test/subdirs2/sub00/sub.rant0000644000175000017500000000015310527253215017173 0ustar xavierxavier define_print_task :a file "a.t" => "@a.t" do |t| sys.cp "../a.t", t.name end task :default => "a.t" rant-0.5.8/test/subdirs2/sub1/0000755000175000017500000000000010527254520015436 5ustar xavierxavierrant-0.5.8/test/subdirs2/sub1/sub.rant0000644000175000017500000000054110527253215017115 0ustar xavierxavier task :a do |t| puts t.full_name end desc "noop" task :b file "sub.t" => "@a.t" do |t| sys.cp sys.expand_path(t.source), t.name end gen Command, "c1.t", "@config.t", "$[sh_cat] ${<} > $(>)" gen Command, "c2.t", "@config.t", "$[sh_cat] ${prerequisites} > $(name)" gen Command, "c3.t", "a.r.t", "$[sh_cat] ${<} > $(>)" gen Directory, "dir.t" rant-0.5.8/test/subdirs2/root.rant0000644000175000017500000000173010527253216016437 0ustar xavierxavier import "autoclean", "subfile", "command" @sh_cat = "#{sys.sp Env::RUBY_EXE} -e \"print ARGF.read\"" @sh_puts = "#{sys.sp Env::RUBY_EXE} -e \"puts ARGV\"" def define_print_task(name) task name do |t| puts t.full_name end end desc "show full task name" task :a do |t| puts t.full_name end file "a.t" do |t| write t.name end file "b.t" do |t| write t.name end file "config.t" do |t| var :config => "config\n" write(t.name, var[:config]) end gen Command, "c1.t", ["sub1/c1.t"], "$[sh_puts] ${<} > $(>)" gen Rule, ".r.t" => ".s.t" do |target, sources| gen Command, target, sources, "$[sh_cat] $(<) > $(>)" end gen Action, /\.s\.t$/ do puts ".s.t action" end gen SubFile, "t/t", "a.t" => "b.t" do |t| write(t.name, "abc") end gen AutoClean subdirs sys["sub?"] # doesn't cover sub00, per intent def self.write(fn, content=nil) puts "writing to #{fn}" open fn, "w" do |f| f.write(content || "#{fn}\n") end end rant-0.5.8/test/subdirs2/test_subdirs2.rb0000644000175000017500000002133010527253216017705 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import/sys/more' $test_subdirs2_dir ||= File.expand_path(File.dirname(__FILE__)) class TestSubdirs2 < Test::Unit::TestCase include Rant::TestUtil def rootdir_rx(subdir=@subdir) /^\(root is #$test_subdirs2_dir, in #{subdir}\)$/ end def setup # Ensure we run in test directory. Dir.chdir($test_subdirs2_dir) end def teardown Dir.chdir($test_subdirs2_dir) assert_rant "autoclean" assert Rant::FileList["**/*.t*"].shun(".svn").empty? assert Rant::FileList["**/.rant.meta"].shun(".svn").empty? end def test_first out, err = assert_rant assert err.empty? assert_equal "a\n", out end def test_subdir_task_from_commandline out, err = assert_rant "sub1/a" assert err.empty? assert_equal "(in sub1)\nsub1/a\n", out end def test_first_in_subdir Dir.chdir "sub1" out, err = assert_rant assert err.empty? assert_equal "(root is #$test_subdirs2_dir, in sub1)\nsub1/a\n", out end def test_directory_in_subdir Dir.chdir "sub1" out, err = assert_rant "dir.t" assert err.empty? assert out.include?("mkdir") assert test(?d, "dir.t") out, err = assert_rant "dir.t" assert err.empty? assert !out.include?("mkdir") end def test_root_dir_task_in_subdir_from_commandline Dir.chdir "sub1" out, err = assert_rant "@a" assert err.empty? lines = out.split(/\n/) assert_match(/^\(root is .*\)$/, lines[0]) assert lines.size < 4 assert_equal "a", lines.last end def test_root_dir_task_from_commandline out, err = assert_rant "@a" assert err.empty? assert_equal "a\n", out end def test_fail_no_subdir_task out, err = assert_rant :fail, "sub00/a.t" assert out.empty? assert_match(/\[ERROR\]/, err) assert_match(/\bsub00\/a\.t\b/, err) end def test_ensure_read_sub_rant Dir.chdir "sub00" out, err = assert_rant assert test(?f, "a.t") assert test(?f, "../a.t") out, err = assert_rant :fail, "autoclean" assert_equal "(root is #$test_subdirs2_dir, in sub00)\n", out assert_rant "@autoclean" assert Dir["*.t"].empty? end def test_show_descriptions out, err = assert_rant "-T" assert err.empty? assert_equal <\s+a$/, lines[1]) assert_match(/^rant b\s+#\s+noop$/, lines[2]) assert_match(/^rant @a\s+#\s+show full task name$/, lines[3]) end def test_opt_look_up in_local_temp_dir "t" do out, err = assert_rant :fail assert out.empty? assert_match(/\[ERROR\].*no rantfile/i, err) out, err = assert_rant "--look-up" assert err.empty? assert_equal "a", out.split(/\n/)[2] in_local_temp_dir "t" do out, err = assert_rant "--look-up", "a.t" assert err.empty? lines = out.split(/\n/) assert_equal 4, lines.size assert_match(rootdir_rx("t/t"), lines[0]) assert_equal "(in #$test_subdirs2_dir)", lines[1] assert_equal "writing to b.t", lines[2] assert_equal "writing to t/t/a.t", lines[3] assert !test(?e, "../../a.t") assert test(?f, "a.t") assert test(?f, "../../b.t") out, err = assert_rant "--look-up", "a.t" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match rootdir_rx("t/t"), lines.first out, err = assert_rant "-u", "@autoclean" assert !test(?e, "../../b.t") assert !test(?e, "a.t") end end end def test_opt_look_up_from_subdir Dir.chdir "sub1" out, err = assert_rant "-u", "dir.t" assert err.empty? assert out.include?("mkdir") assert test(?d, "dir.t") out, err = assert_rant "-u", "dir.t" assert err.empty? assert !out.include?("mkdir") end def test_opt_cd_parent in_local_temp_dir "with space.t" do out, err = assert_rant "--cd-parent" assert err.empty? lines = out.split(/\n/) assert_equal 2, lines.size assert_equal "(in #$test_subdirs2_dir)", lines[0] assert_equal "a", lines[1] in_local_temp_dir "a" do orig_pwd = Dir.pwd out, err = assert_rant "-c", "a.t" assert_equal orig_pwd, Dir.pwd assert err.empty? lines = out.split(/\n/) assert_equal 2, lines.size assert_equal "(in #$test_subdirs2_dir)", lines[0] assert_equal "writing to a.t", lines[1] assert test(?f, "../../a.t") assert_equal "a.t\n", File.read("../../a.t") end end end def test_opt_cd_parent_from_dir_with_rantfile out, err = assert_rant "-c" assert err.empty? assert_equal "a\n", out end def test_opt_cd_parent_from_subdir Dir.chdir "sub1" out, err = assert_rant "-c" assert err.empty? lines = out.split(/\n/) assert_equal 2, lines.size assert_equal "(in #$test_subdirs2_dir)", lines[0] assert_equal "a", lines[1] end def test_opt_cd_parent_from_subdir_sub Dir.chdir "sub1" in_local_temp_dir do out, err = assert_rant "-c" assert err.empty? lines = out.split(/\n/) assert_equal 2, lines.size assert_equal "(in #$test_subdirs2_dir)", lines[0] assert_equal "a", lines[1] end end def test_rant_import run_import("-q", "--auto", "rant.t") assert_exit out = run_ruby("rant.t", "a.t") assert_exit assert test(?f, "a.t") out = run_ruby("rant.t", "a.t") assert_exit assert out.empty? Rant::Sys.safe_ln "rant.t", "sub1" Dir.chdir "sub1" out = run_ruby("rant.t", "dir.t") assert_exit assert test(?d, "dir.t") out = run_ruby("rant.t", "@autoclean") assert !test(?e, "dir.t") ensure Dir.chdir $test_subdirs2_dir Rant::Sys.rm_f Dir["**/rant.t"] end def test_expand_path_md5_in_sub1 Dir.chdir "sub1" out, err = assert_rant "-imd5", "sub.t" assert err.empty? assert !out.empty? assert test(?f, "sub.t") assert_equal "a.t\n", File.read("sub.t") out, err = assert_rant "-imd5", "sub.t" assert err.empty? assert out !~ /writing to/ assert out !~ /\bcp\b/ Dir.chdir ".." out, err = assert_rant "-imd5", "sub1/sub.t" assert err.empty? assert out.empty? assert_rant "-imd5", "autoclean" end def test_expand_path out, err = assert_rant "sub1/sub.t" assert err.empty? assert test(?f, "sub1/sub.t") assert_equal "a.t\n", File.read("sub1/sub.t") out, err = assert_rant "sub1/sub.t" assert err.empty? assert out.empty? end def test_define_in_current_subdir Dir.chdir "sub00" out, err = assert_rant "a" assert err.empty? assert_equal "sub00/a", out.split(/\n/).last end def test_command out, err = assert_rant "c1.t" assert err.empty? assert_file_content "config.t", "config\n" assert_file_content "sub1/c1.t", "config\n" assert_file_content "c1.t", "sub1/c1.t\n" out, err = assert_rant "c1.t" assert err.empty? assert out.empty? end def test_command_in_sub Dir.chdir "sub1" out, err = assert_rant "c2.t" assert_file_content "../config.t", "config\n" assert_file_content "c2.t", "config\n" out, err = assert_rant "c2.t" assert err.empty? assert out.split(/\n/).size < 2 end def test_custom_rule_task Rant::Sys.write_to_file "sub1/a.s.t", "foo\n" out, err = assert_rant "sub1/c3.t" assert err.empty? assert_match(/\.s\.t action/, out) assert_file_content "sub1/c3.t", "foo\n" ensure Rant::Sys.rm_f ["sub1/a.s.t"] end end rant-0.5.8/test/units/0000755000175000017500000000000010527254520014171 5ustar xavierxavierrant-0.5.8/test/units/csharp/0000755000175000017500000000000010527254520015451 5ustar xavierxavierrant-0.5.8/test/units/csharp/test_base_compiler_adapter.rb0000644000175000017500000000600710527253220023340 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/csharp/base_compiler_adapter') class TestBaseCompilerAdapter < Test::Unit::TestCase begin require 'mocha' @@mocha_required = true rescue LoadError @@mocha_required = false end def setup @c = Rant::CSharp::BaseCompilerAdapter.new("testbin") end # Tests def test_initialize_should_fail_with_blank_bin assert_raise(Exception) { Rant::CSharp::BaseCompilerAdapter.new } assert_raise(Exception) { Rant::CSharp::BaseCompilerAdapter.new("") } end def test_cmd_should_fail_with_blank_target assert_raise(Exception) { @c.cmd("", nil, nil) } end def test_cmd_should_create_compile_line assert_equal "testbin out:outfile target:library ", @c.cmd("outfile", {}, mock_context) end def test_cmd_should_create_complex_compile_line args = {:sources => ["a", "b"], :libs => ["c", "d"], :target => "exe", :checked => false} cmd = @c.cmd("outfile", args, mock_context) # Order of arguments is undefined, so use regex to test assert_regex "^testbin out:outfile " , cmd assert_regex " target:exe " , cmd assert_regex " checked- " , cmd assert_regex " libs:c libs:d " , cmd assert_regex "a b$" , cmd end def test_should_provide_default_map_target assert_equal "string 1", @c.map_target("string 1") assert_equal "string 2", @c.map_target("string 2") end def test_should_guess_module_target assert_equal "module", @c.guess_target("outfile.netmodule") end def test_should_guess_exe_target_as_winexe assert_equal "winexe", @c.guess_target("outfile.exe") end def test_should_default_to_library_target assert_equal "library", @c.guess_target("outfile.dll") assert_equal "library", @c.guess_target("outfile") end if @@mocha_required def test_should_escape_sources_as_string sys = mock() sys.expects(:sp).with("outfile").returns("outfile") sys.expects(:sp).with("a").returns("a") context = mock() context.expects(:sys).at_least_once.returns(sys) @c.cmd("outfile", {:sources => "a"}, context) end def test_should_escape_sources_as_array sys = mock() sys.expects(:sp).with("outfile").returns("outfile") sys.expects(:sp).with("a").returns("a").times(2) context = mock() context.expects(:sys).at_least_once.returns(sys) @c.cmd("outfile", {:sources => ["a", "a"]}, context) end else print "**** Could not run all tests for BaseCompilerAdapter, " + "requires mocha libary ****\n" end # Helpers def mock_context Struct.new(:sys).new(MockSys.new) end def assert_regex regex, actual assert actual =~ Regexp.new(regex), "<\"#{actual}\"> did not match /#{regex}/" end # Mocks class MockSys def sp(file) file end end end rant-0.5.8/test/units/csharp/test_compiler_adapter_factory.rb0000644000175000017500000000343710527253220024101 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/csharp/compiler_adapter_factory') class TestCompilerAdapterFactory < Test::Unit::TestCase # Tests def test_should_create_compiler_from_bin_in_path c = Rant::CSharp::CompilerAdapterFactory.new(mock_context(MockEnv.new)) c.compiler_map = {"testbin" => MockCompiler } assert c.compiler.kind_of?(MockCompiler), "Compiler was not an instance of MockCompiler" end def test_should_raise_exception_if_no_compiler_found mock_context = Struct.new(:env).new(MockEnvNoBin.new) c = Rant::CSharp::CompilerAdapterFactory.new(mock_context(MockEnvNoBin.new)) assert_raise(Exception) { c.compiler } end def test_should_cache_compiler mock_context = Struct.new(:env).new(MockEnv.new) c = ::Rant::CSharp::CompilerAdapterFactory.new(mock_context) c.compiler_map = {"testbin" => MockCompiler } c.compiler # First call to populate cache c.compiler_map = nil assert c.compiler.kind_of?(MockCompiler), "Complier was not cached" end def test_should_have_valid_default_compiler_map c = Rant::CSharp::CompilerAdapterFactory.new(nil) assert c.compiler_map.respond_to?("[]"), "Default compiler map is invalid" c.compiler_map.each_pair do |key, value| assert value.respond_to?(:new), "Default compiler map contains an invalid pair: #{key} => #{value}" end end # Helpers def mock_context(env) Struct.new(:env).new(env) end # Mocks class MockEnv def find_bin(bin) bin == "testbin" end end class MockEnvNoBin def find_bin(bin) false end end class MockCompiler def initialize context end end end rant-0.5.8/test/units/csharp/test_csc_compiler.rb0000644000175000017500000000114110527253220021470 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/csharp/csc_compiler') class TestCscCompiler < Test::Unit::TestCase def setup @c = Rant::CSharp::CscCompiler.new end # Tests def test_initialize_should_provide_csc_bin assert_equal "csc /nologo", @c.bin end def test_initialize_should_allow_alternate_bin c = Rant::CSharp::CscCompiler.new("altbin") assert_equal "altbin", c.bin end def test_argument_prefix_should_be_slash assert_equal "/", @c.argument_prefix end end rant-0.5.8/test/units/csharp/test_gmsc_compiler.rb0000644000175000017500000000100410527253220021647 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/csharp/gmcs_compiler') class TestGmcsCompiler < Test::Unit::TestCase def setup @c = Rant::CSharp::GmcsCompiler.new end # Tests def test_initialize_should_provide_gmcs_bin assert_equal "gmcs", @c.bin end def test_initialize_should_allow_alternate_bin c = Rant::CSharp::GmcsCompiler.new("altbin") assert_equal "altbin", c.bin end end rant-0.5.8/test/units/csharp/test_msc_compiler.rb0000644000175000017500000000113010527253220021500 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/csharp/mcs_compiler') class TestMcsCompiler < Test::Unit::TestCase def setup @c = Rant::CSharp::McsCompiler.new end # Tests def test_initialize_should_provide_mcs_bin assert_equal "mcs", @c.bin end def test_initialize_should_allow_alternate_bin c = Rant::CSharp::McsCompiler.new("altbin") assert_equal "altbin", c.bin end def test_argument_prefix_should_be_dash assert_equal "-", @c.argument_prefix end end rant-0.5.8/test/units/import/0000755000175000017500000000000010527254520015503 5ustar xavierxavierrant-0.5.8/test/units/import/test_csharp.rb0000644000175000017500000000674410527253220020356 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') module Rant::Generators; end; require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/import/csharp') begin require 'mocha' class TestCSharp < Test::Unit::TestCase def setup @csharp = Rant::Generators::CSharp end # Tests def test_should_require_sources rant = mock() rant.expects(:abort_at ).with({}, "CSharp requires sources" ).raises(Exception ).times(2) assert_raise(Exception) { @csharp.rant_gen(rant, {}, ["target", {}]) } assert_raise(Exception) { @csharp.rant_gen(rant, {}, ["target", {:sources => []}]) } end def test_should_require_target rant = mock() rant.expects(:abort_at ).with({}, "CSharp requires a target" ).raises(Exception) assert_raise(Exception) { @csharp.rant_gen(rant, {}, ["", {:sources => ["a"]}]) } end def test_should_create_file_task_depending_on_sources rant = mock() rant.expects(:file ).with({"target" => ["a"]}) @csharp.rant_gen(rant, nil, ["target", {:sources => ["a"]}]) end def test_file_task_should_depend_on_resources rant = mock() rant.expects(:file ).with({"target" => ["a", "b"]}) @csharp.rant_gen(rant, nil, ["target", {:sources => ["a"], :resources => ["b"]}]) end def test_file_task_should_depend_on_libs rant = mock() rant.expects(:file ).with({"target" => ["a", "b"]}) @csharp.rant_gen(rant, nil, ["target", {:sources => ["a"], :libs => ["b"]}]) end def test_file_task_should_call_shell_command sys = mock() sys.expects(:sh).with("command") context = mock() context.expects(:sys).returns(sys) rant = mock() rant.expects(:file).yields("target") rant.expects(:context).at_least_once.returns(context) compiler = mock() compiler.expects(:cmd).returns("command") @csharp.rant_gen(rant, nil, ["target", {:sources => ["a"], :compiler => compiler}]) end def test_get_compiler_should_use_given_compiler compiler = Object.new assert_equal compiler, @csharp.get_compiler(nil, {:compiler => compiler}), "Does not use given compiler" end def test_get_compiler_should_create_instance_of_given_compiler_class klass = Class.new(Object) ret = @csharp.get_compiler(nil, {:compiler => klass}) assert ret.kind_of?(klass), "Created compiler was of class #{ret.class}, should have been #{klass}" end def test_get_compiler_should_remove_compiler_from attributes args = {:compiler => Object} @csharp.get_compiler(nil, args) assert args.empty?, ":compiler should have been removed from arguments" end def test_get_compiler_should_use_default_if_no_compiler_specified assert_equal "default_compiler", @csharp.get_compiler(nil, {}) end end # Mocks class Rant::Generators::CSharp class MockFactory def compiler context "default_compiler" end end def self.compiler_adapter_factory MockFactory.new end end rescue LoadError print "**** Could not test CSharp, requires mocha libary ****\n" end rant-0.5.8/test/units/import/test_nunittest.rb0000644000175000017500000000610610527253220021123 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') module Rant::Generators; end; require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/import/nunittest') begin require 'mocha' class TestNUnitTest < Test::Unit::TestCase def setup @nunit = Rant::Generators::NUnitTest end # Tests def test_should_require_dlls rant = mock() rant.expects(:abort_at ).with({}, "NUnitTest requires dlls" ).raises(Exception ).times(2) assert_raise(Exception) { @nunit.rant_gen(rant, {}, ["target", {}]) } assert_raise(Exception) { @nunit.rant_gen(rant, {}, ["target", {:dlls => []}]) } end def test_should_require_task_name rant = mock() rant.expects(:abort_at ).with({}, "NUnitTest requires a task name" ).raises(Exception) assert_raise(Exception) { @nunit.rant_gen(rant, {}, ["", {:dlls => ["a"]}]) } end def test_should_create_task rant = mock() rant.expects(:task) @nunit.rant_gen(rant, nil, ["target", {:dlls => ["a"]}]) end def test_task_should_call_shell_command dll = "a" sys = mock() sys.expects(:sh).with("command a") sys.expects(:sp).returns(dll) context = mock() context.expects(:sys).returns(sys).at_least_once rant = mock() rant.expects(:task).yields("target") rant.expects(:context).at_least_once.returns(context) @nunit.rant_gen(rant, nil, ["target", {:dlls => dll, :bin => "command"}]) end def test_should_accept_dlls_as_filelist dll = mock() dll.expects(:arglist).returns("a") assert_equal "a", @nunit.process_dlls(nil, dll) end def test_should_accept_dlls_as_array dll = ["a", "a"] sys = mock() sys.expects(:sp).returns("a").times(2) context = mock() context.expects(:sys).returns(sys).at_least_once rant = mock() rant.expects(:context).at_least_once.returns(context) assert_equal "a a", @nunit.process_dlls(rant, dll) end def test_should_accept_dll_as_string dll = "a" sys = mock() sys.expects(:sp).returns("a").times(1) context = mock() context.expects(:sys).returns(sys).at_least_once rant = mock() rant.expects(:context).at_least_once.returns(context) assert_equal "a", @nunit.process_dlls(rant, dll) end def test_default_command_is_nunit_console sys = mock() sys.expects(:sp).returns("a").at_least_once sys.expects(:sh).returns("nunit-console /nologo a") context = mock() context.expects(:sys).returns(sys).at_least_once rant = mock() rant.expects(:task).yields("target") rant.expects(:context).at_least_once.returns(context) @nunit.rant_gen(rant, nil, ["target", {:dlls => "a"}]) end end rescue LoadError print "**** Could not test NUnitTest, requires mocha libary ****\n" end rant-0.5.8/test/units/import/test_resgen.rb0000644000175000017500000000556510527253220020361 0ustar xavierxavierrequire File.expand_path(File.dirname(__FILE__) + '/../csharp_test_helper') module Rant::Generators; end; require File.expand_path(File.dirname(__FILE__) + '/../../../lib/rant/import/resgen') begin require 'mocha' class TestResgen < Test::Unit::TestCase def setup @resgen = Rant::Generators::Resgen end def test_should_create_rule_gen rant = rule_gen_rant_mock(/(.+?)\.resources/) @resgen.rant_gen(rant, nil, [{}]) end def test_should_use_namespace rant = rule_gen_rant_mock(/Rant\.Test\.(.+?)\.resources/) @resgen.rant_gen(rant, nil, [{:namespace => 'Rant.Test'}]) end def test_should_use_build_dir rant = rule_gen_rant_mock(/build\/(.+?)\.resources/) @resgen.rant_gen(rant, nil, [{:build_dir => 'build'}]) end def test_src_matches_resx rant = rule_gen_rant_mock do |src| "a.resx" == src.call("a.resources")[0] end @resgen.rant_gen(rant, nil, [{}]) end def test_src_matches_resx_with_namespace rant = rule_gen_rant_mock do |src| "a/b.resx" == src.call("a.b.resources")[0] end @resgen.rant_gen(rant, nil, [{}]) end def test_src_matches_resx_with_build_dir rant = rule_gen_rant_mock do |src| "a.resx" == src.call("build/a.resources")[0] end @resgen.rant_gen(rant, nil, [{:build_dir => "build"}]) end def test_rule_should_call_shell_command task = Struct.new(:source, :name).new("source", "name") sys = mock() sys.expects(:sp).returns("source,name") sys.expects(:sh).with("resgen /useSourcePath /compile source,name") context = mock() context.expects(:gen).yields(task) context.expects(:sys).returns(sys).at_least_once rant = mock() rant.expects(:context).at_least_once.returns(context) @resgen.rant_gen(rant, nil, [{}]) end def test_should_escape_source_and_name task = Struct.new(:source, :name).new("sou rce", "nam e") sys = mock() sys.expects(:sp).with("sou rce,nam e").returns("") sys.expects(:sh) context = mock() context.expects(:gen).yields(task) context.expects(:sys).returns(sys).at_least_once rant = mock() rant.expects(:context).at_least_once.returns(context) @resgen.rant_gen(rant, nil, [{}]) end # Helpers def rule_gen_rant_mock regex = nil context = mock() context.expects(:gen).with() do |klass, task| ret = (klass == Rant::Generators::Rule) ret &&= (!regex || (task.keys[0].to_s == regex.to_s)) ret &&= (!block_given? || yield(task.values[0])) ret end rant = mock() rant.expects(:context).returns(context) rant end end # Mocks class Rant::Generators::Rule end rescue LoadError print "**** Could not test Resgen, requires mocha libary ****\n" end rant-0.5.8/test/units/csharp_test_helper.rb0000644000175000017500000000015210527253220020366 0ustar xavierxavier# Unit assumes this module/class is defined module Rant; class FileList; end; end; require 'test/unit' rant-0.5.8/test/Rantfile0000644000175000017500000000345310527253221014520 0ustar xavierxavier gen Action do puts "running action" if var[:act_verbose] end task :do_nothing file "auto.rf" do |t| open(t.name, "w") { |f| f << <<-EOF task "auto.t" do |t| sys.touch t.name end EOF } end file "version.t" do |t| open(t.name, "w") { |f| f.puts "1.0" } end gen Action do rant.build "version.t" end auto_t_task = source "auto.rf" rant.abort unless auto_t_task.name == "auto.t" task :clean do sys.rm_f %w(auto.t auto.rf version.t) sys.rm_rf %w(basedir.t) end gen Directory, "tmp.t" task "tmp.t/Rantfile" => "tmp.t" do |t| open(t.name, "w") { |f| f << <<-EOF file "test.t" do |t| sys.touch t.name end EOF } end task :subdir_tmp do subdirs %w(tmp.t) end task :build_test_t do |t| rant.build "tmp.t/test.t" # just ensure we're NOT in the tmp.t directory #STDERR.puts Dir.pwd test(?d, "tmp.t") or t.fail end desc "Make some path (basedir.t/a/b)." gen Directory, "basedir.t", "a/b" gen Action do if var[:make_path] make Directory, "basedir.t" make "basedir.t/a/b" end end task :make_file do |t| make "make_file.t" do |t| sys.touch t.name end end file "make_files_dep.t" do |t| sys.touch t.name end gen Action do if var[:make_files] make "make_files.t" => "make_files_dep.t" do |t| sys.touch t.name end end if var[:make_gen_with_block] import "subfile" make SubFile, "a.t/a.t" do |t| sys.touch t.name end end end task :dep_on_make_files => ["make_files.t"] task "print_name.t" do |t| puts t.full_name end file "depends_name.t" => "print_name.t" do |t| import "sys/more" sys.write_to_file t.name, File.read(t.source) + "a\n" end task "call-make" do |t| make "print_name.t" puts t.full_name end # vim: ft=ruby rant-0.5.8/test/action.rant0000644000175000017500000000057610527253221015177 0ustar xavierxavier import "sys/more" gen Action, /\.t$/ do puts 'executing action: rx /\.t$/' source "action.t.rant" end file "action.t.rant" do |t| sys.write_to_file t.name, <<-EOF file "a.t" do |t| sys.touch t.name end file "b.t" do |t| sys.touch t.name end EOF end file "b.tt" do |t| sys.touch t.name end subdirs "sub.t" if test ?d, "sub.t" rant-0.5.8/test/dyn_dependencies.rf0000644000175000017500000000065210527253221016660 0ustar xavierxavier import "sys/more", "autoclean" @print = lambda { |t| puts t.name } @write = lambda { |t| sys.write_to_file(t.name, (var[t.name[0,1]]||t.name)) } task :A => :B, &@print task :B do |t| enhance :A => [:C, :D] @print[t] end task :C, &@print task :D, &@print file "a.t" => "b.t", &@write file "b.t" do |t| enhance "a.t" => ["c.t", "d.t"] @write[t] end file "c.t", &@write file "d.t", &@write gen AutoClean rant-0.5.8/test/rule.rf0000644000175000017500000000137710527253221014334 0ustar xavierxaviergen Rule, '.o' => '.c' do |t| sys "cc -c -o #{t.name} #{t.source}" end gen Rule, :tt => :t do |t| t.source =~ /\.t$/ or t.fail sys.touch t.name end gen Rule, :t do |t| test(?e, t.name) && t.fail sys.touch t.name end src = lambda { |target| [target.sub_ext("t"), target.sub_ext("tt")] } gen Rule, :lt => src do |t| t.prerequisites[0] =~ /\.t$/ or t.fail t.prerequisites[1] =~ /\.tt$/ or t.fail sys.touch t.name end src = lambda { |target| target + "t" } gen Rule, :rt => src do |t| t.prerequisites[0] =~ /\.rtt$/ or t.fail t.source =~ /\.rtt$/ or t.fail sys.touch t.name end gen Rule, :ett => :et do |t| sys.touch t.name end if var[:enhance_t] enhance "eh.t" do puts "eh.t created" end end # vim:ft=ruby rant-0.5.8/test/standalone.rf0000644000175000017500000000015010527253221015501 0ustar xavierxavier require 'rant' task :t_standalone do |t| puts t.name end if $0 == __FILE__ exit Rant.run end rant-0.5.8/test/test_action.rb0000644000175000017500000000472210527253221015672 0ustar xavierxavier require 'test/unit' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestAction < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($testDir) end def teardown Rant::Sys.rm_f Rant::FileList["*.t", "*.tt", "action.t.rant"] end def test_one_arg_regex out, err = assert_rant "-faction.rant", "a.t", "b.t" assert err.empty? lines = out.split(/\n/) assert_equal 4, lines.size assert_equal 'executing action: rx /\.t$/', lines[0] assert_match(/action\.t\.rant/, lines[1]) assert_match(/touch\s+a\.t/, lines[2]) assert_match(/touch\s+b\.t/, lines[3]) end def test_one_arg_regex_remove_action out, err = assert_rant :tmax_1, :fail, "-faction.rant", "a.t", "b.t", "c.t" #assert err.empty? lines = out.split(/\n/) assert_equal 4, lines.size assert_equal 'executing action: rx /\.t$/', lines[0] assert_match(/action\.t\.rant/, lines[1]) assert_match(/touch\s+a\.t/, lines[2]) assert_match(/touch\s+b\.t/, lines[3]) end def test_no_execute out, err = assert_rant "-faction.rant", "b.tt" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/touch\s+b\.tt/, lines[0]) out, err = assert_rant "-faction.rant", "b.tt" assert err.empty? assert out.empty? end def test_pwd Rant::Sys.mkdir "sub.t" Rant::Sys.write_to_file "sub.t/sub.rant", <<-EOF task :a do puts Dir.pwd make "a.t" rescue nil puts Dir.pwd make "@a.t" end EOF out, err = assert_rant "-faction.rant", "sub.t/a" assert err !~ /ERROR/i lines = out.split(/\n/) assert_equal 7, lines.size assert_match(/\bin\b/, lines[0]) subdir = File.join($testDir, "sub.t") assert_equal(subdir, lines[1]) assert_match(/executing action: rx/, lines[2]) assert_match(/\bin\b/, lines[3]) assert_match(/action\.t\.rant/, lines[4]) assert_equal(subdir, lines[5]) assert_match(/touch\s+a\.t/, lines[6]) assert_file_content "a.t", "" assert test(?f, "action.t.rant") assert !test(?e, "sub.t/action.t.rant") ensure Dir.chdir($testDir) Rant::Sys.rm_rf "sub.t" end end rant-0.5.8/test/test_autosubfiletask.rb0000644000175000017500000000276710527253221017631 0ustar xavierxavier require 'test/unit' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestAutoSubFileTask < Test::Unit::TestCase RG = Rant::Generators def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir @rac = Rant::RantApp.new end def teardown FileUtils.rm_rf Dir["*.t"] end def test_create_dir @rac.gen RG::Directory, "dir.t" blk = lambda { |t| FileUtils.touch t.name } @rac.prepare_task("dir.t/file", blk) { |name,pre,blk| Rant::AutoSubFileTask.new(@rac, name, pre, &blk) } tl = @rac.resolve("dir.t/file") assert_equal(1, tl.size) ft = tl.first assert(ft.prerequisites.empty?) @rac.args.replace %w(dir.t/file) capture_std do assert_equal(0, @rac.run) end assert(test(?d, "dir.t")) assert(test(?f, "dir.t/file")) end def test_fail_no_dir blk = lambda { |t| FileUtils.touch t.name } @rac.prepare_task("dir.t/file", blk) { |name,pre,blk| Rant::AutoSubFileTask.new(@rac, name, pre, &blk) } @rac.args.replace %w(dir.t/file) capture_std do assert_equal(1, @rac.run) end assert(!test(?e, "dir.t")) assert(!test(?e, "dir.t/file")) end def test_dir_exists FileUtils.mkdir "dir.t" blk = lambda { |t| FileUtils.touch t.name } @rac.prepare_task("dir.t/file", blk) { |name,pre,blk| Rant::AutoSubFileTask.new(@rac, name, pre, &blk) } @rac.args.replace %w(dir.t/file) capture_std do assert_equal(0, @rac.run) end assert(test(?d, "dir.t")) assert(test(?f, "dir.t/file")) end end rant-0.5.8/test/test_clean.rb0000644000175000017500000001004610527253221015473 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' require 'fileutils' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestClean < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir end def teardown end def layout_project1 FileUtils.mkdir "p1.t" Dir.chdir "p1.t" do open("Rantfile", "w") { |f| f << <<-EOF import "clean" task :mk_junk => "sub1.t/mk_junk" do sys.touch %w(a1.t a2.t b1.t b2.t) sys.mkdir %w(sa.t sb.t) sys.touch %w(sa.t/1 sb.t/1) end gen Clean var[:clean].include "*a*.t" subdirs "sub1.t" EOF } end FileUtils.mkdir "p1.t/sub1.t" Dir.chdir "p1.t/sub1.t" do open("Rantfile", "w") { |f| f << <<-EOF import "clean" task :mk_junk do sys.touch %w(a1.t a2.t b1.t b2.t) sys.mkdir %w(sa.t sb.t) end gen Clean var[:clean].include "*b*.t" EOF } end end def cleanup_project1 FileUtils.rm_rf "p1.t" end def test_project1 layout_project1 assert(test(?d, "p1.t")) Dir.chdir "p1.t" assert(test(?f, "Rantfile")) assert(test(?d, "sub1.t")) assert(test(?f, "sub1.t/Rantfile")) capture_std do assert_equal(0, Rant::RantApp.new.run) end files = %w(a1.t a2.t b1.t b2.t sub1.t/a1.t sub1.t/a2.t sub1.t/b1.t sub1.t/b2.t) dirs = %w(sa.t sb.t) files.each { |f| assert(test(?f, f)) } dirs.each { |f| assert(test(?d, f)) } capture_std do assert_equal(0, Rant::RantApp.new.run("clean")) end %w(sa.t a1.t a2.t sub1.t/b1.t sub1.t/b2.t).each { |f| assert(!test(?e, f)) } %w(sb.t b1.t b2.t sub1.t/a1.t sub1.t/a2.t).each { |f| assert(test(?e, f)) } Dir.chdir "sub1.t" FileUtils.rm_rf %w(sa.t sb.t) capture_std do assert_equal(0, Rant::RantApp.new.run) end %w(a1.t a2.t b1.t b2.t).each { |f| assert(test(?f, f)) } %w(sa.t sb.t).each { |f| assert(test(?d, f)) } capture_std do assert_equal(0, Rant::RantApp.new.run("clean")) end %w(b1.t b2.t sb.t).each { |f| assert(!test(?e, f)) } %w(a1.t a2.t sa.t).each { |f| assert(test(?e, f)) } ensure Dir.chdir $testDir cleanup_project1 end def layout_project2 FileUtils.mkdir "p2.t" FileUtils.mkdir "p2.t/c.t" FileUtils.touch "p2.t/c.t/data" Dir.chdir "p2.t" open("Rantfile", "w") { |f| f << <<-EOF import "autoclean" task :mk_junk => %w(a.t b.t/c.t/d.t) do sys.touch "mk_junk.t" end gen AutoClean file "a.t" do |t| sys.touch t.name end gen Directory, "b.t/c.t" gen Directory, "c.t", "a" file "c.t/a/b" => "c.t/a" do sys.touch t.name end file "b.t/c.t/d.t" => "b.t/c.t" do |t| sys.touch t.name end var[:autoclean].include "mk_junk.t" var[:autoclean].include "nix.t" EOF } end def cleanup_project2 FileUtils.rm_rf "p2.t" end def test_project2_autoclean layout_project2 capture_std do assert_equal(0, Rant::RantApp.new.run) end %w(a.t b.t/c.t/d.t mk_junk.t).each { |f| assert(test(?e, f)) } capture_std do assert_equal(0, Rant::RantApp.new.run("autoclean")) end %w(a.t b.t/c.t/d.t c.t/a mk_junk.t).each { |f| assert(!test(?e, f)) } assert(test(?d, "c.t")) assert(test(?f, "c.t/data")) capture_std do assert_equal(1, Rant::RantApp.new.run("clean")) end ensure cleanup_project2 end def test_clean_rule_target_rx FileUtils.mkdir "t" Dir.chdir "t" FileUtils.mkdir "sub" FileUtils.mkdir "sub/sub" FileUtils.touch "sub/sub/a.t" FileUtils.touch "sub/a.t" FileUtils.touch "a.t" open "Rantfile", "w" do |f| f << <<-EOF gen Rule, :tt => :t do |t| sys.touch t.name end import "autoclean" gen AutoClean EOF end assert_rant("sub/sub/a.tt") assert(test(?f, "sub/sub/a.tt")) assert_rant("sub/a.tt") assert(test(?f, "sub/a.tt")) assert_rant("a.tt") assert(test(?f, "a.tt")) assert_rant("autoclean") %w(a.tt sub/a.tt sub/sub/a.tt).each { |f| assert(!test(?e, f), "#{f} should get unlinked by AutoClean") } %w(a.t sub/a.t sub/sub/a.t).each { |f| assert(test(?e, f)) } ensure Dir.chdir $testDir FileUtils.rm_rf "t" end end rant-0.5.8/test/test_dirtask.rb0000644000175000017500000000627310527253221016061 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' require 'fileutils' # Ensure we run in testproject directory. $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestDirTask < Test::Unit::TestCase Directory = Rant::Generators::Directory def setup Dir.chdir($testDir) unless Dir.pwd == $testDir @rac = Rant::RantApp.new @cx = @rac.context end def teardown FileUtils.rm_rf Dir["*.t"] end def args(*args) @rac.args.replace(args.flatten) end def test_return dt = @cx.gen Directory, "a.t/b.t" assert(Rant::Node === dt) assert_equal("a.t/b.t", dt.name, "`gen Directory' should return task for last directory") args "--quiet", "a.t/b.t" @rac.run assert(test(?d, "a.t")) assert(test(?d, "a.t/b.t")) end def test_4_levels @cx.gen Directory, "1.t/2/3/4" args "-q", "1.t/2/3/4" assert_equal(0, @rac.run) assert(test(?d, "1.t/2/3/4")) end def test_basedir_no_create out, err = capture_std do assert_equal(1, Rant::RantApp.new.run("basedir.t")) end assert_match(/\[ERROR\].*basedir\.t/, err) assert(!test(?e, "basedir.t")) end def test_basedir_fail_no_basedir out, err = capture_std do assert_equal(1, Rant::RantApp.new.run("basedir.t/a")) end assert(!test(?e, "basedir.t")) assert(!test(?e, "a")) end def test_basedir_a FileUtils.mkdir "basedir.t" assert_rant("basedir.t/a") assert(test(?d, "basedir.t/a")) assert_rant("clean") assert(!test(?e, "basedir.t")) end def test_basedir_a_b FileUtils.mkdir "basedir.t" assert_rant("basedir.t/a/b") assert(test(?d, "basedir.t/a/b")) assert_rant("clean") end def test_basdir_no_b FileUtils.mkdir "basedir.t" assert_rant(:fail, "basedir.t/b") assert_rant("clean") end def test_description FileUtils.mkdir "basedir.t" out, err = assert_rant("--tasks") assert_match(%r{basedir.t/a/b\s*#.*Make some path}, out) assert_rant("clean") end def test_basedir_with_slash open "dir_with_slash.t", "w" do |f| f << <<-EOF import "autoclean" file "a.t/b/c" => "a.t/b" do |t| sys.touch t.name end gen Directory, "a.t/", "b" gen AutoClean EOF end assert_rant(:fail, "-fdir_with_slash.t") assert(!test(?e, "a.t")) FileUtils.mkdir "a.t" assert_rant("-fdir_with_slash.t") assert(test(?f, "a.t/b/c")) out, err = assert_rant("-fdir_with_slash.t") assert(out.empty?) assert(err.empty?) assert_rant("-fdir_with_slash.t", "autoclean") assert(test(?d, "a.t")) assert(!test(?e, "a.t/b")) end def test_with_slash open "dir_with_slash.t", "w" do |f| f << <<-EOF import "autoclean" file "a.t/b" => "a.t" do |t| sys.touch t.name end gen Directory, "a.t/" gen AutoClean EOF end assert_rant("-fdir_with_slash.t") assert(test(?f, "a.t/b")) out, err = assert_rant("-fdir_with_slash.t") assert(out.empty?) assert(err.empty?) assert_rant("-fdir_with_slash.t", "autoclean") assert(!test(?e, "a.t")) end end rant-0.5.8/test/test_dyn_dependencies.rb0000644000175000017500000000241010527253221017705 0ustar xavierxavier require 'test/unit' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestDynamicDependencies < Test::Unit::TestCase #include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($testDir) end def teardown assert_rant "-fdyn_dependencies.rf", "autoclean" assert Dir["*.t"].empty? end def test_task out, err = assert_rant "-fdyn_dependencies.rf" assert err.empty? lines = out.split(/\n/) assert_equal %w(B C D A), lines end def test_file out, err = assert_rant "-fdyn_dependencies.rf", "a.t" assert err.empty? assert !out.empty? assert test(?f, "a.t") assert test(?f, "b.t") assert test(?f, "c.t") assert test(?f, "d.t") out, err = assert_rant "-fdyn_dependencies.rf", "a.t" assert err.empty? assert out.empty? end def test_file_md5 out, err = assert_rant "-imd5", "-fdyn_dependencies.rf", "a.t" assert err.empty? assert !out.empty? assert test(?f, "a.t") assert test(?f, "b.t") assert test(?f, "c.t") assert test(?f, "d.t") assert_rant "-imd5", "-fdyn_dependencies.rf", "autoclean" end end rant-0.5.8/test/test_env.rb0000644000175000017500000000246110527253221015203 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestRantEnv < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir end def teardown end def test_on_windows # rather primitive test, but should catch obvious programming # errors when making changes when in hurry ;) if Rant::Env.on_windows? assert(File::ALT_SEPARATOR, "Env says we're on windows, but there is no ALT_SEPARATOR") end end def test_find_bin # not required since Rant 0.4.7 #assert(Rant::Env.find_bin(Rant::Env::RUBY), # "RUBY_INSTALL_NAME should be found by Env.find_bin, " + # "doesn't need to be a bug of Rant") # let's check for the `echo' command which should be on most # systems: have_echo = false begin have_echo = `echo hello` =~ /hello/ rescue Exception end if have_echo =begin # seems to be not so on windows... echo_bin = Rant::Env.find_bin("echo") assert(echo_bin, "echo can be invoked, so find_bin should find it") assert(echo_bin =~ /echo/i) assert(`#{echo_bin} hello` =~ /hello/, "echo should be invokable through `#{echo_bin}'") =end else puts "*** echo not available, will not search with find_bin ***" end end end rant-0.5.8/test/test_examples.rb0000644000175000017500000002613110527253221016231 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' $examplesDir ||= File.join( File.dirname(File.dirname(File.expand_path(__FILE__))), "doc", "examples") $cc_is_gcc ||= Rant::Env.find_bin("cc") && Rant::Env.find_bin("gcc") class TestExamples < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir $examplesDir end def test_myprog Dir.chdir "myprog" assert_match(/Build myprog.*\n.*Remove compiler products/, run_rant("--tasks")) assert(!test(?f, "myprog")) if $cc_is_gcc # Warning: we're assuming cc is actually gcc run_rant assert(test(?f, "myprog")) else $stderr.puts "*** cc isn't gcc, less example testing ***" # less myprog testing end run_rant("clean") assert(!test(?e, "myprog")) assert(!test(?e, "src/myprog")) assert(!test(?e, "src/lib.o")) assert(!test(?e, "src/main.o")) end def test_myprog_md5 Dir.chdir "myprog" assert_match(/Build myprog.*\n.*Remove compiler products/, run_rant("-imd5", "--tasks")) assert(!test(?f, "myprog")) if $cc_is_gcc # Warning: we're assuming cc is actually gcc run_rant("-imd5") assert(test(?f, "myprog")) else $stderr.puts "*** cc isn't gcc, less example testing ***" # less myprog testing end run_rant("-imd5", "clean") assert(!test(?e, "myprog")) assert(!test(?e, "src/myprog")) assert(!test(?e, "src/lib.o")) assert(!test(?e, "src/main.o")) ensure FileUtils.rm_f Dir["**/.rant.meta"] end def test_directedrule Dir.chdir "directedrule" assert_match(/Build foo/, run_rant("-T")) assert(!test(?f, "foo")) if $cc_is_gcc run_rant assert(test(?f, "foo")) end run_rant("clean") Dir["**/*.o"].each { |f| assert(!test(?e, f)) } end def test_directedrule_md5 Dir.chdir "directedrule" assert_match(/Build foo/, run_rant("-imd5", "-T")) assert(!test(?f, "foo")) if $cc_is_gcc run_rant("-imd5") assert(test(?f, "foo")) end run_rant("-imd5", "clean") assert(!test(?e, ".rant.meta")) Dir["**/*.o"].each { |f| assert(!test(?e, f)) } end def test_c_dependencies Dir.chdir "c_dependencies" assert_match( /\bhello\b.*\n.*\bclean\b.*\n.*\bdistclean\b/, run_rant("-T")) assert(!test(?f, "hello")) if $cc_is_gcc run_rant assert(test(?f, "hello")) else run_rant("c_dependencies") end run_rant("clean") assert(Dir["**/*.o"].empty?) assert(!test(?f, "hello")) assert(test(?f, "c_dependencies")) if $cc_is_gcc run_rant assert(test(?f, "hello")) end run_rant("distclean") assert(Dir["**/*.o"].empty?) assert(!test(?f, "c_dependencies")) assert(!test(?f, "hello")) end def test_c_dependencies_md5 Dir.chdir "c_dependencies" assert_match( /\bhello\b.*\n.*\bclean\b.*\n.*\bdistclean\b/, run_rant("-imd5", "-T")) assert(!test(?f, "hello")) if $cc_is_gcc run_rant("-imd5") assert(test(?f, "hello")) else run_rant("-imd5", "c_dependencies") end run_rant("-imd5", "clean") assert(Dir["**/*.o"].empty?) assert(!test(?f, "hello")) assert(test(?f, "c_dependencies")) if $cc_is_gcc run_rant("-imd5") assert(test(?f, "hello")) end run_rant("-imd5", "distclean") assert(!test(?e, ".rant.meta")) assert(Dir["**/*.o"].empty?) assert(!test(?f, "c_dependencies")) assert(!test(?f, "hello")) end def test_c_cpp_examples Dir.chdir "c_cpp_examples" proj_pwd = Dir.pwd out, err = assert_rant("--tasks") # TODO: replace with a not-so-strict regex op = < 10 Rant::Sys.unpack_tgz "pkg/c_cpp.tgz" assert test(?d, "c_cpp") sources.each { |fn| pkg_fn = File.join("c_cpp", fn) if test ?d, fn assert test(?d, pkg_fn) else assert Rant::Sys.compare_file(fn, pkg_fn) end } Rant::Sys.rm_rf "c_cpp" out, err = assert_rant("pkg/c_cpp.tgz") assert(out.empty?) assert(err.empty?) assert_rant("autoclean") gen_files.each { |f| assert(!test(?e, f), "#{f} should have been removed by autoclean") } if Rant::Env.find_bin("gcc") FileUtils.cp "c/template.rf", "c/problem_1_1/sub.rant" Dir.chdir "c/problem_1_1" out = run_rant assert(out.include?("Hello, world!")) # TODO #assert_rant("autoclean") FileUtils.rm_f "sub.rant" Dir.chdir proj_pwd # TODO: remove next line if TODO above is fixed assert_rant("autoclean") gen_files.each { |f| assert(!test(?e, f), "#{f} should have been removed by autoclean") } end ensure Dir.chdir proj_pwd FileUtils.rm_f "c/problem_1_1/sub.rant" FileUtils.rm_f "c++/problem_1_1/sub.rant" end end rant-0.5.8/test/test_filelist.rb0000644000175000017500000004217310527253221016232 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import/filelist/std' require 'rant/import/sys/more' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestFileList < Test::Unit::TestCase include Rant::TestUtil def assert_entries(entry_ary, fl) assert_equal entry_ary.size, fl.size entry_ary.each { |entry| assert fl.include?(entry) } end def fl(*args, &block) Rant::FileList.glob(*args, &block) end def touch_temp(*args) files = args.flatten files.each { |f| FileUtils.touch f } yield if block_given? ensure files.each { |f| File.delete f if File.exist? f } end def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir @rant = Rant::RantApp.new @cx = @rant.cx @sys = @cx.sys end def teardown end def test_create_new @sys.ignore %r{_} fl = @sys.filelist assert fl.kind_of?(Rant::FileList) assert fl.empty? fl.concat ["ab", "c_d"] assert_equal ["ab"], fl.entries end def test_create_bracket_op in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl = @sys["*.t"] assert_entries(["a.t"], fl) end end def test_create_glob in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl = @sys.glob("*.t") assert_entries(["a.t"], fl) fl = @sys.glob(".*.t") # note: no "." and ".." entries assert_entries([".a.t"], fl) fl = @sys.glob("*.t") do |fl| fl.glob ".*.t" end assert_equal ["a.t", ".a.t"], fl.entries end end def test_create_glob_all in_local_temp_dir do Rant::Sys.touch ["a.t", ".a.t"] fl = @sys.glob_all("*.t") assert_entries(["a.t", ".a.t"], fl) fl = @sys.glob_all(".*.t") # note: no "." and ".." entries assert_entries([".a.t"], fl) fl = @sys.glob_all("*.t") do |fl| fl.keep(".") end assert_entries ["a.t", ".a.t", "."], fl end end def test_conversion_from_filelist fl1 = @sys.filelist fl2 = @sys.filelist(fl1) assert fl1.equal?(fl2) # same object end def test_conversion_from_ary @sys.ignore "foo" fl = @sys.filelist(["foo", "bar"]) assert fl.kind_of?(Rant::FileList) assert_equal ["bar"], fl.entries end def test_conversion_type_error assert_raise_kind_of(TypeError) do fl = @sys.filelist(Object.new) end end def test_conversion_type_error_nil assert_raise_kind_of(TypeError) do fl = @sys.filelist(nil) end end def test_in_flatten touch_temp %w(1.t 2.t) do assert(test(?f, "1.t")) # test touch_temp... assert(test(?f, "2.t")) assert_equal(2, fl("*.t").size) # see comments in FileList implementation to understand # the necessity of this test... assert_equal(2, [fl("*.t")].flatten.size) end end def test_exclude_name l = fl inc_list = %w( CVS_ a/b a ).map! { |f| f.tr "/", File::SEPARATOR } not_inc_list = %w( CVS a/CVS a/CVS/b CVS/CVS //CVS /CVS/ /a/b/CVS/c ).map! { |f| f.tr "/", File::SEPARATOR } l.concat(not_inc_list + inc_list) l.exclude_name "CVS" inc_list.each { |f| assert(l.include?(f)) } not_inc_list.each { |f| assert(!l.include?(f)) } end def test_ignore l = fl r = l.ignore "CVS" assert_same(l, r) inc_list = %w( CVS_ a/b a ).map! { |f| f.tr "/", File::SEPARATOR } not_inc_list = %w( CVS a/CVS a/CVS/b CVS/CVS //CVS /CVS/ /a/b/CVS/c ).map! { |f| f.tr "/", File::SEPARATOR } l.concat(not_inc_list + inc_list) inc_list.each { |f| assert(l.include?(f)) } not_inc_list.each { |f| assert(!l.include?(f)) } end def test_ignore_more FileUtils.mkdir "fl.t" l = fl "fl.t/*", "fl.t", "*.t" touch_temp %w(a.t fl.t/CVS fl.t/a~) do l.ignore(/\~$/, "CVS") assert(l.include?("fl.t")) assert(l.include?("a.t")) assert(!l.include?("fl.t/a~")) assert(!l.include?("fl.t/CVS")) end ensure FileUtils.rm_rf "fl.t" end def test_initialize touch_temp %w(1.t 2.tt) do assert(fl("*.t").include?("1.t"), 'FileList["*.t"] should include 1.t') l = fl("*.t", "*.tt") assert(l.include?("1.t")) assert(l.include?("2.tt")) end end def test_glob_with_yield touch_temp %w(a.t b.t a.tt b.tt) do list = Rant::FileList.glob { |l| l.include "*.t", "*.tt" l.exclude "a*" } %w(b.t b.tt).each { |f| assert(list.include?(f), "`#{f}' should be included") } %w(a.t a.tt).each { |f| assert(!list.include?(f), "`#{f}' shouln't be included") } end end def test_mix_include_exclude touch_temp %w(a.t b.t c.t d.t) do list = fl "a*" list.exclude("a*", "b*").include(*%w(b* c* d*)) assert(list.exclude_name("d.t").equal?(list)) assert(list.include?("b.t")) assert(list.include?("c.t")) assert(!list.include?("a.t")) assert(!list.include?("d.t")) end end def test_exclude_regexp touch_temp %w(aa.t a.t a+.t) do list = fl "*.t" list.exclude(/a+\.t/) assert(list.include?("a+.t")) assert(!list.include?("a.t")) assert(!list.include?("aa.t")) assert_equal(1, list.size) list = fl "*.t" list.exclude("a+.t") assert(!list.include?("a+.t")) assert(list.include?("a.t")) assert(list.include?("aa.t")) assert_equal(2, list.size) end end def test_addition touch_temp %w(t.t1 t.t2 t.t3) do l = (fl("*.t1") << "a") + fl("*.t2", "*.t3") assert_equal(4, l.size) %w(t.t1 t.t2 t.t3 a).each { |f| assert(l.include?(f), "`#{f}' should be included") } end end def test_2addition touch_temp %w(1.ta 1.tb 2.t) do l1 = fl "*.ta", "*.t" l2 = fl "1*" l2.exclude "*.ta" l = l1 + l2 assert(l.include?("1.ta")) assert_equal(3, l.size) assert(!l1.include?("1.tb")) assert_equal(2, l1.size) assert(!l2.include?("2.t")) assert_equal(1, l2.size) end end def test_3addition cx = Rant::RantApp.new.cx in_local_temp_dir do touch_temp %w(a.t1 a.t2 b.t2) do l1 = cx.sys["a*"] l1.exclude "*.t2" l2 = cx.sys["*.t2"] l3 = l1 + l2 %w(a.t1 a.t2 b.t2).each { |fn| assert(l3.include(fn), "#{fn} missing") } l3.uniq! assert_equal(3, l3.size) end end end def test_add_array touch_temp %w(1.t 2.t) do l1 = fl "*.t" l2 = l1 + %w(x) assert_equal(2, l1.size) assert_equal(3, l2.size) assert(l2.include?("x")) assert_equal("x", l2.to_a.last) end end def test_2add_array touch_temp %w(a.t) do l1 = fl("*.t") l1.resolve a = %w(x) l2 = l1 + a a << "y" assert_equal(%w(a.t x), l2.to_ary) end end def test_glob touch_temp %w(t.t1 t.t2) do l = fl "*.t1" l.glob "*.t2" assert_equal(2, l.size) assert(l.include?("t.t1")) assert(l.include?("t.t2")) end end def test_shun touch_temp %w(t.t1 t.t2) do l = fl "t.*" l.shun "t.t1" assert_equal(1, l.size) assert(l.include?("t.t2")) end end def test_rac_sys_glob rac = Rant::RantApp.new cx = rac.context FileUtils.mkdir "fl.t" l = cx.sys.glob "fl.t/*", "fl.t", "*.t" touch_temp %w(a.t fl.t/CVS fl.t/a~) do assert(l.include?("fl.t")) assert(l.include?("a.t")) assert(l.include?("fl.t/a~")) assert(l.include?("fl.t/CVS")) end ensure FileUtils.rm_rf "fl.t" end def test_rac_sys_glob_ignore rac = Rant::RantApp.new cx = rac.context cx.var["ignore"] = ["CVS", /\~$/] FileUtils.mkdir "fl.t" l = cx.sys.glob "fl.t/*", "fl.t", "*.t" touch_temp %w(a.t fl.t/CVS fl.t/a~) do assert(l.include?("fl.t")) assert(l.include?("a.t")) assert(!l.include?("fl.t/a~")) assert(!l.include?("fl.t/CVS")) end ensure FileUtils.rm_rf "fl.t" end def test_sys_glob_late_ignore rac = Rant::RantApp.new cx = rac.context FileUtils.mkdir "fl.t" l = cx.sys["fl.t/*", "fl.t", "*.t"] touch_temp %w(a.t fl.t/CVS fl.t/a~) do cx.var["ignore"] = ["CVS", /\~$/] assert(l.include?("fl.t")) assert(l.include?("a.t")) assert(!l.include?("fl.t/a~")) assert(!l.include?("fl.t/CVS")) end # change in Rant 0.5.1 # l[0] = "CVS" l.unshift "CVS" assert(!l.include?("CVS")) ensure FileUtils.rm_rf "fl.t" end =begin Makes no sense since Rant 0.5.1 def test_return_from_array_method touch_temp "a.t" do l = fl("a.t", "a.t") ul = l.uniq assert(Array === ul) assert_equal(1, ul.size) end end =end def test_return_self_from_array_method touch_temp "a.t", "b.t" do l = fl("*.t") sl = l.sort! assert_same(l, sl) assert_equal("a.t", l.to_a.first) assert_equal("b.t", l.to_a[1]) end end def test_sys_with_cd FileUtils.mkdir "sub.t" open("sys_cd.rf.t", "w") { |f| f << <<-EOF file "sub.t/a.t" => "sub.t/b.t" do |t| sys.touch t.name end file "sub.t/b.t" do |t| sys.touch t.name end task :clean do sys.cd "sub.t" sys.rm_f sys["*.t"] end EOF } capture_std do Rant::RantApp.new.run("-fsys_cd.rf.t", "sub.t/a.t") end assert(test(?f, "sub.t/a.t")) assert(test(?f, "sub.t/b.t")) capture_std do Rant::RantApp.new.run("-fsys_cd.rf.t", "clean") end assert(!test(?e, "sub.t/a.t")) assert(!test(?e, "sub.t/b.t")) ensure FileUtils.rm_rf %w(sub.t sys_cd.rf.t) end def test_sys_select cx = Rant::RantApp.new.cx touch_temp %w(a.t b.t) do l1 = cx.sys["*.t"] l2 = l1.select { |f| f =~ /^b/ } assert_equal(2, l1.size) assert(l1.include("a.t")) assert(l1.include("b.t")) assert_equal(1, l2.size) assert(l1.include("b.t")) end end def test_sys_find_all_resolve cx = Rant::RantApp.new.cx touch_temp %w(a.t b.t) do l1 = cx.sys["*.t"] l1.resolve l2 = l1.find_all { |f| f =~ /^b/ } assert_equal(2, l1.size) assert(l1.include("a.t")) assert(l1.include("b.t")) assert_equal(1, l2.size) assert(l1.include("b.t")) end end def test_sys_glob_flags cx = Rant::RantApp.new.cx touch_temp %w(a.t .a.t b.t .b.t) do l1 = cx.sys.glob # change in Rant 0.5.1 #l1.glob_flags |= File::FNM_DOTMATCH l1.glob_dotfiles l1.include("*.t") l2 = cx.sys["*.t"] assert_equal(4, l1.size) assert_equal(2, l2.size) %w(a.t .a.t b.t .b.t).each { |f| assert(l1.include?(f)) } %w(a.t b.t ).each { |f| assert(l2.include?(f)) } end end def test_add_no_dir cx = Rant::RantApp.new.cx FileUtils.mkdir "tfl.t" FileUtils.mkdir "tfl.tt" touch_temp %w(a.t a.tt) do l1 = cx.sys["*.t"] l1 += cx.sys["*.tt"].no_dir assert_equal(3, l1.size) %w(tfl.t a.t a.tt).each { |f| assert(l1.include?(f)) } end ensure FileUtils.rm_rf %w(tfl.t tfl.tt) end def test_exclude_arrows_op cx = Rant::RantApp.new.cx touch_temp %w(a.t b.t) do fl = cx.sys["*.t"] fl.exclude "*.t" fl << "a.t" assert(fl.include?("a.t")) end end def test_ignore_and_shift_op cx = Rant::RantApp.new.cx cx.var[:ignore] << /CVS/ touch_temp %w(CVS.t a.t a.tt) do fl = cx.sys["*.t"] << "CVS" fl.include "*.tt" assert_equal(%w(a.t CVS a.tt), fl.to_a) end end def test_map cx = Rant::RantApp.new.cx touch_temp %w(a.t b.t) do fl = cx.sys["*.t"] l2 = fl.map { |f| f + "t" } assert(Rant::FileList === l2) assert_equal(2, l2.size) assert(l2.include?("a.tt")) assert(l2.include?("b.tt")) assert_equal(2, fl.size) assert(fl.include?("a.t")) assert(fl.include?("b.t")) end end def test_no_file require 'rant/import/filelist/more' cx = Rant::RantApp.new.cx touch_temp %w(a.t b.t) do fl = cx.sys["*.t"] fl.no_file "a.t" assert_equal(1, fl.size) assert(fl.include?("b.t")) end end def test_to_s cx = Rant::RantApp.new.cx assert_equal("a b", cx.sys[].concat(%w(a b)).to_s) assert_equal("", "#{cx.sys[]}") end def test_var_ignore_and_dup cx = Rant::RantApp.new.cx cx.var[:ignore] << "a.t" touch_temp %w(a.t b.t a.tt) do fl = cx.sys["*.t"] + cx.sys["*.tt"] assert_equal(2, fl.size) assert(fl.include?("b.t")) assert(fl.include?("a.tt")) end end if Rant::Env.on_windows? def test_to_s_quoting_spaces_win cx = Rant::RantApp.new.cx assert_equal('"a a" b', "#{cx.sys[].concat(["a a", "b"])}") end else def test_to_s_quoting_spaces cx = Rant::RantApp.new.cx # changed in 0.4.7 #assert_equal("'a a' b", "#{cx.sys[].concat(["a a", "b"])}") assert_equal("a\\ a b", "#{cx.sys[].concat(["a a", "b"])}") end end def test_dotfiles in_local_temp_dir do Rant::Sys.touch %w(a.t .a.t) Rant::Sys.mkdir %w(d.t .d.t) Rant::Sys.touch %w(d.t/a.t d.t/.a.t) Rant::Sys.touch %w(.d.t/a.t .d.t/.a.t) Rant::Sys.mkdir %w(d.t/d.t) Rant::Sys.touch %w(d.t/d.t/a.t d.t/d.t/.a.t) cx = Rant::RantApp.new.cx files = cx.sys["**/*.t"] %w(a.t d.t d.t/a.t d.t/d.t d.t/d.t/a.t).each { |fn| assert files.include?(fn), "#{fn} missing" } #p files.sort.to_a assert_equal 5, files.size files = cx.sys.glob_all "**/*.t" %w(a.t .a.t d.t .d.t d.t/a.t d.t/.a.t .d.t/a.t .d.t/.a.t d.t/d.t d.t/d.t/a.t d.t/d.t/.a.t).each { |fn| assert files.include?(fn), "#{fn} missing" } assert_equal 11, files.size files = cx.sys.glob_all("**/*.t").exclude(".a.*") %w(a.t d.t .d.t d.t/a.t d.t/.a.t .d.t/a.t .d.t/.a.t d.t/d.t d.t/d.t/a.t d.t/d.t/.a.t).each { |fn| assert files.include?(fn), "#{fn} missing" } assert_equal 10, files.size files = cx.sys.glob_all("**/*.t").shun(".d.t") %w(a.t .a.t d.t d.t/a.t d.t/.a.t d.t/d.t d.t/d.t/a.t d.t/d.t/.a.t).each { |fn| assert files.include?(fn), "#{fn} missing" } assert_equal 8, files.size files.include("**/*.t").uniq! %w(a.t .a.t d.t d.t/a.t d.t/.a.t d.t/d.t d.t/d.t/a.t d.t/d.t/.a.t).each { |fn| assert files.include?(fn), "#{fn} missing" } assert_equal 11, files.size # redundant, just to emphasize assert !files.include?(".") assert !files.include?("..") assert !files.include?("d.t/.") assert !files.include?("d.t/..") assert !files.include?(".d.t/.") assert !files.include?(".d.t/..") end end def test_dotdirs in_local_temp_dir do cx = Rant::RantApp.new.cx Rant::Sys.mkdir ["d", ".d", "d/d", "d/.d", ".d/.d", ".d/d", "d/.d/d"] files = cx.sys["**/*"] %w(d d/d).each { |fn| files.include?(fn) } assert_equal 2, files.size end end def test_rantfile_std in_local_temp_dir do Rant::Sys.mkdir ["d1", "d2"] Rant::Sys.touch ["a.1", "d1/a.2", "d2/b.2"] Rant::Sys.write_to_file "Rantfile", <<-EOF import "filelist/std" task :default do fl = sys["**/*"] puts fl.dirs.join puts fl.files.join fl.no_dir puts fl.join end EOF # Run in subprocess to check if #dirs, #files and #no_dir # is defined by import "filelist/std". out, err = assert_rant(:x) lines = out.split(/\n/) assert_entries ["d1", "d2"], lines[0].split assert_entries ["a.1", "d1/a.2", "d2/b.2", "Rantfile"], lines[1].split assert_entries ["a.1", "d1/a.2", "d2/b.2", "Rantfile"], lines[2].split end end def test_rantfile_object_inspect in_local_temp_dir do Rant::Sys.write_to_file "Rantfile", <<-EOF task :default do |t| fl = sys["**/*"] fl.object_inspect.kind_of?(String) or t.fail puts fl.object_inspect end EOF # Run in subprocess to check if #dirs, #files and #no_dir # is defined by import "filelist/std". out, err = assert_rant(:x) assert !out.strip.empty? end end end rant-0.5.8/test/test_filetask.rb0000644000175000017500000000531510527253221016216 0ustar xavierxavier require 'test/unit' require 'tutil' $test_filetask_file = File.expand_path(__FILE__) $test_dir ||= File.dirname($test_filetask_file) class TestFileTask < Test::Unit::TestCase include Rant::TestUtil def setup Dir.chdir $test_dir @rant = Rant::RantApp.new end def test_needed_non_existent run = false t = Rant::FileTask.new(@rant, "non_existent") { run = true } assert(t.needed?, "`non_existent' doesn't exist, so filetask is needed") assert(!run, "only FileTask#needed? was called, which shouldn't run task block") end def test_needed_no_dep run = false t = @rant.file $test_filetask_file do run = true end assert(!t.needed?, "file exists and has no prerequisite, so needed? should return false") assert(!run) end =begin commented out due to a semantics change in 0.4.5 def test_single_dep tr = false t = @rant.task :t do tr = true end run = false f = @rant.file "testfile" => :t do run = true end f.invoke assert(tr) assert(run) end =end def test_prerequisites @rant.file "a" do true end @rant.file "b" do true end f = @rant.file "c" => %w(a b) do |t| assert_equal(t.prerequisites, %w(a b), "prerequisites should always be an array of _strings_") true end f.invoke end def test_no_invoke_task_dep write_to_file "print_name.t", "b\n" out, err = assert_rant "depends_name.t" assert err.empty? assert test(?f, "depends_name.t") assert_equal "b\na\n", File.read("depends_name.t") assert_match(/writing.*depends_name\.t/, out) assert !out.include?("print_name.t"), "file task mustn't invoke task as prerequisite" out, err = assert_rant "depends_name.t" assert err.empty? assert out.empty? assert test(?f, "depends_name.t") assert_equal "b\na\n", File.read("depends_name.t") ensure Rant::Sys.rm_f %w(print_name.t depends_name.t) end def test_no_invoke_task_dep_md5 write_to_file "print_name.t", "b\n" out, err = assert_rant "-imd5", "depends_name.t" assert err.empty? assert test(?f, "depends_name.t") assert_equal "b\na\n", File.read("depends_name.t") assert_match(/writing.*depends_name\.t/, out) assert !out.include?("print_name.t"), "file task mustn't invoke task as prerequisite" out, err = assert_rant "-imd5", "depends_name.t" assert err.empty? assert out.empty? assert test(?f, "depends_name.t") assert_equal "b\na\n", File.read("depends_name.t") ensure Rant::Sys.rm_f %w(auto.rf .rant.meta print_name.t depends_name.t) Rant::Sys.rm_f Dir["*.t"] end end rant-0.5.8/test/test_rac.rb0000644000175000017500000000277510527253221015170 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestRac < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir end def teardown end def test_parse_caller_elem_nil assert_nothing_raised { ch = Rant::Lib.parse_caller_elem(nil) assert_equal("", ch[:file]) assert_equal(0, ch[:ln]) } end def test_parse_caller_elem_file_ln assert_nothing_raised { ch = Rant::Lib.parse_caller_elem("C:\\foo\\bar:32") assert_equal("C:\\foo\\bar", ch[:file]) assert_equal(32, ch[:ln]) } end def test_parse_caller_elem_file_ln_meth assert_nothing_raised { ch = Rant::Lib.parse_caller_elem("C:\\foo abc\\bar de:32:in nix") assert_equal("C:\\foo abc\\bar de", ch[:file]) assert_equal(32, ch[:ln]) } end def test_parse_caller_elem_eval assert_nothing_raised { ch = Rant::Lib.parse_caller_elem("-e:1") assert_equal("-e", ch[:file]) assert_equal(1, ch[:ln]) } end def test_parse_caller_elem_file_with_colon_ln_meth assert_nothing_raised { ch = Rant::Lib.parse_caller_elem("abc:de:32:in nix") assert_equal("abc:de", ch[:file]) assert_equal(32, ch[:ln]) } end def test_parse_caller_elem_no_line_number assert_nothing_raised { out, err = capture_std do ch = Rant::Lib.parse_caller_elem("foo") assert_equal("foo", ch[:file]) assert_equal(0, ch[:ln]) end } end end rant-0.5.8/test/test_rant_interface.rb0000644000175000017500000000566110527253221017404 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestRantInterface < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir end def teardown end def test_cmd_targets @app = Rant::RantApp.new op = capture_stderr { assert_equal(@app.run("-f non_existent", "target", "-aforced_target"), 1, "Rant should fail because there is no such Rantfile.") } assert(op =~ /\[ERROR\]/, "rant should print error message if -f RANTFILE not found") assert_equal(@app.cmd_targets.size, 2, "there were to targets given on commandline") assert(@app.cmd_targets.include?("target")) assert(@app.cmd_targets.include?("forced_target")) assert(@app.cmd_targets.first == "forced_target", "forced_target should run first") end def test_envvar_on_cmdline @app = Rant::RantApp.new @app.context.var.env "VAR" assert_equal(@app.run("VAR=VAL"), 0) assert_equal(ENV["VAR"], "VAL", "rant should set arguments of form VAR=VAL in var") end def test_envvar_on_cmdline_lc @app = Rant::RantApp.new assert_equal(@app.run("var2=val2"), 0) assert_equal(@app.context.var["var2"], "val2", "rant should set arguments of form var2=val2 in var") end def test_opt_targets @app = Rant::RantApp.new @app.desc 'This is a "public" target.' @app.task :public_task @app.task :private_task op = capture_stdout { assert_equal(@app.run("--tasks"), 0) } assert(op =~ /\bpublic_task\b/, "rant -T output should contain name of described task") assert(op !~ /private_task/, "rant -T output shouldn't contain name of not-described task") end def test_opt_help op = capture_stdout { assert_equal(Rant.run("--help"), 0, "rant --help should return 0") } assert(!op.empty?, "rant --help should write to STDOUT") assert(op.split("\n").size > 15, "rant --help should print at least 16 lines to STDOUT") end def test_opt_version out, err = assert_rant("--version") assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/^rant \d\.\d\.\d$/i, lines.first) out2, err2 = assert_rant("-V") assert_equal err, err2 assert_equal out, out2 end def test_no_such_option out, err = assert_rant :fail, "-N" assert out.empty? lines = err.split(/\n/) assert lines.size < 3 assert_match(/\[ERROR\].*option.*\bN\b/, lines.first) end def test_no_such_long_option out, err = assert_rant :fail, "--nix" assert out.empty? lines = err.split(/\n/) assert lines.size < 3 assert_match(/\[ERROR\].*option.*\bnix\b/, lines.first) end def test_opt_rantfile_no_such_file out, err = assert_rant :fail, "-fdoesnt_exist.rf" assert out.empty? assert err =~ /\bdoesnt_exist\.rf\b/ end end rant-0.5.8/test/test_rantfile_api.rb0000644000175000017500000001273710527253221017057 0ustar xavierxavier require 'test/unit' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestRantfileAPI < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir $testDir @app = Rant::RantApp.new end def teardown Dir.chdir $testDir assert_rant("clean") assert(!test(?e, "auto.rf")) FileUtils.rm_rf Dir["*.t"] end def test_action @app.args << "act_verbose=1" out, err = capture_std do assert_equal(0, @app.run) end assert_match(/running action/, out) end def test_action_query @app.args << "act_verbose=1" << "--tasks" out, err = capture_std do assert_equal(0, @app.run) end assert(out !~ /running action/) end def test_rac_build capture_std do assert_equal(0, @app.run) end assert(test(?f, "version.t")) old_mtime = File.mtime "version.t" timeout capture_std do assert_equal(0, Rant::RantApp.new.run) end assert_equal(old_mtime, File.mtime("version.t")) end def test_rac_build_cd assert_rant("tmp.t/Rantfile", "subdir_tmp", "build_test_t") end def test_string_sub_ext assert_equal("hello.txt", "hello.sxw".sub_ext(".sxw", ".txt")) end def test_string_sub_ext_2 assert_equal("hello.txt", "hello.sxw".sub_ext("sxw", "txt")) end def test_string_sub_ext_one_arg assert_equal("hello.txt", "hello.sxw".sub_ext("txt")) end def test_string_sub_ext_new_ext assert_equal("hello.txt", "hello".sub_ext("txt")) end def test_string_sub_ext_dot assert_equal("hello.txt", "hello.".sub_ext("txt")) end def test_string_sub_ext_empty_str assert_equal("hello.", "hello.txt".sub_ext("")) end def test_string_sub_ext_nil assert_equal("hello.", "hello.txt".sub_ext(nil)) end def test_name_error_in_task open "rf.t", "w" do |f| f << <<-EOF task :a do n_i_x end EOF end out, err = assert_rant(:fail, "-frf.t") assert(out.strip.empty?) assert_match(/rf\.t/, err) assert_match(/2/, err) assert_match(/n_i_x/, err) end def test_make_file out, err = assert_rant("make_file") assert(err.empty?) assert(test(?f, "make_file.t")) out, err = assert_rant("make_file") assert(out.empty?) assert(err.empty?) end def test_make_files out, err = assert_rant("make_files=ON") assert(err.empty?) assert(test(?f, "make_files.t")) assert(test(?f, "make_files_dep.t")) out, err = assert_rant("make_files=ON") assert(out.empty?) end def test_dep_on_make_files_fail assert_rant(:fail, "dep_on_make_files") assert(!test(?e, "make_files.t")) assert(!test(?e, "make_files_dep.t")) end def test_dep_on_make_files assert_rant("dep_on_make_files", "make_files=1") assert(test(?e, "make_files.t")) assert(test(?e, "make_files_dep.t")) end def test_make_path out, err = assert_rant("make_path=1") assert(err.empty?) assert(test(?d, "basedir.t/a/b")) out, err = assert_rant("make_path=1") assert(out.empty?) assert(err.empty?) end def test_make_subfile out, err = assert_rant("make_gen_with_block=1") assert(err.empty?) assert(test(?f, "a.t/a.t")) out, err = assert_rant("make_gen_with_block=1") assert(out.empty?) assert(err.empty?) end def test_source_self open "source_self.t", "w" do |f| f << <<-EOF puts "test" task :a source "source_self.t" EOF end out, err = nil, nil th = Thread.new { out, err = assert_rant("-fsource_self.t") } # OK, give it one second to complete assert_equal(th, th.join(1)) assert_equal("test\n", out) assert(err.empty?) end def test_task_no_arguments in_local_temp_dir do write_to_file "Rantfile", <<-EOF task task :default do puts "hello" end EOF out, err = assert_rant :fail assert out.empty? lines = err.split(/\n/) assert lines.size < 4 assert_match(/\[ERROR\].*Rantfile\b.*\b1\b/, lines[0]) assert_match(/argument/, lines[1]) end end def test_file_too_many_hash_elements in_local_temp_dir do write_to_file "root.rant", <<-EOF file :a => :b, :c => :d do |t| sys.touch t.name end EOF out, err = assert_rant :fail assert out.empty? assert !test(?e, "a") assert !test(?e, "c") lines = err.split(/\n/) assert lines.size < 4 assert_match(/\[ERROR\].*root\.rant\b.*\b2\b/, lines[0]) assert_match(/\btoo many\b.*\bone\b/i, lines[1]) end end def test_task_string_or_symbol_required in_local_temp_dir do write_to_file "root.rant", <<-EOF task Object.new EOF out, err = assert_rant :fail assert out.empty? lines = err.split(/\n/) assert lines.size < 4 assert_match(/\[ERROR\].*root\.rant\b.*\b1\b/, lines[0]) assert_match(/string or symbol/i, lines[1]) end end def test_make_plain_task out, err = assert_rant "call-make" assert err.empty? assert_equal "print_name.t\ncall-make\n", out end end rant-0.5.8/test/test_rule.rb0000644000175000017500000001411410527253221015360 0ustar xavierxavier require 'tutil' require 'test/unit' require 'rant/import/sys/more' # Ensure we run in testproject directory. $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestRule < Test::Unit::TestCase include Rant::TestUtil def setup Dir.chdir($testDir) unless Dir.pwd == $testDir end def teardown FileUtils.rm_rf Dir["*.t*"] FileUtils.rm_rf Dir["*.lt"] FileUtils.rm_rf Dir["*.rt"] FileUtils.rm_rf Dir["*.rtt"] end def test_target_and_source_as_symbol FileUtils.touch "r.t" FileUtils.touch "r2.t" capture_std do assert_equal(0, Rant.run("-frule.rf", "r.tt", "r2.tt")) end assert(test(?f, "r.t")) assert(test(?f, "r2.t")) end def test_rule_depends_on_rule capture_std do assert_equal(0, Rant.run("-frule.rf", "r.tt", "r2.tt")) end assert(test(?f, "r.t")) assert(test(?f, "r2.t")) end def test_src_block FileUtils.touch "r.rtt" capture_std do assert_equal(0, Rant.run("-frule.rf", "r.rt")) end assert(test(?f, "r.rtt")) assert(test(?f, "r.rt")) end def test_src_block_multiple_deps capture_std do assert_equal(0, Rant.run("-frule.rf", "r.lt")) end assert(test(?f, "r.t")) assert(test(?f, "r.tt")) assert(test(?f, "r.lt")) end def test_enhance_rule_task out, err = assert_rant("-frule.rf", "enhance_t=1", "eh.t") assert(test(?f, "eh.t")) assert_match(/eh\.t created/, out) assert(err !~ /\[WARNING\]|\[ERROR\]/) end def test_rule_no_block_error in_local_temp_dir do write_to_file "Rantfile", <<-EOF task :a do puts "a" end gen Rule, :o => :c EOF out, err = assert_rant :fail assert out.empty? lines = err.split(/\n/) assert lines.size < 4 assert_match(/\[ERROR\].*Rantfile\b.*\b4\b/, lines.first) assert_match(/Rule\b.*block required\b/, lines[1]) end end def test_empty_extension_target in_local_temp_dir do Rant::Sys.write_to_file "root.rant", <<-EOF gen Rule, "" => ".t" do |t| sys.cp t.source, t.name end EOF Rant::Sys.write_to_file "a.t", "abc\n" out, err = assert_rant :tmax_1, "a" assert err.empty? assert !out.empty? assert_file_content "a", "abc\n" out, err = assert_rant :tmax_1, "a" assert err.empty? assert out.empty? end end =begin # TODO: needs change in prerequisite handling of file tasks def test_file_hook_only_file_source in_local_temp_dir do Rant::Sys.write_to_file "Rantfile", <<-EOF gen Rule, :o => :c do |t| sys.cp t.source, t.name end task "a.c" do |t| puts t.name end file "b.c" do |t| sys.write_to_file t.name, "ok\n" end task "d.c" do |t| puts "task for d.c" end file "d.c" do |t| sys.write_to_file t.name, "ok 2\n" end EOF out, err = assert_rant :fail, "a.o" assert out.empty? assert_match(/ERROR\b.*\ba\.o/, err) assert !test(?e, "a.o") out, err = assert_rant "b.o" assert err.empty? assert !out.empty? assert_file_content "b.o", "ok\n" out, err = assert_rant "d.o" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/writing to file/, lines[0]) assert lines[0] =~ /task for/ out, err = assert_rant "b.o" assert err.empty? assert out.empty? out, err = assert_rant "d.o" assert err.empty? assert out.empty? end end def test_file_hook_only_file_source_md5 in_local_temp_dir do Rant::Sys.write_to_file "Rantfile", <<-EOF gen Rule, :o => :c do |t| sys.cp t.source, t.name end task "a.c" do |t| puts t.name end file "b.c" do |t| sys.write_to_file t.name, "ok\n" end task "d.c" do |t| puts "task for d.c" end file "d.c" do |t| sys.write_to_file t.name, "ok 2\n" end EOF out, err = assert_rant :fail, "-imd5", "a.o" assert out.empty? assert_match(/ERROR\b.*\ba\.o/, err) assert !test(?e, "a.o") out, err = assert_rant "-imd5", "b.o" assert err.empty? assert !out.empty? assert_file_content "b.o", "ok\n" out, err = assert_rant "-imd5", "d.o" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/writing to file/, lines[0]) assert lines[0] =~ /task for/ out, err = assert_rant "-imd5", "b.o" assert err.empty? assert out.empty? out, err = assert_rant "-imd5", "d.o" assert err.empty? assert out.empty? end end =end if Rant::Env.find_bin("cc") && Rant::Env.find_bin("gcc") # Note: we are assuming that "cc" invokes "gcc"! def test_cc FileUtils.touch "a.t.c" capture_std do assert_equal(0, Rant.run("a.t.o", "-frule.rf")) end assert(test(?f, "a.t.o")) end else $stderr.puts "*** cc/gcc not available, less rule tests ***" end def test_abs_path_source FileUtils.touch "abs_rule_test.et" abs_target_path = File.expand_path "abs_rule_test.ett" out, err = assert_rant("-frule.rf", abs_target_path) assert test(?f, abs_target_path) out, err = assert_rant("-frule.rf", abs_target_path) assert out.empty? ensure Rant::Sys.rm_f ["abs_rule_test.et", "abs_rule_test.ett"] end end rant-0.5.8/test/test_source.rb0000644000175000017500000000224210527253221015710 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'tutil' require 'fileutils' # Ensure we run in testproject directory. $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestSource < Test::Unit::TestCase def setup Dir.chdir $testDir end def teardown capture_std do assert_equal(0, Rant.run("clean")) end end def test_task_for_source capture_std do assert_equal(0, Rant.run("auto.t")) end assert(test(?f, "auto.rf")) assert(test(?f, "auto.t")) end def test_source_now open "rf.t", "w" do |f| f << <<-EOF file "source.rf.t" do |t| sys.touch t.name end task :source_now do source :n, "source.rf.t" end task :source_now2 do sys.touch "source.rf.t" source :n, "source.rf.t" end task :mk_source do source "source.rf.t" end EOF end assert_rant("-frf.t", "mk_source") assert(test(?f, "source.rf.t")) FileUtils.rm "source.rf.t" out, err = assert_rant(:fail, "-frf.t", "source_now") assert(!test(?f, "source.rf.t")) assert_match(/\[ERROR\].*source.*No such file.*source\.rf\.t/im, err) assert_rant("-frf.t", "source_now2") assert(test(?f, "source.rf.t")) end end rant-0.5.8/test/test_sourcenode.rb0000644000175000017500000001053710527253221016564 0ustar xavierxavier require 'test/unit' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestSourceNode < Test::Unit::TestCase def setup # Ensure we run in test directory. Dir.chdir $testDir end def teardown Dir.chdir $testDir FileUtils.rm_rf Dir["*.t"] end def tmp_rf(content = @rf, fn = "rf.t") open(fn, "w") { |f| f.write content } yield ensure FileUtils.rm_f fn end def test_invoke @rf = <<-EOF gen SourceNode, "a.t" EOF tmp_rf do out, err = assert_rant("-frf.t") assert(!test(?f, "a.t")) assert(out.strip.empty?) assert(err.strip.empty?) end tmp_rf do out, err = assert_rant("-frf.t", "a.t") assert(out.strip.empty?) assert(err.strip.empty?) end end def test_deps_empty_array @rf = <<-EOF gen SourceNode, "a.t" => [] EOF tmp_rf do assert_rant("-frf.t") end end def test_with_deps @rf = <<-EOF gen SourceNode, "a.t" => %w(b.t c.t) EOF tmp_rf do assert_rant("-frf.t") end tmp_rf do assert_rant(:fail, "-frf.t", "b.t") end end def test_as_dependency @rf = <<-EOF file "a.t" => "b.t" do |t| sys.touch t.name end gen SourceNode, "b.t" => "c.t" EOF tmp_rf do out, err = assert_rant(:fail, "-frf.t") assert(out.strip.empty?) assert_match(/\[ERROR\].*no such file.*b\.t/m, err) end assert(!test(?e, "a.t")) FileUtils.touch "b.t" tmp_rf do out, err = assert_rant(:fail, "-frf.t") assert(out.strip.empty?) assert_match(/\[ERROR\].*no such file.*c\.t/m, err) end assert(!test(?e, "a.t")) FileUtils.touch "c.t" tmp_rf do out, err = assert_rant("-frf.t") end assert(test(?f, "a.t")) end def test_timestamps @rf = <<-EOF file "a.t" => "b.t" do |t| sys.touch t.name end gen SourceNode, "b.t" => %w(c.t d.t) EOF FileUtils.touch %w(b.t c.t d.t) tmp_rf do assert_rant("-frf.t") assert(test(?f, "a.t")) out, err = assert_rant("-frf.t") assert(out.strip.empty?, "no source changed, no update required") old_mtime = File.mtime "a.t" timeout FileUtils.touch "b.t" assert_rant("-frf.t") assert(File.mtime("a.t") > old_mtime) old_mtime = File.mtime "a.t" timeout FileUtils.touch "c.t" assert_rant("-frf.t") assert(File.mtime("a.t") > old_mtime) end end def test_with_block @rf = <<-EOF gen SourceNode, "a.t" do end EOF tmp_rf do out, err = assert_rant(:fail, "-frf.t") assert_match(/\[ERROR\].*SourceNode.*block/m, err) end end def test_with_autoclean @rf = <<-EOF import "autoclean" gen SourceNode, "a.t" => %w(b.t c.t) gen AutoClean EOF tmp_rf do assert_rant("-frf.t", "autoclean") FileUtils.touch %w(a.t b.t c.t) assert_rant("-frf.t", "autoclean") assert(test(?f, "a.t")) assert(test(?f, "b.t")) assert(test(?f, "c.t")) end end def test_sourcenode_depends_on_sourcenode @rf = <<-EOF file "a.t" => "b.t" do |t| sys.touch t.name end gen SourceNode, "b.t" => %w(c.t d.t) gen SourceNode, "d.t" => "e.t" EOF FileUtils.touch %w(b.t c.t d.t e.t) tmp_rf do assert_rant("-frf.t") assert(test(?f, "a.t")) timeout FileUtils.touch "e.t" old_mtime = File.mtime "a.t" assert_rant("-frf.t") assert(File.mtime("a.t") > old_mtime) end end def test_circular_dep @rf = <<-EOF gen SourceNode, "a.t" => "b.t" gen SourceNode, "b.t" => "a.t" EOF FileUtils.touch %w(a.t b.t) tmp_rf do th = Thread.new{ assert_rant("-frf.t") } assert_equal(th, th.join(0.5)) end end def test_file_pre @rf = <<-EOF import "autoclean" file "f.t" => "a.t" do |t| sys.touch t.name end gen SourceNode, "a.t" => ["b.t", "c.t"] file "b.t" do |t| sys.touch t.name end gen AutoClean EOF FileUtils.touch "c.t" FileUtils.touch "a.t" tmp_rf do out, err = assert_rant("-frf.t") assert(err.empty?) assert(!out.empty?) assert(test(?f, "f.t")) assert(test(?f, "b.t")) out, err = assert_rant("-frf.t") assert(err.empty?) assert(out.empty?) assert_rant("-frf.t", "autoclean") assert(!test(?f, "f.t")) assert(!test(?f, "b.t")) assert(test(?f, "a.t")) assert(test(?f, "c.t")) end end end rant-0.5.8/test/test_sys.rb0000644000175000017500000002205310527253221015230 0ustar xavierxavier require 'test/unit' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestSys < Test::Unit::TestCase include Rant::Sys include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($testDir) end def test_ruby cx = Rant::RantApp.new.cx block_executed = false op = capture_stdout do cx.sys.ruby('-e ""') { |stat| block_executed = true assert_equal(0, stat) } end assert(block_executed) assert(op =~ /\-e/i, "sys should print command with arguments to $stdout") end def test_ruby_no_block assert(!test(?e, "a.t")) out, err = capture_std do assert_nothing_raised { ruby '-e', 'open "a.t", "w" do end' } end assert(test(?f, "a.t")) ensure FileUtils.rm_f "a.t" end def test_ruby_exit_code cx = Rant::RantApp.new.cx block_executed = false out, err = capture_std do cx.sys.ruby('-e', 'exit 2') { |stat| block_executed = true assert_equal(2, stat.exitstatus) } end assert(block_executed) assert(err.empty?) assert_match(/. -e exit 2\n\z/m, out) end def test_ruby_fail out, err = capture_std do assert_raises(Rant::CommandError) { ruby '-e exit 1' } end end def test_split_all pl = split_all("/home/stefan") assert_equal(pl.size, 3, "/home/stefan should get split into 3 parts") assert_equal(pl[0], "/") assert_equal(pl[1], "home") assert_equal(pl[2], "stefan") pl = split_all("../") assert_equal(pl.size, 1, '../ should be "split" into one element') assert_equal(pl[0], "..") assert_equal ["."], Rant::Sys.split_all("./") assert_equal ["."], Rant::Sys.split_all(".") assert_equal [".", "foo"], Rant::Sys.split_all("./foo") assert_equal [], Rant::Sys.split_all("") end def test_expand_path in_local_temp_dir do rootdir = Dir.pwd write_to_file "root.rant", <<-EOF task :a do puts sys.expand_path("@") end subdirs "sub" EOF in_local_temp_dir "sub" do write_to_file "sub.rant", <<-'EOF' task :a do puts sys.expand_path("@") end task :b do puts sys.expand_path("@/abc") end task :c do puts sys.expand_path("@abc") end task :d do puts sys.expand_path("../abc") end task :e do puts sys.expand_path(nil) end task :f do puts sys.expand_path("@/../abc") end task :g do puts sys.expand_path("a@b") end task :h do puts sys.expand_path("@a@b") end task :i do puts sys.expand_path('\@a@b') end task :j do puts sys.expand_path(nil) end EOF Dir.chdir ".." out, err = assert_rant "a" assert_equal rootdir, out.chomp out, err = assert_rant "sub/a" assert_equal rootdir, out.split(/\n/).last Dir.chdir "sub" out, err = assert_rant "a" assert_equal rootdir, out.split(/\n/).last out, err = assert_rant "b" assert_equal "#{rootdir}/abc", out.split(/\n/).last out, err = assert_rant "c" assert_equal "#{rootdir}/abc", out.split(/\n/).last out, err = assert_rant "d" assert_equal "#{rootdir}/abc", out.split(/\n/).last out, err = assert_rant "e" assert_equal "#{rootdir}/sub", out.split(/\n/).last out, err = assert_rant "f" assert_equal "#{File.dirname(rootdir)}/abc", out.split(/\n/).last out, err = assert_rant "g" assert_equal "#{rootdir}/sub/a@b", out.split(/\n/).last out, err = assert_rant "h" assert_equal "#{rootdir}/a@b", out.split(/\n/).last out, err = assert_rant "i" assert_equal "#{rootdir}/sub/@a@b", out.split(/\n/).last out, err = assert_rant "e" assert_equal "#{rootdir}/sub", out.split(/\n/).last end end end # perhaps this test should go into a seperate file def test_toplevel out = run_rant("-ftoplevel.rf") #assert_match(/\btd\b/, out, # "Sys module should print commands to stdout") assert_equal(0, $?, "rant -ftoplevel.rf in test/ should be successfull") ensure File.delete "td" if File.exist? "td" end # ...ditto def test_name_error File.open("name_error.rf", "w") { |f| f << "no_var_no_method\n" } out, err = capture_std do assert_equal(1, Rant.run("-fname_error.rf")) end lines = err.split(/\n/) assert_equal(3, lines.size) assert_match(/\bname_error\.rf\b.*\b1\b/, lines[0]) assert_match(/Name\s*Error/i, lines[1]) ensure File.delete "name_error.rf" if File.exist? "name_error.rf" end # ... def test_standalone out = `#{Rant::Sys.sp(Rant::Env::RUBY_EXE)} -I#{Rant::Sys.sp(RANT_DEV_LIB_DIR)} standalone.rf` assert_exit assert_match(/^t_standalone/, out) end def test_cp_with_filelist rac = Rant::RantApp.new rac[:quiet] = true open "a.t", "w" do |f| f.puts "a" end open "b.t", "w" do |f| f.puts "b" end FileUtils.mkdir "cp.t" assert_nothing_raised { rac.cx.sys.cp rac.cx.sys["a.t","b.t"], "cp.t" assert_equal("a\n", File.read("cp.t/a.t")) assert_equal("b\n", File.read("cp.t/b.t")) } ensure FileUtils.rm_rf %w(cp.t a.t b.t) end def test_sys_with_block open "exit_1.t", "w" do |f| f << <<-EOF exit 1 EOF end open "rf.t", "w" do |f| f << <<-EOF task :rbexit1_block do sys Env::RUBY_EXE, "exit_1.t" do |status| puts "no success" if status != 0 puts status.exitstatus end end task :rbexit1 do sys.ruby "exit_1.t" end EOF end out, err = assert_rant("-frf.t") assert(err.empty?) assert_equal(["no success", "1"], out.split(/\n/)[1..-1]) out, err = assert_rant(:fail, "-frf.t", "rbexit1") ensure FileUtils.rm_f %w(exit_1.t rf.t) end def test_escape assert_equal "abc", Rant::Sys.escape("abc") assert_equal "", Rant::Sys.escape("") # might change assert_equal "", Rant::Sys.escape(nil) if Rant::Env.on_windows? assert_equal '"a b"', Rant::Sys.escape("a b") assert_equal '" "', Rant::Sys.escape(" ") assert_equal '" a b "', Rant::Sys.escape(" a b ") o = Object.new def o.to_s; "to s"; end assert_equal '"to s"', Rant::Sys.escape(o) else assert_equal "a\\ b", Rant::Sys.escape("a b") assert_equal "\\ ", Rant::Sys.escape(" ") assert_equal "\\ a\\ b\\ ", Rant::Sys.escape(" a b ") o = Object.new def o.to_s; "to s"; end assert_equal "to\\ s", Rant::Sys.escape(o) end end def test_escape_array assert_equal "a b", Rant::Sys.escape(%w(a b)) assert_equal "", Rant::Sys.escape([]) assert_equal "", Rant::Sys.escape([[]]) res = Rant::Sys.escape(["", []]) assert_equal "", res.strip assert res.length < 2 if Rant::Env.on_windows? assert_equal '"a b" c d " e"', Rant::Sys.escape([["a b"], "c", ["d", " e"]]) else assert_equal "a\\ \\ b c d \\ e", Rant::Sys.escape([["a b"], "c", ["d", " e"]]) end end def test_sp assert_equal "a", Rant::Sys.sp("a") assert_equal "", Rant::Sys.sp("") if Rant::Env.on_windows? assert_equal '"a b"', Rant::Sys.sp("a b") assert_equal '" " a b "c " d', Rant::Sys.sp([" ", ["a", "b", "c ", ["d"]]]) assert_equal "a\\b", Rant::Sys.sp("a/b") assert_equal '"a\ b" c\d', Rant::Sys.sp(["a/ b", "c/d"]) assert_equal "a\\b\\c\\", Rant::Sys.sp("a/b/c/") assert_equal "\"a b\\c\\\\\"", Rant::Sys.sp("a b/c/") else assert_equal "a\\ b", Rant::Sys.sp("a b") assert_equal "\\ a b c\\ d", Rant::Sys.sp([" ", ["a", "b", "c ", ["d"]]]) assert_equal "a/b", Rant::Sys.sp("a/b") assert_equal "a/\\ b c/d", Rant::Sys.sp(["a/ b", "c/d"]) end end end rant-0.5.8/test/test_sys_methods.rb0000644000175000017500000004671410527253221016765 0ustar xavierxavier require 'test/unit' require 'tutil' require 'rant/import/sys/more' require 'rant/import/filelist/std' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestSysMethods < Test::Unit::TestCase include Rant::TestUtil def setup # Ensure we run in test directory. Dir.chdir($testDir) @rant = Rant::RantApp.new @cx = @rant.cx @sys = @cx.sys end def teardown Dir.chdir($testDir) Rant::Sys.rm_rf "t" Rant::Sys.rm_rf Rant::FileList["*.t"] end def test_pwd assert_equal Dir.pwd, @sys.pwd end def test_cd__mkdir_single_str__pwd__rmdir_single_empty_dir out, err = capture_std do assert_nothing_raised do @sys.mkdir "t" assert(test(?d, "t")) @sys.cd "t" assert_equal(File.join($testDir, "t"), @sys.pwd) @sys.cd ".." assert_equal($testDir, @sys.pwd) @sys.rmdir "t" assert(!test(?e, "t")) end end assert err.empty? lines = out.split(/\n/) assert_equal 4, lines.size assert_match(/mkdir\s+t/, lines[0]) assert_match(/cd\s+t/, lines[1]) assert_match(/cd\s+/, lines[2]) assert_match(/rmdir\s+t/, lines[3]) end def test_cd_absolute_path_with_block out, err = capture_std do assert_raise(RuntimeError) do @sys.mkdir "t" @sys.cd(File.join($testDir, "t")) do assert_equal(File.join($testDir, "t"), @sys.pwd) raise end end assert_equal $testDir, @sys.pwd end assert err.empty? lines = out.split(/\n/) assert_match(/mkdir\s+t/, lines[0]) assert_match(/cd\s.*t/, lines[1]) end def test_mkdir_array__rmdir_array out, err = capture_std do assert_nothing_raised do @sys.mkdir ["foo.t", File.join($testDir, "bar.t")] assert test(?d, "foo.t") assert test(?d, "bar.t") assert_raise_kind_of(SystemCallError) do @sys.mkdir "foo.t" end assert test(?d, "foo.t") @sys.rmdir [File.join($testDir, "foo.t")] assert !test(?e, "foo.t") @sys.rmdir @sys["*.t"] assert !test(?e, "bar.t") end end assert err.empty? lines = out.split(/\n/) assert_equal 4, lines.size assert_match(/mkdir.*foo\.t.*bar\.t/, lines[0]) assert_match(/mkdir.*foo\.t/, lines[1]) assert_match(/rmdir.*foo\.t/, lines[2]) assert_match(/rmdir.*bar\.t/, lines[3]) end def test_plain_cp open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end out, err = capture_std do assert_nothing_raised do @sys.cp "a.t", "b.t" end end assert test(?f, "b.t") ca = File.open("a.t", "rb") { |f| f.read } cb = File.open("b.t", "rb") { |f| f.read } assert_equal ca, "a\nb\rc\n\rd\r\n" assert_equal ca, cb assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/cp\s+a\.t\s+b\.t/, lines[0]) end def test_cp_filelist_to_dir open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end out, err = capture_std do assert_nothing_raised do @sys.cp "a.t", "b.t" @sys.mkdir "t" @sys.cp @sys["*.t"], "t" end end assert test(?f, "b.t") assert test(?f, "t/a.t") assert test(?f, "t/b.t") ca = File.open("t/a.t", "rb") { |f| f.read } cb = File.open("t/b.t", "rb") { |f| f.read } assert_equal ca, "a\nb\rc\n\rd\r\n" assert_equal ca, cb assert err.empty? lines = out.split(/\n/) assert_equal 3, lines.size assert_match(/cp\s+a\.t\s+b\.t/, lines[0]) assert_match(/mkdir\s+t/, lines[1]) assert_match(/cp\s+a\.t\s+b\.t\s+t/, lines[2]) end def test_cp_dir_fail out, err = capture_std do @sys.mkdir "t" assert test(?d, "t") assert_raise_kind_of(SystemCallError) do @sys.cp "t", "a.t" end end #assert !test(?e, "a.t") # TODO assert err.empty? lines = out.split(/\n/) assert_equal(2, lines.size) end def test_cp_r_like_cp open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end out, err = capture_std do assert_nothing_raised do @sys.cp_r "a.t", "b.t" end end assert test(?f, "b.t") ca = File.open("a.t", "rb") { |f| f.read } cb = File.open("b.t", "rb") { |f| f.read } assert_equal ca, "a\nb\rc\n\rd\r\n" assert_equal ca, cb assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/cp -r\s+a\.t\s+b\.t/, lines[0]) end def test_cp_r out, err = capture_std do @sys.mkdir "a.t" @sys.mkdir "t" open "a.t/a", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end @sys.touch "b.t" assert_nothing_raised do @sys.cp_r @sys["*.t"], "t" end end assert test(?d, "t/a.t") ca = File.open("t/a.t/a", "rb") { |f| f.read } assert_equal ca, "a\nb\rc\n\rd\r\n" assert test(?f, "t/b.t") assert test(?d, "a.t") assert test(?f, "a.t/a") assert test(?f, "b.t") lines = out.split(/\n/) assert_equal 4, lines.size assert_match(/cp -r\s.*t/, lines[3]) end def test_plain_mv open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end out, err = capture_std do assert_nothing_raised do @sys.mv "a.t", "b.t" end end assert test(?f, "b.t") assert !test(?e, "a.t") cb = File.open("b.t", "rb") { |f| f.read } assert_equal cb, "a\nb\rc\n\rd\r\n" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/mv\s+a\.t\s+b\.t/, lines[0]) end def test_mv_dirs_and_files out, err = capture_std do @sys.mkdir "a.t" @sys.mkdir "t" @sys.touch "a.t/a" @sys.touch "b.t" assert_nothing_raised do @sys.mv @sys["*.t"], "t" end end assert test(?d, "t/a.t") assert test(?f, "t/a.t/a") assert test(?f, "t/b.t") assert !test(?e, "a.t") assert !test(?e, "b.t") lines = out.split(/\n/) assert_equal 5, lines.size end def test_plain_rm out, err = capture_std do @sys.touch "a.t" assert test(?f, "a.t") @sys.rm "a.t" assert !test(?e, "a.t") assert_raise_kind_of(SystemCallError) do @sys.rm "a.t" end end assert err.empty? lines = out.split(/\n/) assert_equal 3, lines.size assert_match(/rm\s+a\.t/, lines[1]) end def test_rm_dir_fail out, err = capture_std do @sys.mkdir "a.t" assert test(?d, "a.t") assert_raise_kind_of(SystemCallError) do @sys.rm "a.t" end assert test(?d, "a.t") end end def test_rm_filelist__touch_array out, err = capture_std do @sys.touch ["a.t", "b.t"] assert test(?f, "a.t") assert test(?f, "b.t") @sys.rm @sys["*.t"] assert !test(?e, "a.t") assert !test(?e, "b.t") end assert err.empty? lines = out.split(/\n/) assert_equal 2, lines.size end def test_rm_f out, err = capture_std do @sys.touch "a.t" assert test(?f, "a.t") @sys.rm_f "a.t" assert !test(?e, "a.t") assert_nothing_raised do @sys.rm_f "a.t" @sys.rm_f ["a.t", "b.t"] end end assert err.empty? lines = out.split(/\n/) assert_equal 4, lines.size assert_match(/rm -f\s+a\.t/, lines[1]) end def test_rm_r_dir__rm_r_fail_not_exist out, err = capture_std do @sys.mkdir "t" @sys.touch "t/a" @sys.mkdir "t/sub" assert_nothing_raised do @sys.rm_r "t" end assert !test(?e, "t") assert_raise_kind_of(SystemCallError) do @sys.rm_r "t" end end assert err.empty? lines = out.split(/\n/) assert_equal 5, lines.size assert_match(/rm -r\s+t/, lines[3]) end def test_rm_rf out, err = capture_std do @sys.mkdir "t" @sys.touch "t/a" @sys.mkdir "t/sub" assert_nothing_raised do @sys.rm_rf "t" end assert !test(?e, "t") assert_nothing_raised do @sys.rm_rf "t" end end assert err.empty? lines = out.split(/\n/) assert_equal 5, lines.size assert_match(/rm -rf\s+t/, lines[3]) end =begin # TODO, but tested indirectly in many other tests anyway def test_touch end =end def test_safe_ln open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end out, err = capture_std do assert_nothing_raised do @sys.safe_ln "a.t", "b.t" end end assert test(?f, "b.t") ca = File.open("a.t", "rb") { |f| f.read } cb = File.open("b.t", "rb") { |f| f.read } assert_equal ca, "a\nb\rc\n\rd\r\n" assert_equal ca, cb assert err.empty? lines = out.split(/\n/) assert lines.size == 1 || lines.size == 2 assert_match(/(ln|cp)\s+a\.t\s+b\.t/, lines[-1]) lines[-1] =~ /(ln|cp)\s+a\.t\s+b\.t/ puts "\n*** hardlinks #{$1 == "ln" ? "" : "not"} supported ***" if $1 == "ln" assert test_hardlink("a.t", "b.t", :allow_write => true) end end def test_compare_file open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end open "b.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end assert @sys.compare_file("a.t", "b.t") end def test_compare_file_binary? # probably not the right test... open "a.t", "wb" do |f| f << "a\nb\rc\n\rd\r\n" end open "b.t", "wb" do |f| f << "a\nb\rc\n\rd\n" end assert !@sys.compare_file("a.t", "b.t") end def test_compare_empty_files Rant::Sys.touch "a.t" Rant::Sys.touch "b.t" assert @sys.compare_file("a.t", "b.t") end def test_ln__ln_f Rant::Sys.write_to_file "a.t", "abc\n" e = nil out, err = capture_std do begin @sys.ln "a.t", "b.t" rescue Exception => e puts "\n*** hard links not supported ***" assert(e.kind_of?(SystemCallError) || e.kind_of?(NotImplementedError), "exception Errno::EOPNOTSUPP " + "expected but #{e.class} risen") end end if e assert !test(?e, "b.t") else #assert test(?-, "b.t", "a.t") assert test_hardlink("a.t", "b.t") assert !test(?l, "b.t") # shouldn't be necessary assert_file_content "b.t", "abc\n" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/ln\s+a\.t\s+b\.t/, lines[0]) # further tests Rant::Sys.mkdir "t" out, err = capture_std do assert_nothing_raised do @sys.ln "a.t", "t" end end #assert test(?-, "t/a.t", "a.t") assert test_hardlink("t/a.t", "a.t") assert_file_content "t/a.t", "abc\n" Rant::Sys.touch "c.t" capture_std do assert_raise_kind_of(SystemCallError) do @sys.ln "a.t", "c.t" end end #assert !test(?-, "c.t", "a.t") assert !test_hardlink("c.t", "a.t") assert_file_content "c.t", "" capture_std do assert_nothing_raised do @sys.ln_f "a.t", "c.t" end end #assert test(?-, "c.t", "a.t") assert test_hardlink("c.t", "a.t") assert_file_content "c.t", "abc\n" end end def test_ln_s__ln_sf Rant::Sys.write_to_file "a.t", "abc\n" e = nil out, err = capture_std do begin @sys.ln_s "a.t", "b.t" rescue Exception => e puts "\n*** symbolic links not supported ***" # TODO: raises NotImplementedError on WinXP/NTFS/ruby-1.8.2 assert(e.kind_of?(SystemCallError) || e.kind_of?(NotImplementedError), "exception Errno::EOPNOTSUPP " + "expected but #{e.class} risen") end end if e assert !test(?e, "b.t") else assert test(?l, "b.t") assert_file_content "b.t", "abc\n" assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/ln -s\s+a\.t\s+b\.t/, lines[0]) # further tests Rant::Sys.mkdir "t" out, err = capture_std do assert_nothing_raised do @sys.ln_s File.expand_path("a.t"), "t" end end assert test(?l, "t/a.t") assert_file_content "t/a.t", "abc\n" Rant::Sys.touch "c.t" capture_std do assert_raise_kind_of(SystemCallError) do @sys.ln_s "a.t", "c.t" end end assert !test(?l, "c.t") assert_file_content "c.t", "" capture_std do assert_nothing_raised do @sys.ln_sf "a.t", "c.t" end end assert test(?l, "c.t") assert_file_content "c.t", "abc\n" end end def test_uptodate? assert !@sys.uptodate?("a.t", []) Rant::Sys.touch "a.t" assert @sys.uptodate?("a.t", []) timeout Rant::Sys.touch "b.t" assert !@sys.uptodate?("a.t", @sys.glob("*.t").exclude("a.t")) Rant::Sys.touch ["a.t", "b.t"] assert !@sys.uptodate?("a.t", ["b.t"]) timeout Rant::Sys.touch "a.t" assert @sys.uptodate?("a.t", ["b.t"]) Rant::Sys.touch "c.t" assert !@sys.uptodate?("a.t", ["c.t", "b.t"]) end def test_install # TODO: more tests, especially option testing Rant::Sys.mkdir "t" Rant::Sys.mkdir ["lib.t", "lib.t/a"] Rant::Sys.touch ["lib.t/a.s", "lib.t/a/b.s"] out, err = capture_std do Rant::Sys.cd "lib.t" do assert_nothing_raised do @sys.install @sys.glob("**/*").no_dir, "#$testDir/t" end end end assert err.empty? assert !out.empty? # TODO: more accurate assert_file_content "t/a.s", "" assert_file_content "t/b.s", "" end def test_mkdir_p out, err = capture_std do assert_nothing_raised do @sys.mkdir_p "t" assert test(?d, "t") @sys.mkdir_p "t/t1/t2/t3" assert test(?d, "t/t1/t2/t3") @sys.mkdir_p ["#$testDir/tt/a", "ttt/a/b/c"] assert test(?d, "tt/a") assert test(?d, "ttt/a/b/c") end end assert err.empty? lines = out.split(/\n/) assert_equal 3, lines.size assert_match(/mkdir -p\s+t/, lines[0]) ensure Rant::Sys.rm_rf ["tt", "ttt"] end def test_chmod # TODO Rant::Sys.touch "a.t" out, err = capture_std do assert_nothing_raised do @sys.chmod 0755, "a.t" end end assert err.empty? lines = out.split(/\n/) assert_equal 1, lines.size assert_match(/chmod 0?755 a\.t/, lines[0]) s = File.stat("a.t") unless (s.mode & 0777) == 0755 puts "\n***chmod 0755 not fully functional (actual: #{s.mode.to_s(8)}) ***" end end def test_write_to_file @cx.import "sys/more" capture_std do # TODO: specialize exception class assert_raise_kind_of(NoMethodError) do @sys.write_to_file "a.t", Object.new end end assert !test(?e, "a.t") out, err = capture_std do @sys.write_to_file "a.t", "hello\n" end assert_file_content "a.t", "hello\n" end def test_write_to_binfile @cx.import "sys/more" capture_std do # TODO: specialize exception class assert_raise_kind_of(NoMethodError) do @sys.write_to_binfile "a.t", Object.new end end assert !test(?e, "a.t") out, err = capture_std do @sys.write_to_binfile "a.t", "hello\nsepp" end assert test(?f, "a.t") File.open("a.t", "rb") do |f| assert_equal "hello\nsepp", f.read end end def test_regular_filename if Rant::Env.on_windows? assert_equal "a/b", Rant::Sys.regular_filename('a\b') assert_equal "a/b", @sys.regular_filename('a\\\\b') else assert_equal "a/b", Rant::Sys.regular_filename("a/b") assert_equal "a/b", @sys.regular_filename("a//b") end end def test_root_dir? assert Rant::Sys.root_dir?("/") assert !Rant::Sys.root_dir?("foo") assert !Rant::Sys.root_dir?("/foo") if Rant::Env.on_windows? assert Rant::Sys.root_dir?("C:/") assert Rant::Sys.root_dir?("C:\\") assert Rant::Sys.root_dir?("XY:\\") assert !Rant::Sys.root_dir?("C:\\foo") assert !Rant::Sys.root_dir?("C:/foo/bar") end end def test_absolute_path? assert Rant::Sys.absolute_path?("/foo") assert Rant::Sys.absolute_path?("/foo/bar/baz.txt") assert Rant::Sys.absolute_path?("/") assert Rant::Sys.absolute_path?("//foo") assert !Rant::Sys.absolute_path?("foo/") assert !Rant::Sys.absolute_path?("") assert !Rant::Sys.absolute_path?("foo\\bar") if Rant::Env.on_windows? assert Rant::Sys.absolute_path?("\\foo") assert Rant::Sys.absolute_path?("\\") assert Rant::Sys.absolute_path?("C:\\foo") assert Rant::Sys.absolute_path?("C:/foo") assert Rant::Sys.absolute_path?("C:\\") assert Rant::Sys.absolute_path?("C:/") else assert !Rant::Sys.absolute_path?("C:/") assert !Rant::Sys.absolute_path?("C:/foo") assert !Rant::Sys.absolute_path?("\\") assert !Rant::Sys.absolute_path?("C:\\") end end end rant-0.5.8/test/test_task.rb0000644000175000017500000000726210527253221015361 0ustar xavierxavier require 'test/unit' require 'tutil' $-w = true class TestTask < Test::Unit::TestCase def setup @rant = Rant::RantApp.new end def test_version assert(Rant::VERSION.length >= 5) end def test_needed run = false t = Rant::Task.new(@rant, :non_existent) { run = true } assert(t.needed?, "Rant::Task should always be 'needed?' before first invocation") assert(!run, "Rant::Task shouldn't get run when 'needed?' is called") end def test_invoke run = false block = lambda { run = true } task = Rant::Task.new(@rant, :test_run, &block) task.invoke assert(run, "block should have been executed") assert(task.done?, "task is done") assert(!task.needed?, "task is done, so 'needed?' should return false") end def test_fail block = lambda { |t| t.fail "this task abortet itself" } task = Rant::Task.new(@rant, :test_fail, &block) assert_raise(Rant::TaskFail, "run should throw Rant::TaskFail if block raises Exception") { task.invoke } assert(task.fail?) assert(task.invoked?, "although task failed, it was invoked") end def test_dependent r1 = r2 = false t1 = Rant::Task.new(@rant, :t1) { r1 = true } t2 = Rant::Task.new(@rant, :t2) { r2 = true } t1 << t2 t1.invoke assert(r1) assert(r2, "t1 depends on t2, so t2 should have been run") assert(t1.done?) assert(t2.done?) assert(!t1.needed?) assert(!t2.needed?) end def test_dependency_fails t1 = Rant::Task.new(@rant, :t1) { true } t2 = Rant::Task.new(@rant, :t2) { |t| t.fail } t1 << t2 assert_raise(Rant::TaskFail, "dependency t2 failed, so t1 should fail too") { t1.invoke } assert(t1.fail?, "fail flag should be set for task if dependency fails") assert(t2.fail?, "fail flag should be set for task if it fails") end def test_task run = false t = @rant.task :t do |t| run = true end t.invoke assert(run) end def test_dep_on_self run = false t = @rant.task :t => "t" do |t| run = true end th = Thread.new { t.invoke } # shouldn't take half a second... assert_equal(th.join(0.5), th, "task should remove dependency on itself") assert(run, "task should get run despite dependency on itself") end def test_circular_dependency t1r = false t2r = false t1 = @rant.task :t1 => :t2 do |t| assert(t2r) t1r = true end t2 = @rant.task :t2 => :t1 do |t| t2r = true end out, err = capture_std do th = Thread.new { t1.invoke } assert_equal(th, th.join(0.5), "task should detect circular dependency") end assert(t1r) assert(t2r) assert_match(/\[WARNING\]/, err, "Rant should print a warning to stderr about circular" + "dependency") end def test_dep_on_self_in_deplist rl = [] t1 = @rant.task :t1 do |t| rl << t.name end t2 = @rant.task :t2 do |t| rl << t.name end t3 = @rant.task :t3 => [:t1, :t3, :t2] do |t| rl << t.name end th = Thread.new { t3.invoke } # shouldn't take half a second... assert_equal(th.join(0.5), th, "task should remove dependency on itself from dependency list") assert_equal(rl, %w(t1 t2 t3), "t3 was run and depends on [t1, t2] => run order: t1 t2 t3") end def test_enhance_gen_task app = Rant::RantApp.new enhance_run = false t_run = false t2_run = false app.gen Rant::Generators::Task, :t do |t| t.needed { true } t.act { assert(t2_run, "enhance added `t2' as prerequisite") t_run = true } end app.gen Rant::Generators::Task, :t2 do |t| t.needed { true } t.act { t2_run = true } end assert_nothing_raised("generated Task should be enhanceable") { app.enhance :t => :t2 do enhance_run = true end } assert_equal(0, app.run) assert(t_run) end end rant-0.5.8/test/test_var.rb0000644000175000017500000002016010527253221015177 0ustar xavierxavier require 'test/unit' require 'rant/rantlib' require 'fileutils' require 'tutil' $testDir ||= File.expand_path(File.dirname(__FILE__)) class TestVar < Test::Unit::TestCase RV = Rant::RantVar RS = Rant::RantVar::Space def setup # Ensure we run in test directory. Dir.chdir($testDir) unless Dir.pwd == $testDir @rac = Rant::RantApp.new @cx = @rac.context end def teardown end def test_space require "rant/import/var/numbers" s = nil assert_nothing_raised { s = RS.new assert_nil(s[:a]) s[:a] = 1 assert_equal(1, s["a"]) assert_equal(1, s[:a]) s[:b] = "b" assert_equal("b", s.query(:b)) assert_same(s[:b], s["b"]) assert_same(s.query("b"), s["b"]) s.query :a, :Integer, 2 assert_equal(2, s[:a]) s.query :c, :Integer assert_equal(0, s[:c]) } end def test_rac_var_ignore assert_equal([], @cx.var(:ignore)) @cx.var[:ignore] = "CVS" assert_equal(%w(CVS), @cx.var["ignore"]) end def test_invalid_for_constraint @cx.import "var/numbers" @cx.var :a, :Integer assert_equal(0, @cx.var[:a]) assert_raises(Rant::RantVar::ConstraintError) { @cx.var[:a] = "x" } assert_equal(0, @cx.var[:a]) end def test_env ENV["RANT_TEST_VAL"] = "val" @cx.var.env "RANT_TEST_VAL" assert_equal("val", @cx.var["RANT_TEST_VAL"]) assert_equal("val", @cx.var("RANT_TEST_VAL")) @cx.var[:RANT_TEST_VAL] = "new" assert_equal("new", @cx.var["RANT_TEST_VAL"]) assert_equal("new", ENV["RANT_TEST_VAL"]) env_val2 = ENV["RANT_TEST_VAL2"] @cx.var["RANT_TEST_VAL2"] = "val2" assert_equal(env_val2, ENV["RANT_TEST_VAL2"]) assert_equal("val2", @cx.var("RANT_TEST_VAL2")) end def test_late_env @cx.var "rtv3" => "val3" @cx.var.env "rtv3", "rtv4" assert_equal("val3", @cx.var["rtv3"]) assert_equal("val3", ENV["rtv3"]) ENV["rtv4"] = "val4" assert_equal("val4", @cx.var["rtv4"]) end def test_env_with_array @cx.var "rtv6" => "val6" @cx.var.env %w(rtv6 rtv7) assert_equal("val6", @cx.var["rtv6"]) assert_equal("val6", ENV["rtv6"]) ENV["rtv7"] = "val7" assert_equal("val7", @cx.var["rtv7"]) end def test_defaults @rac.args.replace %w(-fvar.rf) capture_std do assert_equal(0, @rac.run) end assert(test(?f, "default_1.t")) assert(test(?f, "default_2.t")) capture_std do assert_equal(0, Rant::RantApp.new.run(%w(-fvar.rf clean))) end end def test_override @rac.args.replace %w(v1=val1.t v2=val2.t -fvar.rf) capture_std do assert_equal(0, @rac.run) end assert(test(?f, "val1.t")) assert(test(?f, "default_2.t")) capture_std do assert_equal(0, Rant::RantApp.new.run(%w(-fvar.rf clean))) end end def test_is_string @cx.import "var/strings" @rac.var :s, :String assert_equal("", @rac.var["s"]) @rac.var[:s] = "abc" assert_equal("abc", @rac.var["s"]) obj = Object.new def obj.to_str "obj" end assert_nothing_raised { @rac.var[:s] = obj } assert_equal("obj", @rac.var[:s]) assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:s] = 3 } assert_equal("obj", @rac.var[:s]) end =begin def test_is_integer @cx.import "var/numbers" @rac.var(:count => 10).is :Integer assert_equal(10, @rac.var[:count]) assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:count] = "no_integer" } assert_equal(10, @rac.var[:count]) end def test_is_integer_in_range @rac.var(:count => 10).is 0..20 assert_equal(10, @rac.var[:count]) assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:count] = "no_integer" } assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:count] = 21 } assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:count] = -1 } assert_equal(10, @rac.var[:count]) @rac.var[:count] = "15" assert_equal(15, @rac.var(:count)) end =end def test_restrict @cx.import "var/numbers" assert_equal(nil, @rac.var[:num]) @rac.var.restrict :num, :Float, -1.1..2.0 assert_equal(-1.1, @rac.var[:num]) @rac.var[:num] = "1.5" assert_equal(1.5, @rac.var[:num]) assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:num] = -1.2 } assert_equal(1.5, @rac.var[:num]) end def test_restrict_cmd @rac.args.replace %w(-fvar.rf show_count) out, err = capture_std { @rac.run } assert_match(/count 1/, out) end def test_restrict_cmd_change @rac.args.replace %w(-fvar.rf count=5 show_count) out, err = capture_std { @rac.run } assert_match(/count 5/, out) end def test_restrict_cmd_error @rac.args.replace %w(-fvar.rf count=0 show_count) out, err = capture_std { assert_equal(1, @rac.run) } assert_match(/[ERROR]/, err) end def test_float_range_cmd @rac.args.replace %w(-fvar.rf num=5.0 show_num) out, err = capture_std do assert_equal(0, @rac.run) end assert_match(/num 5.0/, out) end def test_float_range_cmd_invalid @rac.args.replace %w(-fvar.rf num=0.0 show_num) out, err = capture_std do assert_equal(1, @rac.run) end assert_match(/[ERROR]/, err) end def test_float_range_default @rac.args.replace %w(-fvar.rf show_num) out, err = capture_std do assert_equal(0, @rac.run) end assert_match(/num 1.1/, out) end def test_env_to_string @cx.import "var/strings" @rac.var "RT_TO_S", :ToString @rac.var.env "RT_TO_S" if Rant::Env.on_windows? # very odd on windows: when setting ENV["ABC"]="" you'll # get out ENV["ABC"] == nil assert(ENV["RT_TO_S"] == "" || ENV["RT_TO_S"] == nil) assert(@rac.var["RT_TO_S"] == "" || @rac.var["RT_TO_S"] == nil) else assert_equal(ENV["RT_TO_S"], "") assert_equal(@rac.var["RT_TO_S"], "") end assert_nothing_raised { @rac.var[:RT_TO_S] = "abc" assert_equal("abc", ENV["RT_TO_S"]) obj = Object.new def obj.to_s; "obj"; end @rac.var[:RT_TO_S] = obj assert_equal("obj", ENV["RT_TO_S"]) } end def test_bool @cx.import "var/booleans" @rac.var "true?", :Bool assert_equal(false, @rac.var[:true?]) assert_nothing_raised { @rac.var[:true?] = true assert_equal(true, @rac.var[:true?]) @rac.var[:true?] = false assert_equal(false, @rac.var[:true?]) @rac.var[:true?] = 1 assert_equal(true, @rac.var[:true?]) @rac.var[:true?] = 0 assert_equal(false, @rac.var[:true?]) @rac.var[:true?] = :on assert_equal(true, @rac.var[:true?]) @rac.var[:true?] = :off assert_equal(false, @rac.var[:true?]) @rac.var[:true?] = "yes" assert_equal(true, @rac.var[:true?]) @rac.var[:true?] = "no" assert_equal(false, @rac.var[:true?]) @rac.var[:true?] = "true" assert_equal(true, @rac.var[:true?]) @rac.var[:true?] = "false" assert_equal(false, @rac.var[:true?]) @rac.var[:true?] = "y" assert_equal(true, @rac.var[:true?]) @rac.var[:true?] = "n" assert_equal(false, @rac.var[:true?]) @rac.var[:true?] = nil assert_equal(false, @rac.var[:true?]) } assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:true?] = "abc" } assert_equal(false, @rac.var[:true?]) end def test_bool_shortcut_true @cx.import "var/booleans" @rac.var :bs, true assert_equal(true, @rac.var[:bs]) @rac.var[:bs] = false assert_equal(false, @rac.var[:bs]) assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:bs] = "abc" } assert_equal(false, @rac.var[:bs]) end def test_bool_shortcut_false @cx.import "var/booleans" @rac.var :bs, false assert_equal(false, @rac.var[:bs]) @rac.var[:bs] = "1" assert_equal(true, @rac.var[:bs]) assert_raise(::Rant::RantVar::ConstraintError) { @rac.var[:bs] = "abc" } assert_equal(true, @rac.var[:bs]) end def test_violation_message @rac.args.replace %w(-fvar.rf source_err) out, err = capture_std do assert_equal(1, @rac.run) end assert_match( /source_err\.rf\.t.+2.*\n.*11.+constraint.+integer/i, err) ensure assert_equal(0, Rant::RantApp.new.run("-fvar.rf", "clean", "-q")) end def test_rant_import @rac.args.replace %w(-fvar.rf show_num) run_import "-q", "-ivar/numbers", "ant.t" assert_exit out = run_ruby("ant.t", "-fvar.rf", "show_num") assert_exit assert_match(/num 1.1/, out) ensure Rant::Sys.rm_f "ant.t" end end rant-0.5.8/test/toplevel.rf0000644000175000017500000000011410527253221015203 0ustar xavierxavier include Rant::Sys def tf fn touch fn end file :td do tf "td" end rant-0.5.8/test/ts_all.rb0000644000175000017500000000023010527253221014622 0ustar xavierxavier # assumes to be run in the test/ directory of the Rant distribution $:.unshift(File.expand_path("../lib")) Dir["**/test_*.rb"].each { |t| require t } rant-0.5.8/test/tutil.rb0000644000175000017500000002360310527253221014516 0ustar xavierxavier # This file contains methods that aid in testing Rant. $-w = true require 'rant/rantlib' require 'rant/import/sys/tgz' require 'rant/import/sys/zip' require 'fileutils' module Test module Unit class TestCase def ext_rb_test(script, opts = {}) out = nil Rant::TestUtil.in_local_temp_dir do script_fn = opts[:fn] || "fl.rb" dirs = (opts[:dirs] || []).dup touch = (opts[:touch] || []).dup touch.each { |fn| dirs << File.dirname(fn) } dirs.each { |dir| next if [".", "..", "/"].include?(dir) Rant::Sys.mkdir_p dir } touch.each { |fn| Rant::Sys.touch fn } Rant::Sys.write_to_file script_fn, script if opts[:return] == :stdout out = `#{Rant::Sys.sp Rant::Env::RUBY_EXE} -w -I#{Rant::Sys.sp ENV['RANT_DEV_LIB_DIR']} #{Rant::Sys.sp script_fn}` else Rant::Sys.ruby "-w", "-I", ENV["RANT_DEV_LIB_DIR"], script_fn end assert_exit(0, opts[:msg]) end out end def assert_rant(*args) res = 0 capture = true newproc = false tmax_1 = false out, err = nil, nil args.flatten! args.reject! { |arg| if Symbol === arg case arg when :fail: res = 1 when :v: capture = false when :verbose: capture = false when :x: newproc = true when :tmax_1: tmax_1 = true else raise "No such option -- #{arg}" end true else false end } action = lambda { if newproc if capture # TODO: stderr out = `#{Rant::Sys.sp(Rant::Env::RUBY_EXE)} #{Rant::Sys.sp(RANT_BIN)} #{args.flatten.join(' ')}` else system("#{Rant::Sys.sp(Rant::Env::RUBY_EXE)} " + "#{Rant::Sys.sp(RANT_BIN)} " + "#{args.flatten.join(' ')}") end assert_equal(res, $?.exitstatus) elsif capture out, err = capture_std do assert_equal(res, ::Rant::RantApp.new.run(*args)) end else assert_equal(res, ::Rant::RantApp.new.run(*args)) end } if tmax_1 th = Thread.new(&action) unless th.join(1) th.kill assert(false, "execution aborted after 1 second") end else action.call end return out, err end def assert_exit(status = 0, msg = nil) msg ||= "exit status expected to be " + "#{status} but is #{$?.exitstatus}" assert_equal(status, $?.exitstatus, msg) end def assert_file_content(fn, content, *opts) assert(test(?f, fn), "`#{fn}' doesn't exist") fc = File.read(fn) fc.strip! if opts.include? :strip assert(fc == content, "file `#{fn}' should contain `#{content}' " + "but contains `#{fc}'") end if RUBY_VERSION < "1.8.1" def assert_raise(*args, &block) assert_raises(*args, &block) end end def assert_raise_kind_of(klass) e = nil begin yield rescue Exception => e end if e.nil? flunk("Exception `#{klass}' expected but non risen.") else unless e.kind_of? klass flunk("Exception `#{klass}' expected " + "but `#{e.class}' thrown") end end end end # class TestCase end # module Unit end # module Test RANT_BIN = File.expand_path( File.join(File.dirname(__FILE__), "..", "run_rant")) RANT_IMPORT_BIN = File.expand_path( File.join(File.dirname(__FILE__), "..", "run_import")) RANT_DEV_LIB_DIR = File.expand_path( File.join(File.dirname(__FILE__), "..", "lib")) $rant_test_to = Rant::Env.on_windows? ? 3 : 2 if ENV["TO"] begin $rant_test_to = Integer(ENV["TO"]) rescue end end def timeout sleep $rant_test_to end # Everything written to $stdout during +yield+ will be returned. No # output to $stdout. def capture_stdout tfn = "._ranttestcstdout.tmp" if File.exist? tfn raise <<-EOD When testing Rant: `#{Dir.pwd + "/" + tfn}' exists. The testing process temporarily needs this file. Ensure that the file doesn't contain data useful for you and try to remove it. (Perhaps this file was left by an earlier testrun.) EOD end begin stdout = $stdout File.open(tfn, "w") { |tf| $stdout = tf yield } o = File.read tfn ensure $stdout = stdout File.delete tfn if File.exist? tfn end end def capture_stderr tfn = "._ranttestcstderr.tmp" if File.exist? tfn raise <<-EOD When testing Rant: `#{Dir.pwd + "/" + tfn}' exists. The testing process temporarily needs this file. Ensure that the file doesn't contain data useful for you and try to remove it. (Perhaps this file was left by an earlier testrun.) EOD end begin stderr = $stderr File.open(tfn, "w") { |tf| $stderr = tf yield } o = File.read tfn ensure $stderr = stderr File.delete tfn if File.exist? tfn end end def capture_std outfn = "._ranttestcstdout.tmp" errfn = "._ranttestcstderr.tmp" if File.exist? outfn raise <<-EOD When testing Rant: `#{Dir.pwd + "/" + outfn}' exists. The testing process temporarily needs this file. Ensure that the file doesn't contain data useful for you and try to remove it. (Perhaps this file was left by an earlier testrun.) EOD end if File.exist? errfn raise <<-EOD When testing Rant: `#{Dir.pwd + "/" + errfn}' exists. The testing process temporarily needs this file. Ensure that the file doesn't contain data useful for you and try to remove it. (Perhaps this file was left by an earlier testrun.) EOD end begin stdout = $stdout stderr = $stderr File.open(outfn, "w") { |of| $stdout = of File.open(errfn, "w") { |ef| $stderr = ef yield } } [File.read(outfn), File.read(errfn)] ensure $stderr = stderr $stdout = stdout File.delete outfn if File.exist? outfn File.delete errfn if File.exist? errfn end end def run_rant(*args) `#{Rant::Sys.sp(Rant::Env::RUBY_EXE)} #{Rant::Sys.sp(RANT_BIN)} #{args.flatten.join(' ')}` end def run_import(*args) `#{Rant::Sys.sp(Rant::Env::RUBY_EXE)} #{Rant::Sys.sp(RANT_IMPORT_BIN)} #{args.flatten.join(' ')}` end def run_ruby(*args) `#{Rant::Sys.sp(Rant::Env::RUBY_EXE)} #{args.flatten.join(' ')}` end # Returns a list with the files required by the IO object script. def extract_requires(script, dynamic_requires = []) in_ml_comment = false requires = [] script.each { |line| if in_ml_comment if line =~ /^=end/ in_ml_comment = false end next end # skip shebang line next if line =~ /^#! ?(\/|\\)?\w/ # skip pure comment lines next if line =~ /^\s*#/ if line =~ /^=begin\s/ in_ml_comment = true next end name = nil lib_file = nil if line =~ /\s*(require|load)\s*('|")([^\2]*)\2/ fn = $3 if fn =~ /\#\{[^\}]+\}/ || fn =~ /\#\@/ dynamic_requires << fn else requires << fn end end } requires end module Rant::TestUtil TEST_HARDLINK_BROKEN = Rant::Env.on_windows? && RUBY_VERSION < "1.8.4" def in_local_temp_dir(dirname = "t") dirname = dirname.dup base_dir = Dir.pwd raise "dir `#{t}' already exists" if test ?e, dirname FileUtils.mkdir dirname Dir.chdir dirname yield ensure Dir.chdir base_dir FileUtils.rm_rf dirname end def write_to_file(fn, content) open fn, "w" do |f| f.write content end end # replacement for core test(?-, a, b) which is eventually # corrupted if TEST_HARDLINK_BROKEN def test_hardlink(a, b, opts = {}) # test(?-, a, b) corrupt in ruby < 1.8.4 (final) # on Windows unless defined? @@corrupt_test_hardlink_msg @@corrupt_test_hardlink_msg = true puts "\n*** Ruby core test for hardlinks " + "[test(?-, file1, file2)] considered broken. Using heuristics for unit tests. ***" end # Use some heuristic instead. if test(?l, a) return test(?l, b) && File.readlink(a) == File.readlink(b) else return false if test(?l, b) end content = File.read(a) return false unless File.read(b) == content if opts[:allow_write] if content.size > 1 Rant::TestUtil.write_to_file(a, content[0]) else Rant::TestUtil.write_to_file(a, "hardlink test\n") end File.read(a) == File.read(b) else true end end else def test_hardlink(a, b, opts = {}) test(?-, a, b) end end extend self end rant-0.5.8/test/var.rf0000644000175000017500000000112510527253221014144 0ustar xavierxavier import "var/numbers" var "v1" => "default_1.t" var["v2"] = "default_2.t" var.restrict :count, :Integer, 1..10 var :num, 1.1 .. 11.11 task :default => [var[:v1], var[:v2]] file var[:v1] do |t| sys.touch t.name end file var[:v2] do |t| sys.touch t.name end task :show_count do |t| puts "count #{var :count}" end task :show_num do puts "num #{var :num}" end task :source_err do source "source_err.rf.t" end file "source_err.rf.t" do |t| open(t.name, "w") { |f| f << <<-EOF var[:count] = 11 EOF } end task :clean do sys.rm_f sys["*.t"] end # vim:ft=ruby rant-0.5.8/COPYING0000644000175000017500000006347610527253231013121 0ustar xavierxavier GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! rant-0.5.8/INSTALL0000644000175000017500000000273010527253231013101 0ustar xavierxavier == Installing Rant Of course you need Ruby to run rant. You can get the latest Ruby package from the {Ruby hompage}[http://www.ruby-lang.org/en/]. There are two ways to install Rant on your system: === Installing Rant as a RubyGem RubyGems has to be installed on your system. Check this by trying the _gem_ command: % gem If this prints a help message, RubyGems should work. Otherwise install Rant as described in the next section. Now the following command: % gem install --remote rant will download and install Rant. Depending on the RubyGems configuration, you will probably need superuser privileges. Then try to run: % rant --version which should print name and version of the _rant_ command. If this is done, you have successfully installed Rant. Congratulations! === Conventional installation First download the latest version of Rant from http://rubyforge.org/frs/?group_id=207. Choose the .zip or .tar.gz file, whatever you like, with the highest version number. Then unpack the archive, cd to the new directory and run the install.rb script. This could look like: % tar -xzf rant-.tar.gz % cd rant- % ruby install.rb Depending on your Ruby installation, you'll probably need superuser privileges for the last command. Finally try to run % rant --version to verify Rant was installed correctly. If you aren't already reading this documentation in html format, you can generate it with: % rant doc The output will be in doc/html. rant-0.5.8/NEWS0000644000175000017500000002602610527253452012560 0ustar xavierxavier = Rant NEWS == Rant 0.5.8 Fixes and minor improvements: * Added man pages * Added bash completion script == Rant 0.5.7 Fixes and minor improvements: * Fixed: Rules wouldn't work if the target file was specified by an absolute path. (Reported by Brendan Boesen.) * Fixed: Script files use env ruby (Russel Windir) New features: * The method Rant::Sys.absolute_path?. Read doc/sys.rdoc[link:files/doc/sys_rdoc.html] for documentation. * Added non-deprecated support for C#. Read doc/csharp.rdoc[link:files/doc/csharp_rdoc.html] for documentation. == Rant 0.5.6 Fixes and minor improvements: * Package::Zip, Package::Tgz: Fix bug where a file that starts with the package name wouldn't be included in the package. (Reported and fixed by Kevin Burge.) * Fixed: The C source file scanner used by the C::Dependencies task was confused by C-style single-line comments. * Fix a typo in the C::Dependencies task which affected the :search option. (Reported by Kevin Burge.) * +RubyTest+ no longer uses +testrb+ as test runner per default. Thus it works on systems without +testrb+ now (e.g. Debian based systems). The old behaviour can be enabled by setting the +loader+ attribute to :testrb. A patch was provided by Max Nickel. New features: * The C::Dependencies task accepts the new option :correct_case, which is useful on case-insenstive file systems. (Patch provided by Peter Allin.) Read doc/c.rdoc[link:files/doc/c_rdoc.html] for documentation. * The method Rant::Sys.root_dir?. Read doc/sys.rdoc[link:files/doc/sys_rdoc.html] for documentation. == Rant 0.5.4 Incompatible changes: * The undocumented method var.is which is deprecated since release 0.5.2 is not defined anymore. Fixes and minor improvements: * Fix a rule bug where a custom rule task wouldn't find it's prerequisites in subdirectories. * An non-backwards compatible change in the YAML library of Ruby 1.8.3 and newer causes gems to be non-backwards compatible. The fix for backwards compatibility of gems created with RubyPackage is enabled for all newer Ruby versions now. (See changes of last Rant release.) * The new method sys.write_to_binfile. (Kevin Burge's idea.) Read doc/sys.rdoc[link:files/doc/sys_rdoc.html] for documentation. == Rant 0.5.2 Incompatible changes: * The two undocumented Array methods ary.arglist and ary.shell_pathes, which are deprecated since release 0.4.8, are removed with this release. Use sys.sp(ary) in Rantfiles instead. * The method +rac+ which is deprecated since release 0.4.6 is gone. * Filelists no longer respond to all Array methods. To use the filelist method +no_dir+, import "filelist/std" is required now. See below for documentation links. Fixes and minor improvements: * A bug in the YAML library of Ruby 1.8.3/1.8.4-preview1 prevented created gems to work with other ruby versions. Since this Rant release gems created with a RubyPackage task and Ruby 1.8.3 will work with all Ruby versions (with gem support, of course). * Fixed bug where the method Rant::Sys.split_all would drop a leading dot directory (e.g. as in "./foo/bar"). * New method Rant::Sys.regular_filename for filename conversion. New features: * Major rework of filelist support. The Rant::FileList class is available as "normal" Ruby library now. Read doc/filelist.rdoc[link:files/doc/filelist_rdoc.html] for Rant::FileList documentation and doc/sys_filelist.rdoc[link:files/doc/sys_filelist_rdoc.html] for instructions on how to use filelists in Rantfiles. == Rant 0.5.0 Incompatible changes: * The undocumented filelist methods +no_file+, +no_suffix+ and +no_prefix+ require import "filelist/more" now. Deprecated: * The undocumented var.is method. It won't be in release 0.5.4 and later. * To use a (numeric) range as variable constraint, you should explicitely import "var/numbers" now. Also explicitely import "var/strings" to use the :String constraint and import "var/booleans" to use the :Bool constraint. Read Constraining variables in doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]. Fixes and minor improvements: * Fix a few warnings from ruby 1.8.0/1.8.1. * Prevent infinite rule recursion. * Documentation for common file system operations and path manipulation methods. Read doc/sys.rdoc[link:files/doc/sys_rdoc.html]. New features: * The --dry-run (-n) option for +rant+. Read doc/rant.rdoc[link:files/doc/rant_rdoc.html]. * +Action+ takes a regular expression now. When rant looks for a task/file that matches the regular expression, the action block will get executed once. Read More selective actions in doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]. == Rant 0.4.8 Incompatible changes: * The filenames Rantfile.rb and rantfile.rb which were deprecated since the last release aren't recognized anymore. Use +Rantfile+, +rantfile+ or root.rant instead. Deprecated: * The two undocumented Array methods ary.arglist and ary.shell_pathes are deprecated. Use sys.sp(ary) in Rantfiles instead. * rant-import -v option. Use -V or --version instead. Fixes and minor improvements: * Fix output of rant -T for descriptions with more than two lines. * Filelists: Same handling of files starting with a dot with all supported ruby versions (1.8.0 - 1.9). * The sys.ruby method uses an absolute path to start the Ruby interpreter. Thus sys.ruby also works if ruby is not on the PATH. * Fix for latest ruby 1.9, which renamed +fcall+ to +funcall+. New features: * rant-import supports --zip (-z) option for zip-compression now. * Tasks with command change recognition. Read doc/command.rdoc[link:files/doc/command_rdoc.html] * Improved rule support. == Rant 0.4.6 Incompatible changes: * *Important*: The notation "#foo" to reference a task defined in the project's root directory changed to "@foo". * Previous Rant versions loaded both, +Rantfile+ and Rantfile.rb, if present. This version will only load one of them (preferably +Rantfile+). * +RubyTest+ tasks no longer honour TESTOPTS variable. Deprecated: * The filenames Rantfile.rb and rantfile.rb are deprecated for rantfiles and won't be recognized by future Rant versions. Use +Rantfile+, +rantfile+ or root.rant instead. Fixes and minor improvements: * Fix an issue where a file was unnecessary rebuilt when rant was started from different directories, the file task was created from a rule and md5 signatures were used. * Concise output from Package::Tgz and Package::Zip tasks. * A manpage will be installed on Linux/Unix systems by the install.rb script. * A fix for a FileUtils::Verbose bug in Ruby 1.8.3 which affects some sys methods. New features: * The --cd-parent (-c) option to search for an Rantfile in parent directories. * The --look-up (-u) option to search for the root Rantfile in parent directories. * Recognition of rantfiles with the names root.rant and sub.rant, where a sub.rant file is meant for a project subdirectory and is treated special. Read doc/subdirs.rdoc[link:files/doc/subdirs_rdoc.html] * The +sys+ and sys.ruby methods take an optional block for custom process exit status handling (per default, Rant aborts if a subprocess started with +sys+ exits with another status than 0). == Rant 0.4.4 Besides internal changes, this release is backwards compatible to 0.4.2. Fixes and minor improvements: * Rant is Ruby 1.8.0 compatible now. * Fixes for filelists. * A fix for Directory generator (and thus dependent features). New features: * Optional recognition of file changes based on MD5 checksums. Read doc/md5.rdoc[link:files/doc/md5_rdoc.html] for documentation. == Rant 0.4.2 This is mainly a bugfix release and thus fully backwards compatible to 0.4.0. Fixes and minor improvements: * Fixes for Ruby 1.8.1 compatibility. * Fixes for Rules and multiple buildfiles per project. * rant-import created scripts append inlined files to $LOADED_FEATURES. New features: * The +make+ command. * New method +sub_ext+ for filelists. Read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]. == Rant 0.4.0 Unless you extended Rant with a custom generator, you can upgrade from 0.3.8 without changing any dependent code. Incompatible changes: * _Internal_: A generator has to respond to +rant_gen+ instead of +rant_generate+. New features: * Creating zip and gzipped tar archives on all supported platforms without installing extra software. Seamless integration with rant-import. Read doc/package.rdoc[link:files/doc/package_rdoc.html]. * The standard RubyPackage tasks create zip and gzipped tar archives (and optional gem packages) on all platforms, including Windows, now. * rant-import recognizes the new directives +uncomment+ and +remove+. == Rant 0.3.8 This version should be fully backwards compatible to 0.3.6. New features: * Dependency checking for C/C++ sources. Read doc/c.rdoc[link:files/doc/c_rdoc.html] documentation. * Installing ".cmd" files on Windows with the Win32::RubyCmdWrapper. Read doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html]. * Convenient directory/file creation with SubFile. Read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]. * rant-import inlines specially marked, +require+ files. == Rant 0.3.6 This version should be fully backwards compatible to 0.3.4. New features: * Automatic cleanup of generated files * Directed rules * Constraining variables * rant-import searches $LOAD_PATH * Immediately build targets with rac.build "target" Read doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] and doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] for docu. == Rant 0.3.4 Incompatible changes: * Arguments of the form VAR=VAL to rant no longer set environment variables directly, they are available through +var+ now. Read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] for more info. * Replace any include Sys with include Rant::Sys in Rantfiles. Or even better: don't include the +Sys+ module. New features: * Installation with install.rb installs .cmd files on Windows. Read README[link:files/README.html] * Sharing variables between Rantfiles. Read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] for more info. * Selecting files with the +sys+ command. * Rules Read doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] for docu. == Rant 0.3.2 This version should be fully backwards compatible to 0.3.0. New features: * Support splitting your buildfiles up and placing them into multiple directories with the +subdirs+ command. Please read doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] for usage. This is especially useful for bigger projects. == Rant 0.3.0 First release of Rant on RubyForge. == See also Rant Overview:: README[link:files/README.html] rant-0.5.8/README0000644000175000017500000001666410527253231012743 0ustar xavierxavier = Rant -- Ruby's ant Rant is a flexible build tool written entirely in Ruby. The equivalent to a _Makefile_ for _make_ is the _Rantfile_. An _Rantfile_ is actually a valid Ruby script that is read in by the _rant_ command. Rant currently features: * Rantfiles are written in Ruby. * Defining custom tasks * Automated packaging, testing and RDoc generation for Ruby applications and libraries. * The rant-import command creates a monolithic rant script, so you don't depend on an rant installation anymore. * Creating gzipped tar and zip archives -- without installing additional software. * Optional recognition of file changes based on MD5 checksums instead of file modification times. * Tasks with command change recognition. * Dependency checking for C/C++ source files. * Compiling of C# sources and resources As programmers usually want to see code, here is a short and very basic example of rant usage: A file called +Rantfile+ contains the code: file "backup/data" => "data" do |t| sys.cp t.source, t.name end Running rant in the directory of this file: % rant cp data backup/data will ensure that the "data" file in the "backup" directory is up to date. This document was written for version 0.5.7 of Rant. Most things described here will work for older/newer versions of Rant, but look at the README file in the Rant distribution you've installed for exact documentation of your Rant version. == Support The newest version of this document can be found at http://rant.rubyforge.org. For further information, feature requests, bugreports or comments join the mailing list {rant-cafe}[http://rubyforge.org/mailman/listinfo/rant-cafe] or visit the {RubyForge site for Rant}[http://rubyforge.org/projects/rant/]. == Roadmap Installing Rant:: read INSTALL[link:files/INSTALL.html] License:: read the section _Copying_ in this document Invoking *rant*:: read doc/rant.rdoc[link:files/doc/rant_rdoc.html] Writing an *Rantfile*:: read doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] Automation for your Ruby library/application:: read doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html] Independent from Rant? The rant-import command:: read doc/rant-import.rdoc[link:files/doc/rant-import_rdoc.html] Advanced Rantfiles:: read doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] Packaging (creating zip/tgz archives):: read doc/package.rdoc[link:files/doc/package_rdoc.html] Using MD5 checksums instead of file modification times:: read doc/md5.rdoc[link:files/doc/md5_rdoc.html] Compiling C/C++:: read doc/c.rdoc[link:files/doc/c_rdoc.html] Buildfiles in subdirectories:: read doc/subdirs.rdoc[link:files/doc/subdirs_rdoc.html] Tasks with command change recognition:: read doc/command.rdoc[link:files/doc/command_rdoc.html] Using the Configure plugin:: read doc/configure.rdoc[link:files/doc/configure_rdoc.html] *deprecated* Compiling C#:: read doc/csharp.rdoc[link:files/doc/csharp_rdoc.html] Common file system operations:: read doc/sys.rdoc[link:files/doc/sys_rdoc.html] Upgrading:: read the NEWS[link:files/NEWS.html] for new features, not backwards compatible changes and other issues. Using Rant libraries:: read doc/rubylib.rdoc[link:files/doc/rubylib_rdoc.html] == Copying Copyright (C) 2005 Stefan Lang This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --- The file COPYING[link:../../COPYING] in the Rant package contains a copy of the LGPL. Of course your Rantfiles don't need to be licenced under the terms of the LGPL. --- The file lib/rant/archive/minitar.rb is Copyright 2004 Mauricio Julio Fernandez Pradier and Austin Ziegler. It is licensed under the GNU General Public Licence or Ruby's licence. The file lib/rant/archive/rubyzip.rb and the files in the lib/rant/archive/rubyzip directory were written by Thomas Sondergaard. They are distributed under the same license as ruby. See http://www.ruby-lang.org/en/LICENSE.txt. == Other info Rant was started in February 2005. It was originally written by Stefan Lang (langstefan AT gmx DOT at) and is now being maintained by Xavier Shay (contact AT rhnh DOT net) === Credits Rant has taken the basic syntax of a _task_ and some other concepts from Rake. So thanks to Jim Weirich, the author of Rake[http://rubyforge.org/projects/rake]. Rant comes with parts of archive-tar-minitar to create (gzipped) tar archives on platforms where no +tar+ command is available. archive-tar-minitar is developed by Mauricio Julio Fernandez Pradier and Austin Ziegler. Rant comes with parts of rubyzip to create zip archives on platforms where no +zip+ command is available. rubyzip is developed by Thomas Sondergaard. === Goals of Rant * Most important is to be a very *flexible* build tool. This currently means that you can run/use Rant in many different ways. E.g. you can invoke it the conventional way like _make_, define tasks and run Rant from _irb_ (the interactive Ruby shell), create an independent script with rant-import, or simply use Rant as a library to automate tasks for your application. * *Extensible*: currently through _plugins_ and _generators_. (Think of the +plugin+ and +import+ functions.) * Portability: see _Platforms_ below for details * Well scaling: Rant should work acceptable for bigger projects (from a performance POV and for the writer of the Rantfiles). === Platforms Rant was tested on: System Ruby version ======================================================= Linux 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9 MacOS X 1.8.2 Windows XP 1.8.2 (OneClick Installer) Windows XP 1.8.4 (OneClick Installer) Windows 2000 1.8.2 (OneClick Installer) It *should* run on most platforms where Ruby runs, but you never know... If you encounter problems with Rant on any platform (with Ruby 1.8.0 or higher) please write a bugreport! === Why did you write another build tool? Because I wasn't satisfied by any other build tool. Before I started Rant, I had to write a program in C#. The program had to run under Windows, but I wanted to develop under Linux. Also I decided to write the documentation in Docbook. So there where quite a few problems arising: * I had to compile with cscc on Linux. * compile with csc on Windows. * automate PDF and HTML generation from Docbook _Nant_ would have been ok to compile the C# sources. But it isn't suited for more general tasks as clueing togheter other programs (needed for Docbook processing). Then I tried to use _Rake_ and it was the other way round. I liked the basic concept and syntax of Rake and of course I could have written code for Rake (e.g. with a so called _Tasklib_) to support portable C# compilation. But it was a bit quirky and because I like to work with Ruby, I decided to write my own build tool. rant-0.5.8/Rantfile0000644000175000017500000002031210527253702013536 0ustar xavierxavier # Rantfile for Rant :) import "md5" import %w(rubytest rubydoc rubypackage autoclean win32/rubycmdwrapper sys/more) ENV["RANT_DEV_LIB_DIR"] = sys.expand_path("lib") task :default => :test dist_files = sys.filelist [ "NEWS", "README", "INSTALL", "COPYING", "Rantfile", "install.rb", "setup.rb", "run_rant", "run_import" ] dist_files.glob_unix "{bin,lib,test,doc,misc}/**/*" dist_files.exclude_name "html", "coverage" hp_files = sys["doc/homepage/*"] rdoc_files = sys["README", "NEWS", "INSTALL", "doc/*.rdoc"] # remove when compiler stuff is getting useful... dist_files.exclude "lib/rant/compiler*", "lib/rant/import/c/program.rb" rdoc_opts = %w(-S -c UTF-8 --title Rant --main README) gen RubyPackage, "rant#{var :pkg_ext}" do |t| t.version = `#{sys.sp Env::RUBY_EXE} run_rant --version`.split[1] t.summary = "Rant is a Ruby based build tool." t.files = dist_files t.bindir = "bin" t.executable %w(rant rant-import) t.author = "Stefan Lang (maintained by Xavier Shay)" t.email = "contact@rhnh.net" t.rubyforge_project = "rant" t.homepage = "http://rant.rubyforge.org" t.has_rdoc = false t.gem_extra_rdoc_files = rdoc_files t.gem_rdoc_options = rdoc_opts desc "Create packages for distribution." t.package_task end task "dev-pkg" do make Package::Tgz, "pkg/rant-dev", :files => dist_files end task "dist-files" do puts dist_files end desc "Generate documentation." gen RubyDoc do |g| g.verbose = true g.dir = "doc/html" g.files = rdoc_files g.opts = rdoc_opts + %w(-T doc/jamis.rb) end html_index = "doc/html/index.html" api_index = html_index.sub("index", "api") enhance "doc/html/index.html" do sys.rm_f api_index # remove old RDoc index page make "hp2doc" end task "hp2doc" do if test(?f, html_index) && !test(?f, api_index) sys.mv html_index, api_index end sys.cp hp_files, "doc/html" end desc "Publish html docs on make.rubyfore.org.", "Note: scp will prompt for rubyforge password." task "publish-docs" => :doc do sys "scp -r doc/html/* meink@rubyforge.org:/var/www/gforge-projects/rant/" end task "publish-hp" do sys "scp -r doc/homepage/* meink@rubyforge.org:/var/www/gforge-projects/rant/" end desc "Run basic tests." gen RubyTest do |g| g.libs << "test" g.test_files = sys["test/test_*.rb", "test/units/**/test_*.rb"] end desc "Run just unit tests." gen RubyTest, "test:units".to_sym do |g| g.libs << "test" g.test_files = sys["test/units/**/test_*.rb"] end desc "Run first test-project." gen RubyTest, :testp1 do |g| g.libs << "test" g.test_files = ["test/project1/test_project.rb"] end desc "Run second test project." gen RubyTest, :testp2 do |g| g.libs << "test" g.test_files = ["test/project2/test_project.rb"] end desc "Test small Ruby project." gen RubyTest, :testrb1 do |g| g.libs << "test" g.test_files = %w(test/project_rb1/test_project_rb1.rb) end desc "Test plugins." gen RubyTest, :testplugins do |g| g.libs << "test" g.test_files = sys["test/plugin/**/test_*"] end desc "Run all tests and generate coverage with rcov." task :cov do lp = File.expand_path "lib" sys.cd "test" do sys %W(rcov -xtutil.rb,ts_*,tc_*,test_* -I#{lp} ts_all.rb) end end desc "Test project with subdirs." gen RubyTest, :tsubdirs do |t| t.libs << "test" t.test_files = sys["test/subdirs/test_*.rb"] end desc "Test rant-import command." gen RubyTest, :trimport do |t| t.libs << "test" t.test_files = sys["test/rant-import/test_*.rb"] end desc "Test import/ libraries." gen RubyTest, :timport do |t| t.libs << "test" t.test_files = sys["test/import/**/test_*.rb"] end desc "Test C support." gen RubyTest, :tc do |t| t.libs << "test" t.test_files = sys["test/c/test_*.rb", "test/import/c/**/test_*.rb"] end desc "Run all tests." task :tall do puts "Running build tool tests..." make "trantfile" puts "Build tool tests successful." puts "Running library tests..." make "tlib" puts "All tests (build tool and library) successful." end gen RubyTest, :trantfile do |t| t.libs << "test" t.test_files = sys["test/**/test_*.rb"].exclude("test/lib/*") end gen RubyTest, :tlib do |t| t.libs << "test" t.test_files = sys["test/lib/**/test_*.rb"] end task :t180 do |t| # my installed testrb version doesn't work with ruby-1.8.0 sys.cd "test" if var[:TEST] sys "ruby180", "-w", "-rtest/unit", "-I", sys.expand_path("@lib"), "-I", sys.expand_path("."), var[:TEST].sub(/^test\//, '') else sys "ruby180 ts_all.rb" end end desc "Remove autogenerated files." gen AutoClean, :clean var[:clean].include %w( InstalledFiles .config bench-rant bench-depsearch test/coverage ) # Just for quick visual testing of rant... task :please_fail do |t| sys "mix_nix_gibts" end task "to-win" => :package do win_dir = "/mnt/data_fat/stefan/Ruby" Dir["pkg/*"].each { |f| target = File.join(win_dir, File.basename(f)) make target => f do |t| sys.rm_rf target if test(?e, target) sys.cp_r f, t.name end } end desc "Install Rant." task :install do sys.ruby "setup.rb" # try to install man page man_path = ENV["MANPATH"] if man_path puts "trying to install rant(1) manpage..." require 'rbconfig' prefix = Config::CONFIG["prefix"] dirs = sys.split_path man_path man_dir = dirs.find{|d| d == "#{prefix}/man" } || dirs.first sys.install "doc/rant.1", "#{man_dir}/man1", :mode => 0644 if man_dir end end if Env.on_windows? enhance :install => (gen Win32::RubyCmdWrapper, sys["bin/*"]) end task "svn-clean" do `svn stat`.split(/\n/).each { |line| sys.clean line[7..-1] if line[0] == ?? } end task "check-168" do rbfiles = sys["bin/rant*", "lib/**/*.rb"] ok = [] bad = [] rbfiles.each { |fn| sys "ruby168 -c #{fn}" do |ps| (ps.exitstatus == 0 ? ok : bad) << fn end } puts "Bad files:" bad.each { |b| puts " #{b}" } puts "#{ok.size} of #{rbfiles.size} are OK" end desc "Create local backup of svn repos on berlios." task "fetch-svn-dump" do require 'net/http' require 'uri' url = URI.parse("http://svn.berlios.de/svndumps/rant-repos.gz") req = Net::HTTP::Get.new(url.path) ds = Time.now.strftime("%Y-%m-%d") puts "Starting download from: #{url}" res = Net::HTTP.start(url.host, url.port) { |http| http.request(req) } sys.write_to_file "../rant-repos_#{ds}.gz", res.body end task "stats" do require 'scriptlines' files = sys["lib/**/*.rb"] puts ScriptLines.headline sum = ScriptLines.new("TOTAL (#{files.size} scripts)") files.each { |fn| File.open(fn) do |file| script_lines = ScriptLines.new(fn) script_lines.read(file) sum += script_lines puts script_lines end } puts sum end task "rb-stats" do files = sys["lib/**/*.rb"] lines = 0 code_lines = 0 files.each { |fn| l, c = count_rb_lines(fn) lines += l code_lines += c } puts "Number of Ruby files under lib/: #{files.size}" puts " #{lines} total lines" puts " #{code_lines} LOC" files.exclude("lib/rant/archive*") lines = 0 code_lines = 0 files.each { |fn| l, c = count_rb_lines(fn) lines += l code_lines += c } puts " without lib/rant/archive/: #{files.size}" puts " #{lines} total lines" puts " #{code_lines} LOC" end def count_rb_lines(fn) lines = 0 code_lines = 0 in_multiline_comment = false File.readlines(fn).each { |line| lines += 1 case line when /=end(\s|$)/: in_multiline_comment = false next when /^\s*$/, /^\s*#/: next when /^=begin(\s|$)/ in_multiline_comment = true end code_lines += 1 unless in_multiline_comment } [lines, code_lines] end @prefix = var[:prefix] || "/usr/local" task "uninstall" do print <<-EOF Run task _uninstall_ to uninstall Rant from prefix[#@prefix]. EOF end task "_uninstall_" do sys.rm_rf FileList["#@prefix/lib/ruby/site_ruby/1.8/rant*"] sys.rm_f FileList["#@prefix/bin/rant*"] end # vim:ft=ruby rant-0.5.8/install.rb0000644000175000017500000000022410527253231014037 0ustar xavierxavier # Install Rant with Rant :) $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "lib")) require 'rant/rantlib' exit Rant.run("install") rant-0.5.8/run_import0000755000175000017500000000030210527253231014165 0ustar xavierxavier#!/usr/bin/env ruby # Run rant-import from current development directory. $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "lib")) require 'rant/import' exit Rant::RantImport.run rant-0.5.8/run_rant0000755000175000017500000000025610527253231013627 0ustar xavierxavier#!/usr/bin/env ruby # Run rant in current development directory. $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "lib")) require 'rant/rantlib' exit Rant.run rant-0.5.8/setup.rb0000644000175000017500000007212210527253231013537 0ustar xavierxavier# # setup.rb # # Copyright (c) 2000-2004 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # # Modifications made by Stefan Lang are marked with a line ### stefan ### # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted windows stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end # # Config # if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) require arg.split(/=/, 2)[1] $".push 'rbconfig.rb' else require 'rbconfig' end def multipackage_install? FileTest.directory?(File.dirname($0) + '/packages') end class ConfigItem def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default.dup.freeze @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value @value end def eval(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < ConfigItem def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val setup_rb_error "config: --#{@name} accepts only yes/no for argument" end (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no' end end class PathItem < ConfigItem def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < ConfigItem def config_type 'program' end end class SelectItem < ConfigItem def initialize(name, template, default, desc) super @ok = template.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class PackageSelectionItem < ConfigItem def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class ConfigTable_class def initialize(items) @items = items @table = {} items.each do |i| @table[i.name] = i end ALIASES.each do |ali, name| @table[ali] = @table[name] end end include Enumerable def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or raise ArgumentError, "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def new dup() end def savefile '.config' end def load begin t = dup() File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) t[k] = v.strip end t rescue Errno::ENOENT setup_rb_error $!.message + "#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value end } end def [](key) lookup(key).eval(self) end def []=(key, val) lookup(key).set val end end c = ::Config::CONFIG rubypath = c['bindir'] + '/' + c['ruby_install_name'] major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V < 1.6.3 _stdruby = c['rubylibdir'] _siteruby = c['sitedir'] _siterubyver = c['sitelibdir'] _siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 _stdruby = "$prefix/lib/ruby/#{version}" _siteruby = c['sitedir'] _siterubyver = "$siteruby/#{version}" _siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 _stdruby = "$prefix/lib/ruby/#{version}" _siteruby = "$prefix/lib/ruby/#{version}/site_ruby" _siterubyver = _siteruby _siterubyverarch = "$siterubyver/#{c['arch']}" end libdir = '-* dummy libdir *-' stdruby = '-* dummy rubylibdir *-' siteruby = '-* dummy site_ruby *-' siterubyver = '-* dummy site_ruby version *-' parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\ .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\ .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\ .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\ .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver') } libdir = parameterize.call(c['libdir']) stdruby = parameterize.call(_stdruby) siteruby = parameterize.call(_siteruby) siterubyver = parameterize.call(_siterubyver) siterubyverarch = parameterize.call(_siterubyverarch) if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end common_conf = [ PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', libdir, 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for man pages'), PathItem.new('stdruby', 'path', stdruby, 'the directory for standard ruby libraries'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] class ConfigTable_class # open again ALIASES = { 'std-ruby' => 'stdruby', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } end multipackage_conf = [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] if multipackage_install? ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf) else ConfigTable = ConfigTable_class.new(common_conf) end module MetaConfigAPI def eval_file_ifexist(fname) instance_eval File.read(fname), fname, 1 if File.file?(fname) end def config_names ConfigTable.map {|i| i.name } end def config?(name) ConfigTable.key?(name) end def bool_config?(name) ConfigTable.lookup(name).config_type == 'bool' end def path_config?(name) ConfigTable.lookup(name).config_type == 'path' end def value_config?(name) case ConfigTable.lookup(name).config_type when 'bool', 'path' true else false end end def add_config(item) ConfigTable.add item end def add_bool_config(name, default, desc) ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) ConfigTable.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) ConfigTable.lookup(name).default = default end def remove_config(name) ConfigTable.remove(name) end end # # File Operations # module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # does not check '/'... it's too abnormal case dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(fname) $stderr.puts "rm -f #{fname}" if verbose? return if no_harm? if File.exist?(fname) or File.symlink?(fname) File.chmod 0777, fname File.unlink fname end end def rm_rf(dn) $stderr.puts "rm -rf #{dn}" if verbose? return if no_harm? Dir.chdir dn Dir.foreach('.') do |fn| next if fn == '.' next if fn == '..' if File.dir?(fn) verbose_off { rm_rf fn } else verbose_off { rm_f fn } end end Dir.chdir '..' Dir.rmdir dn end def move_file(src, dest) File.unlink dest if File.exist?(dest) begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(str) $stderr.puts str if verbose? system str or raise RuntimeError, "'system #{str}' failed" end def ruby(str) command config('rubyprog') + ' ' + str end def make(task = '') command config('makeprog') + ' ' + task end def extdir?(dir) File.exist?(dir + '/MANIFEST') end def all_files_in(dirname) Dir.open(dirname) {|d| return d.select {|ent| File.file?("#{dirname}/#{ent}") } } end REJECT_DIRS = %w( CVS SCCS RCS CVS.adm .svn ) def all_dirs_in(dirname) Dir.open(dirname) {|d| return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS } end end # # Main Installer # module HookUtils def run_hook(name) try_run_hook "#{curr_srcdir()}/#{name}" or try_run_hook "#{curr_srcdir()}/#{name}.rb" end def try_run_hook(fname) return false unless File.file?(fname) begin instance_eval File.read(fname), fname, 1 rescue setup_rb_error "hook #{fname} failed:\n" + $!.message end true end end module HookScriptAPI def get_config(key) @config[key] end alias config get_config def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # #abstract srcdir_root #abstract objdir_root #abstract relpath def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file? srcfile(path) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.3.1' Copyright = 'Copyright (c) 2000-2004 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke instance().invoke end @singleton = nil def ToplevelInstaller.instance @singleton ||= new(File.dirname($0)) @singleton end include MetaConfigAPI def initialize(ardir_root) @config = nil @options = { 'verbose' => true } @ardir = File.expand_path(ardir_root) end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' @config = load_config('config') parsearg_config init_installers exec_config exec_setup exec_install else @config = load_config(task) __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs eval_file_ifexist "#{@ardir}/metaconfig" end def load_config(task) case task when 'config' ConfigTable.new when 'clean', 'distclean' if File.exist?(ConfigTable.savefile) then ConfigTable.load else ConfigTable.new end else ConfigTable.load end end def init_installers @installer = Installer.new(@config, @options, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/ while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg return arg when '-q', '--quiet' @options['verbose'] = false when '--verbose' @options['verbose'] = true when '-h', '--help' print_usage $stdout exit 0 when '-v', '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def parsearg_no_options unless ARGV.empty? setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/ @options['config-opt'] = [] while i = ARGV.shift if /\A--?\z/ =~ i @options['config-opt'] = ARGV.dup break end m = re.match(i) or setup_rb_error "config: unknown option #{i}" name, value = *m.to_a[1,2] @config[name] = value end end def parsearg_install @options['no-harm'] = false @options['install-prefix'] = '' while a = ARGV.shift case a when /\A--no-harm\z/ @options['no-harm'] = true when /\A--prefix=(.*)\z/ path = $1 path = File.expand_path(path) unless path[0,1] == '/' @options['install-prefix'] = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, '-h,--help', 'print this message' out.printf fmt, '-v,--version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' ConfigTable.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '$prefix' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_show ConfigTable.each do |i| printf "%-20s %s\n", i.name, i.value end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end class ToplevelInstallerMulti < ToplevelInstaller include HookUtils include HookScriptAPI include FileOperations def initialize(ardir) super @packages = all_dirs_in("#{@ardir}/packages") raise 'no package exists' if @packages.empty? end def run_metaconfigs eval_file_ifexist "#{@ardir}/metaconfig" @packages.each do |name| eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig" end end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, @options, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # multi-package metaconfig API # attr_reader :packages def declare_packages(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_clean rm_f ConfigTable.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f ConfigTable.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose'] Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def verbose? @options['verbose'] end def no_harm? @options['no-harm'] end end class Installer FILETYPES = %w( bin lib ext data ) include HookScriptAPI include HookUtils include FileOperations def initialize(config, opt, srcroot, objroot) @config = config @options = opt @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # configs/options # def no_harm? @options['no-harm'] end def verbose? @options['verbose'] end def verbose_off begin save, @options['verbose'] = @options['verbose'], false yield ensure @options['verbose'] = save end end # # TASK config # def exec_config exec_task_traverse 'config' end def config_dir_bin(rel) end def config_dir_lib(rel) end def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end def extconf opt = @options['config-opt'].join(' ') command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}" end def config_dir_data(rel) end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) ### stefan ### # Do not adjust shebang on Windows as it is useless and setup.rb # is failing for me (Note: already fixed in adjust_shebang). #unless PLATFORM =~ /mswin/i all_files_in(curr_srcdir()).each do |fname| adjust_shebang "#{curr_srcdir()}/#{fname}" end #end end def adjust_shebang(path) return if no_harm? tmpfile = File.basename(path) + '.tmp' begin File.open(path, 'rb') {|r| first = r.gets return unless File.basename(config('rubypath')) =~ /ruby/ ### stefan ### # Add || "" which avoids an ArgumentError if first # line doesn't match the regexp. return unless File.basename((first||"").sub(/\A\#!/, '').split[0] || "") == 'ruby' $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose? File.open(tmpfile, 'wb') {|w| w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath')) w.write r.read } } ### stefan ### # Move +move_file+ out of File.open block to close tmpfile # before unlinking (which didn't work on Windows). move_file tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end def setup_dir_lib(rel) end def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end def setup_dir_data(rel) end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files ruby_extentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @options['install-prefix'] list.each do |fname| install fname, dest, mode, @options['install-prefix'] end end def ruby_scripts collect_filenames_auto().select {|n| /\.rb\z/ =~ n } end # picked up many entries from cvs-1.11.1/src/ignore.c reject_patterns = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) mapping = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } REJECT_PATTERNS = Regexp.new('\A(?:' + reject_patterns.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] } }.join('|') + ')\z') def collect_filenames_auto mapdir((existfiles() - hookfiles()).reject {|fname| REJECT_PATTERNS =~ fname }) end def existfiles all_files_in(curr_srcdir()) | all_files_in('.') end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean ).map {|t| sprintf(fmt, t) } }.flatten end def mapdir(filelist) filelist.map {|fname| if File.exist?(fname) # objdir fname else # srcdir File.join(curr_srcdir(), fname) end } end def ruby_extentions(dir) Dir.open(dir) {|d| ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname } if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end return ents } end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f ConfigTable.savefile rm_f 'InstalledFiles' end def clean_dir_bin(rel) end def clean_dir_lib(rel) end def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end def clean_dir_data(rel) end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f ConfigTable.savefile rm_f 'InstalledFiles' end def distclean_dir_bin(rel) end def distclean_dir_lib(rel) end def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end # # lib # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if config('without-ext') == 'yes' and type == 'ext' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') all_dirs_in(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end end if $0 == __FILE__ begin if multipackage_install? ToplevelInstallerMulti.invoke else ToplevelInstaller.invoke end rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end