ritsu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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