ritsu 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/README.md +96 -0
  2. data/Thorfile +95 -0
  3. data/VERSION +1 -0
  4. data/bin/ritsu +32 -0
  5. data/lib/ritsu/block.rb +259 -0
  6. data/lib/ritsu/ext/test_case.rb +20 -0
  7. data/lib/ritsu/external_library.rb +47 -0
  8. data/lib/ritsu/project.rb +89 -0
  9. data/lib/ritsu/project_generator.rb +34 -0
  10. data/lib/ritsu/project_generators/default_generator.rb +73 -0
  11. data/lib/ritsu/project_generators/default_generator_files/Thorfile.erb +9 -0
  12. data/lib/ritsu/project_generators/default_generator_files/meta/project.rb.erb +9 -0
  13. data/lib/ritsu/project_generators.rb +1 -0
  14. data/lib/ritsu/src_file.rb +76 -0
  15. data/lib/ritsu/src_files/cpp_file.rb +33 -0
  16. data/lib/ritsu/src_files/cpp_file_mixin.rb +13 -0
  17. data/lib/ritsu/src_files/executable_cmake_lists.rb +40 -0
  18. data/lib/ritsu/src_files/header_file.rb +46 -0
  19. data/lib/ritsu/src_files/header_file_mixin.rb +20 -0
  20. data/lib/ritsu/src_files/project_cmake_lists.rb +110 -0
  21. data/lib/ritsu/src_files/project_config_header_file.rb +15 -0
  22. data/lib/ritsu/src_files/project_config_header_template_file.rb +46 -0
  23. data/lib/ritsu/src_files/shared_library_cmake_lists.rb +40 -0
  24. data/lib/ritsu/src_files/static_library_cmake_lists.rb +40 -0
  25. data/lib/ritsu/src_files/target_cmake_lists.rb +159 -0
  26. data/lib/ritsu/src_files/templated_src_file.rb +44 -0
  27. data/lib/ritsu/src_files.rb +14 -0
  28. data/lib/ritsu/target.rb +134 -0
  29. data/lib/ritsu/targets/executable.rb +45 -0
  30. data/lib/ritsu/targets/library.rb +29 -0
  31. data/lib/ritsu/targets/shared_library.rb +39 -0
  32. data/lib/ritsu/targets/static_library.rb +33 -0
  33. data/lib/ritsu/targets.rb +4 -0
  34. data/lib/ritsu/template.rb +69 -0
  35. data/lib/ritsu/template_policies.rb +133 -0
  36. data/lib/ritsu/test_helpers.rb +124 -0
  37. data/lib/ritsu/thors/default_thor.rb +57 -0
  38. data/lib/ritsu/thors.rb +1 -0
  39. data/lib/ritsu/utility/accessors.rb +30 -0
  40. data/lib/ritsu/utility/check_upon_add_set.rb +35 -0
  41. data/lib/ritsu/utility/file_robot.rb +129 -0
  42. data/lib/ritsu/utility/files.rb +13 -0
  43. data/lib/ritsu/utility/instance_dependencies.rb +113 -0
  44. data/lib/ritsu/utility/instance_set.rb +29 -0
  45. data/lib/ritsu/utility/platform.rb +21 -0
  46. data/lib/ritsu/utility/simple_io.rb +65 -0
  47. data/lib/ritsu/utility/single_instance.rb +34 -0
  48. data/lib/ritsu/utility/strings.rb +41 -0
  49. data/lib/ritsu/utility.rb +8 -0
  50. data/lib/ritsu.rb +17 -0
  51. data/test/ritsu/block_test.rb +197 -0
  52. data/test/ritsu/external_library_test.rb +42 -0
  53. data/test/ritsu/project_generators/default_generator_test.rb +34 -0
  54. data/test/ritsu/project_test.rb +128 -0
  55. data/test/ritsu/src_file_test.rb +70 -0
  56. data/test/ritsu/src_files/cpp_file_test.rb +43 -0
  57. data/test/ritsu/src_files/executable_cmake_lists_test.rb +52 -0
  58. data/test/ritsu/src_files/header_file_test.rb +58 -0
  59. data/test/ritsu/src_files/project_cmake_lists_test.rb +152 -0
  60. data/test/ritsu/src_files/shared_library_cmake_lists_test.rb +52 -0
  61. data/test/ritsu/src_files/static_library_cmake_lists_test.rb +52 -0
  62. data/test/ritsu/src_files/target_cmake_lists_test.rb +16 -0
  63. data/test/ritsu/target_test.rb +106 -0
  64. data/test/ritsu/targets/executable_test.rb +11 -0
  65. data/test/ritsu/targets/shared_library_test.rb +11 -0
  66. data/test/ritsu/targets/static_library_test.rb +11 -0
  67. data/test/ritsu/template_policies_test.rb +0 -0
  68. data/test/ritsu/utility/accessors_test.rb +15 -0
  69. data/test/ritsu/utility/check_upon_add_set_test.rb +32 -0
  70. data/test/ritsu/utility/file_robot_test.rb +176 -0
  71. data/test/ritsu/utility/strings_test.rb +29 -0
  72. data/test/test_helpers.rb +4 -0
  73. 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
@@ -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