ritsu 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +96 -0
- data/Thorfile +95 -0
- data/VERSION +1 -0
- data/bin/ritsu +32 -0
- data/lib/ritsu/block.rb +259 -0
- data/lib/ritsu/ext/test_case.rb +20 -0
- data/lib/ritsu/external_library.rb +47 -0
- data/lib/ritsu/project.rb +89 -0
- data/lib/ritsu/project_generator.rb +34 -0
- data/lib/ritsu/project_generators/default_generator.rb +73 -0
- data/lib/ritsu/project_generators/default_generator_files/Thorfile.erb +9 -0
- data/lib/ritsu/project_generators/default_generator_files/meta/project.rb.erb +9 -0
- data/lib/ritsu/project_generators.rb +1 -0
- data/lib/ritsu/src_file.rb +76 -0
- data/lib/ritsu/src_files/cpp_file.rb +33 -0
- data/lib/ritsu/src_files/cpp_file_mixin.rb +13 -0
- data/lib/ritsu/src_files/executable_cmake_lists.rb +40 -0
- data/lib/ritsu/src_files/header_file.rb +46 -0
- data/lib/ritsu/src_files/header_file_mixin.rb +20 -0
- data/lib/ritsu/src_files/project_cmake_lists.rb +110 -0
- data/lib/ritsu/src_files/project_config_header_file.rb +15 -0
- data/lib/ritsu/src_files/project_config_header_template_file.rb +46 -0
- data/lib/ritsu/src_files/shared_library_cmake_lists.rb +40 -0
- data/lib/ritsu/src_files/static_library_cmake_lists.rb +40 -0
- data/lib/ritsu/src_files/target_cmake_lists.rb +159 -0
- data/lib/ritsu/src_files/templated_src_file.rb +44 -0
- data/lib/ritsu/src_files.rb +14 -0
- data/lib/ritsu/target.rb +134 -0
- data/lib/ritsu/targets/executable.rb +45 -0
- data/lib/ritsu/targets/library.rb +29 -0
- data/lib/ritsu/targets/shared_library.rb +39 -0
- data/lib/ritsu/targets/static_library.rb +33 -0
- data/lib/ritsu/targets.rb +4 -0
- data/lib/ritsu/template.rb +69 -0
- data/lib/ritsu/template_policies.rb +133 -0
- data/lib/ritsu/test_helpers.rb +124 -0
- data/lib/ritsu/thors/default_thor.rb +57 -0
- data/lib/ritsu/thors.rb +1 -0
- data/lib/ritsu/utility/accessors.rb +30 -0
- data/lib/ritsu/utility/check_upon_add_set.rb +35 -0
- data/lib/ritsu/utility/file_robot.rb +129 -0
- data/lib/ritsu/utility/files.rb +13 -0
- data/lib/ritsu/utility/instance_dependencies.rb +113 -0
- data/lib/ritsu/utility/instance_set.rb +29 -0
- data/lib/ritsu/utility/platform.rb +21 -0
- data/lib/ritsu/utility/simple_io.rb +65 -0
- data/lib/ritsu/utility/single_instance.rb +34 -0
- data/lib/ritsu/utility/strings.rb +41 -0
- data/lib/ritsu/utility.rb +8 -0
- data/lib/ritsu.rb +17 -0
- data/test/ritsu/block_test.rb +197 -0
- data/test/ritsu/external_library_test.rb +42 -0
- data/test/ritsu/project_generators/default_generator_test.rb +34 -0
- data/test/ritsu/project_test.rb +128 -0
- data/test/ritsu/src_file_test.rb +70 -0
- data/test/ritsu/src_files/cpp_file_test.rb +43 -0
- data/test/ritsu/src_files/executable_cmake_lists_test.rb +52 -0
- data/test/ritsu/src_files/header_file_test.rb +58 -0
- data/test/ritsu/src_files/project_cmake_lists_test.rb +152 -0
- data/test/ritsu/src_files/shared_library_cmake_lists_test.rb +52 -0
- data/test/ritsu/src_files/static_library_cmake_lists_test.rb +52 -0
- data/test/ritsu/src_files/target_cmake_lists_test.rb +16 -0
- data/test/ritsu/target_test.rb +106 -0
- data/test/ritsu/targets/executable_test.rb +11 -0
- data/test/ritsu/targets/shared_library_test.rb +11 -0
- data/test/ritsu/targets/static_library_test.rb +11 -0
- data/test/ritsu/template_policies_test.rb +0 -0
- data/test/ritsu/utility/accessors_test.rb +15 -0
- data/test/ritsu/utility/check_upon_add_set_test.rb +32 -0
- data/test/ritsu/utility/file_robot_test.rb +176 -0
- data/test/ritsu/utility/strings_test.rb +29 -0
- data/test/test_helpers.rb +4 -0
- metadata +209 -0
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Ritsu
|
2
|
+
|
3
|
+
Ritsu helps you with C/C++ software development. It provides you with a
|
4
|
+
domain specific language to specify a C/C++ project, and then automates
|
5
|
+
build project generation with the help of CMake.
|
6
|
+
|
7
|
+
## Using
|
8
|
+
|
9
|
+
### Generate a Project
|
10
|
+
|
11
|
+
Run
|
12
|
+
|
13
|
+
ritsu my_project
|
14
|
+
|
15
|
+
in command prompt to generate a project.
|
16
|
+
This will generate directory with the following contents:
|
17
|
+
|
18
|
+
my_project
|
19
|
+
build
|
20
|
+
meta
|
21
|
+
project.rb
|
22
|
+
src
|
23
|
+
cmake_modules
|
24
|
+
--empty--
|
25
|
+
Thorfile
|
26
|
+
|
27
|
+
### Specifying the Project
|
28
|
+
|
29
|
+
The file <tt>meta/project.rb</tt> specifies the project, and its
|
30
|
+
content is initially as follows.
|
31
|
+
|
32
|
+
require 'ritsu'
|
33
|
+
|
34
|
+
Ritsu::Project.create('my_project') do |p|
|
35
|
+
|
36
|
+
##################
|
37
|
+
# YOUR CODE HERE #
|
38
|
+
##################
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
You now can populate the <tt>src</tt> directory with your C/C++ source
|
43
|
+
and header files. For examples:
|
44
|
+
|
45
|
+
my_project
|
46
|
+
build
|
47
|
+
meta
|
48
|
+
project.rb
|
49
|
+
src
|
50
|
+
cmake_modules
|
51
|
+
my_program
|
52
|
+
lib.cpp
|
53
|
+
lib.h
|
54
|
+
main.cpp
|
55
|
+
Thorfile
|
56
|
+
|
57
|
+
|
58
|
+
Then, you can specify targets to build as follows:
|
59
|
+
|
60
|
+
require 'ritsu'
|
61
|
+
|
62
|
+
Ritsu::Project.create('my_project') do |p|
|
63
|
+
p.add_executable('my_program') do |e|
|
64
|
+
e.add_cpp_file 'main.cpp'
|
65
|
+
e.add_header_file 'lib.h'
|
66
|
+
e.add_cpp_file 'lib.cpp'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
As you can see, for each target (an executable or a library),
|
71
|
+
you have to create a directory under <tt>src</tt>, which bears the
|
72
|
+
same name as the project. The file names used in the <tt>add_cpp_file</tt>
|
73
|
+
and <tt>add_header_file</tt> commands are relative to the directory of the target.
|
74
|
+
You can bypass this behavior though.
|
75
|
+
|
76
|
+
### Building
|
77
|
+
|
78
|
+
To build the project, you first need to update the source files and
|
79
|
+
the <tt>CMakeLists.txt</tt> files for the project and for its targets.
|
80
|
+
This is done by running a Thor command in the project directory.
|
81
|
+
|
82
|
+
thor my_project:update_src
|
83
|
+
|
84
|
+
The source files and <tt>CMakeList</tt> files are generated if they are
|
85
|
+
not present. From this point, you can either run cmake to
|
86
|
+
generate platform-specific build scripts, or run
|
87
|
+
|
88
|
+
thor my_project:cmake
|
89
|
+
|
90
|
+
which will create an out-of-source build scripts in the <tt>build</tt> directory.
|
91
|
+
The action
|
92
|
+
|
93
|
+
thor my_project:update
|
94
|
+
|
95
|
+
combines the first two steps in one shot. Now, use your platform-specific
|
96
|
+
tool to build the project.
|
data/Thorfile
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# enconding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'thor/rake_compat'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rake'
|
7
|
+
begin
|
8
|
+
require 'yard'
|
9
|
+
rescue LoadError
|
10
|
+
end
|
11
|
+
|
12
|
+
GEM_NAME = 'ritsu'
|
13
|
+
EXTRA_RDOC_FILES = FileList.new('*') do |list|
|
14
|
+
list.exclude(/(^|[^.a-z])[a-z]+/)
|
15
|
+
list.exclude('TODO')
|
16
|
+
end.to_a + ['Thorfile']
|
17
|
+
|
18
|
+
class Default < Thor
|
19
|
+
include Thor::RakeCompat
|
20
|
+
|
21
|
+
Rake::TestTask.new do |t|
|
22
|
+
t.libs << 'lib'
|
23
|
+
test_files = FileList['test/**/*_test.rb']
|
24
|
+
t.test_files = test_files
|
25
|
+
t.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
if defined?(RDoc)
|
29
|
+
RDoc::Task.new do |rdoc|
|
30
|
+
rdoc.main = "README.rdoc"
|
31
|
+
rdoc.rdoc_dir = "rdoc"
|
32
|
+
rdoc.title = GEM_NAME
|
33
|
+
rdoc.rdoc_files.include(*EXTRA_RDOC_FILES)
|
34
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
35
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
YARD::Rake::YardocTask.new do |t|
|
40
|
+
t.files = FileList.new('lib/**/*.rb').to_a + EXTRA_RDOC_FILES
|
41
|
+
t.options << '--incremental' if Rake.application.top_level_tasks.include?('redoc')
|
42
|
+
#t.options += FileList.new(scope('yard/*.rb')).to_a.map {|f| ['-e', f]}.flatten
|
43
|
+
files = FileList.new('doc-src/*').to_a.sort_by {|s| s.size} + %w[VERSION]
|
44
|
+
t.options << '--files' << files.join(',')
|
45
|
+
#t.options << '--template-path' << scope('yard')
|
46
|
+
t.options << '--title' << ENV["YARD_TITLE"] if ENV["YARD_TITLE"]
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "doc", "Generate YARD Documentation"
|
50
|
+
def doc
|
51
|
+
yard
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
require 'jeweler'
|
56
|
+
Jeweler::Tasks.new do |s|
|
57
|
+
s.name = GEM_NAME
|
58
|
+
s.version = File.read(File.dirname(__FILE__) + '/VERSION').strip
|
59
|
+
s.rubyforge_project = "ritsu"
|
60
|
+
s.platform = Gem::Platform::RUBY
|
61
|
+
s.summary = "A code generation system that facilitates building C/C++ software with the help of CMake and Doxygen"
|
62
|
+
s.email = "dragonmeteor@gmail.com"
|
63
|
+
s.homepage = "http://github.com/dragonmeteor/ritsu"
|
64
|
+
s.description = "A code generation system that facilitates building C/C++ software with the help of CMake and Doxygen"
|
65
|
+
s.authors = ['dragonmeteor']
|
66
|
+
|
67
|
+
s.has_rdoc = true
|
68
|
+
s.extra_rdoc_files = EXTRA_RDOC_FILES
|
69
|
+
s.rdoc_options += [
|
70
|
+
'--title', 'Ritsu',
|
71
|
+
'--main', 'README.md',
|
72
|
+
'--line-numbers',
|
73
|
+
'--inline-source'
|
74
|
+
]
|
75
|
+
|
76
|
+
s.require_path = 'lib'
|
77
|
+
s.bindir = "bin"
|
78
|
+
s.executables = %w( ritsu )
|
79
|
+
s.files = s.extra_rdoc_files + Dir.glob("{bin,lib}/**/*")
|
80
|
+
s.test_files.include 'test/**/*'
|
81
|
+
s.test_files.exclude 'test/**/output/**'
|
82
|
+
|
83
|
+
s.add_runtime_dependency 'thor', '>= 0.13.4'
|
84
|
+
s.add_runtime_dependency 'activesupport', '>= 2.3.5'
|
85
|
+
|
86
|
+
s.add_development_dependency 'jeweler', '>= 1.4.0'
|
87
|
+
s.add_development_dependency 'yard', '>= 0.5.3'
|
88
|
+
s.add_development_dependency 'maruku', '>= 0.5.9'
|
89
|
+
end
|
90
|
+
|
91
|
+
Jeweler::GemcutterTasks.new
|
92
|
+
rescue LoadError
|
93
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
|
94
|
+
end
|
95
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/ritsu
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# The command line Ritsu generator.
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
5
|
+
require 'ritsu'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
options = {:generator => "default"}
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: ritsu PROJECT_NAME [OPTIONS]"
|
11
|
+
opts.on("-g", "--generator GENERATOR", "name of the generator to use") do |generator|
|
12
|
+
options[:generator] = generator
|
13
|
+
end
|
14
|
+
|
15
|
+
opts.parse!
|
16
|
+
if ARGV.length < 1
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
generator_class = Ritsu::ProjectGenerator.generator_classes[options[:generator]]
|
23
|
+
|
24
|
+
if generator_class.nil?
|
25
|
+
puts "there is no generator with name '#{options[:generator]}'"
|
26
|
+
else
|
27
|
+
begin
|
28
|
+
generator_class.new.generate(ARGV[0], '.', options)
|
29
|
+
rescue Exception => e
|
30
|
+
puts e
|
31
|
+
end
|
32
|
+
end
|
data/lib/ritsu/block.rb
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support/core_ext/string/starts_ends_with'
|
3
|
+
require File.dirname(__FILE__) + '/utility/check_upon_add_set'
|
4
|
+
require File.dirname(__FILE__) + '/utility/files'
|
5
|
+
require File.dirname(__FILE__) + '/utility/strings'
|
6
|
+
|
7
|
+
module Ritsu
|
8
|
+
module BlockMixin
|
9
|
+
attr_reader :id
|
10
|
+
attr_accessor :contents
|
11
|
+
attr_accessor :local_indentation
|
12
|
+
attr_accessor :indent_level
|
13
|
+
attr_accessor :indent_length
|
14
|
+
|
15
|
+
def initialize_block_mixin(id = nil, options={})
|
16
|
+
options = {
|
17
|
+
:contents => [],
|
18
|
+
:local_indentation => "",
|
19
|
+
:indent_length=>4
|
20
|
+
}.merge(options)
|
21
|
+
|
22
|
+
@id = id
|
23
|
+
@contents = options[:contents]
|
24
|
+
@local_indentation = options[:local_indentation]
|
25
|
+
@indent_length = options[:indent_length]
|
26
|
+
@indent_level = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_line(line)
|
30
|
+
contents << " " * (indent_level * indent_length) + line
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_new_line
|
34
|
+
add_line("")
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def add_block_structure(block)
|
39
|
+
block.local_indentation = block.local_indentation + " " * (indent_level * indent_length)
|
40
|
+
contents << block
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_line_or_other_content(content)
|
44
|
+
if content.kind_of?(String)
|
45
|
+
add_line(content)
|
46
|
+
else
|
47
|
+
contents << content
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
public
|
52
|
+
def clear_contents
|
53
|
+
contents.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
def indent
|
57
|
+
@indent_level += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def outdent
|
61
|
+
@indent_level -= 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def block_structure?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Block
|
70
|
+
include BlockMixin
|
71
|
+
|
72
|
+
attr_accessor :block_start_prefix
|
73
|
+
attr_accessor :block_end_prefix
|
74
|
+
|
75
|
+
def initialize(id = nil, options={})
|
76
|
+
options = {
|
77
|
+
:block_start_prefix => "//<<",
|
78
|
+
:block_end_prefix => "//>>"
|
79
|
+
}.merge(options)
|
80
|
+
|
81
|
+
@block_start_prefix = options[:block_start_prefix]
|
82
|
+
@block_end_prefix = options[:block_end_prefix]
|
83
|
+
|
84
|
+
initialize_block_mixin(id, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.extract_block_id(str, prefix)
|
88
|
+
str.strip.slice((prefix.length)..-1).strip
|
89
|
+
end
|
90
|
+
|
91
|
+
def extract_block_id(str, prefix)
|
92
|
+
Block.extract_block_id(str, prefix)
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_lines(lines, options={})
|
96
|
+
options = {
|
97
|
+
:block_start_prefix => block_start_prefix,
|
98
|
+
:block_end_prefix => block_end_prefix
|
99
|
+
}.merge(options)
|
100
|
+
@block_start_prefix = options[:block_start_prefix]
|
101
|
+
@block_end_prefix = options[:block_end_prefix]
|
102
|
+
|
103
|
+
block_stack = [self]
|
104
|
+
global_indentation_length = 0
|
105
|
+
|
106
|
+
get_local_indentation = Proc.new do |line|
|
107
|
+
leading_spaces_length = Ritsu::Utility::Strings.leading_spaces(line, options).length
|
108
|
+
remaining_space_length = leading_spaces_length - global_indentation_length
|
109
|
+
if remaining_space_length < 0 then remaining_space_length = 0 end
|
110
|
+
" " * remaining_space_length
|
111
|
+
end
|
112
|
+
|
113
|
+
append_line = Proc.new do |line|
|
114
|
+
block_stack.last.contents << (get_local_indentation.call(line) + line.lstrip)
|
115
|
+
end
|
116
|
+
|
117
|
+
lines.each do |line|
|
118
|
+
if line.strip.starts_with?(block_start_prefix)
|
119
|
+
id = extract_block_id(line, block_start_prefix)
|
120
|
+
local_indentation = get_local_indentation.call(line)
|
121
|
+
|
122
|
+
options[:local_indentation] = local_indentation
|
123
|
+
block = Block.new(id, options)
|
124
|
+
block_stack.last.contents << block
|
125
|
+
block_stack.push(block)
|
126
|
+
global_indentation_length += block.local_indentation.length
|
127
|
+
elsif line.strip.starts_with?(block_end_prefix)
|
128
|
+
id = extract_block_id(line, block_end_prefix)
|
129
|
+
if block_stack.last.id == id
|
130
|
+
block = block_stack.pop()
|
131
|
+
global_indentation_length -= block.local_indentation.length
|
132
|
+
else
|
133
|
+
append_line.call(line)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
append_line.call(line)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
if block_stack.length != 1
|
141
|
+
raise ArgumentError.new("error in input. some blocks are malformed")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_string(string, options={})
|
146
|
+
lines = string.rstrip.split("\n")
|
147
|
+
parse_lines(lines, options)
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_file(filename, options={})
|
151
|
+
text = Ritsu::Utility::Files.read(filename)
|
152
|
+
parse_string(text, options)
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# In all case, the generated string shall have no trailing whitespaces.
|
157
|
+
def to_s(options={})
|
158
|
+
options = {:no_delimiter => false, :indentation => ""}.merge(options)
|
159
|
+
no_delimiter = options.delete(:no_delimiter)
|
160
|
+
indentation = options.delete(:indentation)
|
161
|
+
|
162
|
+
io = StringIO.new
|
163
|
+
io << indentation + local_indentation + block_start_prefix + " " + id + "\n" unless no_delimiter
|
164
|
+
contents.each do |content|
|
165
|
+
if content.kind_of?(Block)
|
166
|
+
io << content.to_s({:indentation=>indentation+local_indentation}.merge(options)) + "\n"
|
167
|
+
else
|
168
|
+
io << indentation + local_indentation + content.to_s + "\n"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
io << indentation + local_indentation + block_end_prefix + " " + id unless no_delimiter
|
172
|
+
|
173
|
+
io.string.rstrip
|
174
|
+
end
|
175
|
+
|
176
|
+
def write_to_file(filename, options={})
|
177
|
+
options = {:no_delimiter => true}.merge(options)
|
178
|
+
|
179
|
+
File.open(filename, "w") do |f|
|
180
|
+
f.write(to_s(options))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.parse(*args)
|
185
|
+
if args.length > 2
|
186
|
+
raise ArgumentError.new("only at most 2 arguments, the second one is a hash, are accepted")
|
187
|
+
end
|
188
|
+
options = {}
|
189
|
+
|
190
|
+
prepare_options_from_kth_arg = Proc.new do |k|
|
191
|
+
options = options.merge(args[k]) if (args.length > k)
|
192
|
+
end
|
193
|
+
|
194
|
+
block = Block.new
|
195
|
+
result = case args[0]
|
196
|
+
when Array
|
197
|
+
prepare_options_from_kth_arg.call(1)
|
198
|
+
block.parse_lines(args[0], options)
|
199
|
+
when String
|
200
|
+
prepare_options_from_kth_arg.call(1)
|
201
|
+
block.parse_string(args[0], options)
|
202
|
+
when Hash
|
203
|
+
if filename = args[0].delete(:file)
|
204
|
+
prepare_options_from_kth_arg.call(0)
|
205
|
+
block.parse_file(filename, options)
|
206
|
+
else
|
207
|
+
raise ArgumentError.new("you must specify :file option if the first argument is a hash")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
return block
|
211
|
+
end
|
212
|
+
|
213
|
+
def child_block_count
|
214
|
+
count = 0
|
215
|
+
contents.each do |content|
|
216
|
+
count += 1 if content.kind_of?(Block)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# @return (Block) the first child block with the given ID. nil if there is no such child block.
|
222
|
+
def child_block_with_id(id)
|
223
|
+
contents.each do |content|
|
224
|
+
if content.kind_of?(Block) and content.id == id
|
225
|
+
return content
|
226
|
+
end
|
227
|
+
end
|
228
|
+
return nil
|
229
|
+
end
|
230
|
+
|
231
|
+
def child_blocks
|
232
|
+
contents.select {|x| x.kind_of?(Block)}
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# @return (Integer) the position of the child block with the given ID in the contents array.
|
237
|
+
# nil if there is no such child block.
|
238
|
+
def child_block_with_id_position(id)
|
239
|
+
contents.length.times do |i|
|
240
|
+
if contents[i].kind_of?(Block) and contents[i].id == id
|
241
|
+
return i
|
242
|
+
end
|
243
|
+
end
|
244
|
+
return nil
|
245
|
+
end
|
246
|
+
|
247
|
+
def add_block(block)
|
248
|
+
add_block_structure(block)
|
249
|
+
end
|
250
|
+
|
251
|
+
def add_content(content)
|
252
|
+
if content.kind_of?(Block)
|
253
|
+
add_block(content)
|
254
|
+
else
|
255
|
+
add_line_or_other_content(content)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
module Test
|
4
|
+
module Unit
|
5
|
+
class TestCase
|
6
|
+
def self.must(name, &block)
|
7
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
8
|
+
defined = instance_method(test_name) rescue false
|
9
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
10
|
+
if block_given?
|
11
|
+
define_method(test_name, &block)
|
12
|
+
else
|
13
|
+
define_method(test_name) do
|
14
|
+
flunk "No implementation provided for #{name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require File.dirname(__FILE__) + '/utility/accessors'
|
4
|
+
require File.dirname(__FILE__) + '/utility/instance_set'
|
5
|
+
|
6
|
+
module Ritsu
|
7
|
+
class ExternalLibrary
|
8
|
+
include Ritsu::Utility::Accessors
|
9
|
+
include Ritsu::Utility::InstanceSet
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
attr_accessor :cmake_name
|
13
|
+
attr_accessor :cmake_find_script
|
14
|
+
attr_accessor :cmake_depend_script
|
15
|
+
|
16
|
+
attr_method :cmake_name
|
17
|
+
attr_method :cmake_find_script
|
18
|
+
attr_method :cmake_depend_script
|
19
|
+
|
20
|
+
def initialize(name, options={})
|
21
|
+
options = {
|
22
|
+
:cmake_name => '',
|
23
|
+
:cmake_find_script => '',
|
24
|
+
:cmake_depend_script => ''}.merge(options)
|
25
|
+
@name = name
|
26
|
+
@cmake_name = options[:cmake_name]
|
27
|
+
@cmake_find_script = options[:cmake_find_script]
|
28
|
+
@cmake_depend_script = options[:cmake_depend_script]
|
29
|
+
ExternalLibrary.instances << self
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.validate_instance(instance)
|
33
|
+
if instances.select { |x| x.name == instance.name }.length > 0
|
34
|
+
raise ArgumentError.new "external library with name '#{instance.name}' already exists"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.find_by_name(name)
|
39
|
+
instances.each do |instance|
|
40
|
+
if instance.name == name
|
41
|
+
return instance
|
42
|
+
end
|
43
|
+
end
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require File.dirname(__FILE__) + '/external_library'
|
4
|
+
require File.dirname(__FILE__) + '/utility/single_instance'
|
5
|
+
require File.dirname(__FILE__) + '/utility/strings'
|
6
|
+
require File.dirname(__FILE__) + '/src_files/project_cmake_lists'
|
7
|
+
require File.dirname(__FILE__) + '/src_files/project_config_header_file'
|
8
|
+
require File.dirname(__FILE__) + '/src_files/project_config_header_template_file'
|
9
|
+
|
10
|
+
module Ritsu
|
11
|
+
class Project
|
12
|
+
include Ritsu::Utility::SingleInstance
|
13
|
+
include Ritsu::Utility::Strings
|
14
|
+
|
15
|
+
attr_reader :name
|
16
|
+
attr_reader :targets
|
17
|
+
attr_reader :external_libraries
|
18
|
+
attr_reader :src_files
|
19
|
+
attr_accessor :project_dir
|
20
|
+
attr_reader :cmake_lists
|
21
|
+
attr_reader :config_header_file
|
22
|
+
attr_reader :config_header_template_file
|
23
|
+
|
24
|
+
def initialize_instance(name)
|
25
|
+
if !is_c_name?(name)
|
26
|
+
raise ArgumentError.new(
|
27
|
+
"the project name must be a valid C name")
|
28
|
+
end
|
29
|
+
|
30
|
+
@name = name
|
31
|
+
@targets = Set.new
|
32
|
+
@external_libraries = Set.new
|
33
|
+
@src_files = Set.new
|
34
|
+
@project_dir = File.expand_path('.')
|
35
|
+
|
36
|
+
@cmake_lists = Ritsu::SrcFiles::ProjectCmakeLists.new(self)
|
37
|
+
@config_header_file = Ritsu::SrcFiles::ProjectConfigHeaderFile.new(self)
|
38
|
+
@config_header_template_file = Ritsu::SrcFiles::ProjectConfigHeaderTemplateFile.new(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.create(name)
|
42
|
+
project = Project.new(name)
|
43
|
+
yield project if block_given?
|
44
|
+
project
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_external_library(name)
|
48
|
+
library = ExternalLibrary.new(name)
|
49
|
+
yield library if block_given?
|
50
|
+
@external_libraries << library
|
51
|
+
end
|
52
|
+
|
53
|
+
def project
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def src_dir
|
58
|
+
project_dir + "/src"
|
59
|
+
end
|
60
|
+
|
61
|
+
def compute_src_path(path, options={})
|
62
|
+
options = {:relative_to => :src}.merge(options)
|
63
|
+
case options[:reltavie_to]
|
64
|
+
when :src
|
65
|
+
path
|
66
|
+
when :absolute
|
67
|
+
src_dir = Pathname.new(self.src_dir)
|
68
|
+
input_path = Pathname.new(path)
|
69
|
+
input_path.relative_path_from(src_dir).to_s
|
70
|
+
else
|
71
|
+
raise ArgumentError.new("option :relative_to must be either :src or :absolute")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def include_file(filename, options={})
|
76
|
+
file_content = Ritsu::Utility::Files.read(filename)
|
77
|
+
instance_eval(file_content)
|
78
|
+
end
|
79
|
+
|
80
|
+
def update
|
81
|
+
src_files.each do |src_file|
|
82
|
+
src_file.update
|
83
|
+
end
|
84
|
+
targets.each do |target|
|
85
|
+
target.update
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/utility/file_robot'
|
2
|
+
require File.dirname(__FILE__) + '/utility/instance_set'
|
3
|
+
require File.dirname(__FILE__) + '/utility/strings'
|
4
|
+
|
5
|
+
module Ritsu
|
6
|
+
class ProjectGenerator
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
def self.generator_classes
|
10
|
+
@generator_classes ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.is_valid_generator_name?(name)
|
14
|
+
Ritsu::Utility::Strings::is_c_name?(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(name)
|
18
|
+
if !ProjectGenerator.is_valid_generator_name?(name)
|
19
|
+
raise ArgumentError.new("'#{name}' is not a valid project name (i.e., a C name)")
|
20
|
+
end
|
21
|
+
@name = name
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.validate_instance(instance)
|
25
|
+
if instances.select { |x| x.name == instance.name }.length > 0
|
26
|
+
raise ArgumentError.new("project generator with name '#{instance.name}' already exists")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate(*args)
|
31
|
+
raise NotImplementedError.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|