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.
- 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
         |